From 69b957b62cbc2a0f5a6ddf187797ea8ddb178284 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 30 Jan 2025 16:57:46 +0100 Subject: [PATCH] refactor(app): wait for app-ready before checking for updates --- src/stremio_app/app.rs | 254 +++++++++++++++++++++++------------------ 1 file changed, 141 insertions(+), 113 deletions(-) diff --git a/src/stremio_app/app.rs b/src/stremio_app/app.rs index 414830d..6ae5e60 100644 --- a/src/stremio_app/app.rs +++ b/src/stremio_app/app.rs @@ -10,7 +10,7 @@ use std::{ process::{self, Command}, str, sync::{Arc, Mutex}, - thread, time, + thread, time::{self, Duration}, }; use url::Url; use winapi::um::{winbase::CREATE_BREAKAWAY_FROM_JOB, winuser::WS_EX_TOPMOST}; @@ -133,6 +133,10 @@ impl MainWindow { let web_tx_arg = web_tx.clone(); let web_tx_upd = web_tx.clone(); let web_rx = web_rx.clone(); + + let (updater_tx, updater_rx) = flume::unbounded::(); + let updater_tx_web = updater_tx.clone(); + let command_clone = self.command.clone(); // Single application IPC @@ -147,34 +151,44 @@ impl MainWindow { let release_candidate = self.release_candidate; let autoupdater_setup_file = self.autoupdater_setup_file.clone(); thread::spawn(move || loop { - let current_version = env!("CARGO_PKG_VERSION") - .parse() - .expect("Should always be valid"); - let updater_endpoint = if let Some(ref endpoint) = autoupdater_endpoint { - endpoint.clone() - } else { - let mut rng = rand::thread_rng(); - let index = rng.gen_range(0..UPDATE_ENDPOINT.len()); - let mut url = Url::parse(UPDATE_ENDPOINT[index]).unwrap(); - if release_candidate { - url.query_pairs_mut().append_pair("rc", "true"); + let check_for_update = || { + let current_version = env!("CARGO_PKG_VERSION") + .parse() + .expect("Should always be valid"); + + let updater_endpoint = if let Some(ref endpoint) = autoupdater_endpoint { + endpoint.clone() + } else { + let mut rng = rand::thread_rng(); + let index = rng.gen_range(0..UPDATE_ENDPOINT.len()); + let mut url = Url::parse(UPDATE_ENDPOINT[index]).unwrap(); + if release_candidate { + url.query_pairs_mut().append_pair("rc", "true"); + } + url + }; + + let updater = updater::Updater::new(current_version, &updater_endpoint, force_update); + match updater.autoupdate() { + Ok(Some(update)) => { + println!("New version ready to install v{}", update.version); + let mut autoupdater_setup_file = autoupdater_setup_file.lock().unwrap(); + *autoupdater_setup_file = Some(update.file.clone()); + web_tx_upd.send(RPCResponse::update_available()).ok(); + } + Ok(None) => println!("No new updates found"), + Err(e) => eprintln!("Failed to fetch updates: {e}"), } - url }; - let updater = updater::Updater::new(current_version, &updater_endpoint, force_update); - match updater.autoupdate() { - Ok(Some(update)) => { - println!("New version ready to install v{}", update.version); - let mut autoupdater_setup_file = autoupdater_setup_file.lock().unwrap(); - *autoupdater_setup_file = Some(update.file.clone()); - web_tx_upd.send(RPCResponse::update_available()).ok(); - } - Ok(None) => println!("No new updates found"), - Err(e) => eprintln!("Failed to fetch updates: {e}"), - } + updater_rx + .try_iter() + .for_each(|message| match message.as_str() { + "check_for_update" => check_for_update(), + _ => {} + }); - thread::sleep(time::Duration::from_secs(UPDATE_INTERVAL)); + thread::sleep(Duration::from_millis(10)); }); // thread if let Ok(mut listener) = PipeServer::bind(socket_path) { @@ -201,109 +215,123 @@ impl MainWindow { .for_each(drop); }); // thread + let mut last_update_check = time::Instant::now(); let toggle_fullscreen_sender = self.toggle_fullscreen_notice.sender(); let quit_sender = self.quit_notice.sender(); let hide_splash_sender = self.hide_splash_notice.sender(); let focus_sender = self.focus_notice.sender(); let autoupdater_setup_mutex = self.autoupdater_setup_file.clone(); thread::spawn(move || loop { - if let Some(msg) = web_rx - .recv() - .ok() - .and_then(|s| serde_json::from_str::(&s).ok()) - { - match msg.get_method() { - // The handshake. Here we send some useful data to the WEB UI - None if msg.is_handshake() => { - web_tx_web.send(RPCResponse::get_handshake()).ok(); - } - Some("win-set-visibility") => toggle_fullscreen_sender.notice(), - Some("quit") => quit_sender.notice(), - Some("app-ready") => { - hide_splash_sender.notice(); - web_tx_web - .send(RPCResponse::visibility_change(true, 1, false)) - .ok(); - let command_ref = command_clone.clone(); - if !command_ref.is_empty() { - web_tx_web.send(RPCResponse::open_media(command_ref)).ok(); + let now = time::Instant::now(); + if now > (last_update_check + time::Duration::from_secs(UPDATE_INTERVAL)) { + last_update_check = now; + updater_tx_web + .send("check_for_update".to_owned()) + .expect("Failed to send value to updater channel"); + } + + web_rx + .try_iter() + .map(|message| serde_json::from_str::(&message)) + .for_each(|result| if let Ok(request) = result { + match request.get_method() { + // The handshake. Here we send some useful data to the WEB UI + None if request.is_handshake() => { + web_tx_web.send(RPCResponse::get_handshake()).ok(); } - } - Some("app-error") => { - hide_splash_sender.notice(); - if let Some(arg) = msg.get_params() { - // TODO: Make this modal dialog - eprintln!("Web App Error: {}", arg); + Some("win-set-visibility") => toggle_fullscreen_sender.notice(), + Some("quit") => quit_sender.notice(), + Some("app-ready") => { + hide_splash_sender.notice(); + web_tx_web + .send(RPCResponse::visibility_change(true, 1, false)) + .ok(); + updater_tx_web + .send("check_for_update".to_owned()) + .expect("Failed to send value to updater channel"); + + let command_ref = command_clone.clone(); + if !command_ref.is_empty() { + web_tx_web.send(RPCResponse::open_media(command_ref)).ok(); + } } - } - Some("open-external") => { - if let Some(arg) = msg.get_params() { - // FIXME: THIS IS NOT SAFE BY ANY MEANS - // open::that("calc").ok(); does exactly that - let arg = arg.as_str().unwrap_or(""); - let arg_lc = arg.to_lowercase(); - if arg_lc.starts_with("http://") - || arg_lc.starts_with("https://") - || arg_lc.starts_with("rtp://") - || arg_lc.starts_with("rtps://") - || arg_lc.starts_with("ftp://") - || arg_lc.starts_with("ipfs://") - { - open::that(arg).ok(); + Some("app-error") => { + hide_splash_sender.notice(); + if let Some(arg) = request.get_params() { + // TODO: Make this modal dialog + eprintln!("Web App Error: {}", arg); } } - } - Some("win-focus") => { - focus_sender.notice(); - } - Some("autoupdater-notif-clicked") => { - // We've shown the "Update Available" notification - // and the user clicked on "Restart And Update" - let autoupdater_setup_file = - autoupdater_setup_mutex.lock().unwrap().clone(); - match autoupdater_setup_file { - Some(file_path) => { - println!("Running the setup at {:?}", file_path); + Some("open-external") => { + if let Some(arg) = request.get_params() { + // FIXME: THIS IS NOT SAFE BY ANY MEANS + // open::that("calc").ok(); does exactly that + let arg = arg.as_str().unwrap_or(""); + let arg_lc = arg.to_lowercase(); + if arg_lc.starts_with("http://") + || arg_lc.starts_with("https://") + || arg_lc.starts_with("rtp://") + || arg_lc.starts_with("rtps://") + || arg_lc.starts_with("ftp://") + || arg_lc.starts_with("ipfs://") + { + open::that(arg).ok(); + } + } + } + Some("win-focus") => { + focus_sender.notice(); + } + Some("autoupdater-notif-clicked") => { + // We've shown the "Update Available" notification + // and the user clicked on "Restart And Update" + let autoupdater_setup_file = + autoupdater_setup_mutex.lock().unwrap().clone(); + match autoupdater_setup_file { + Some(file_path) => { + println!("Running the setup at {:?}", file_path); - let command = Command::new(file_path) - .args([ - "/SILENT", - "/NOCANCEL", - "/FORCECLOSEAPPLICATIONS", - "/TASKS=runapp", - ]) - .creation_flags(CREATE_BREAKAWAY_FROM_JOB) - .stdin(process::Stdio::null()) - .stdout(process::Stdio::null()) - .stderr(process::Stdio::null()) - .spawn(); + let command = Command::new(file_path) + .args([ + "/SILENT", + "/NOCANCEL", + "/FORCECLOSEAPPLICATIONS", + "/TASKS=runapp", + ]) + .creation_flags(CREATE_BREAKAWAY_FROM_JOB) + .stdin(process::Stdio::null()) + .stdout(process::Stdio::null()) + .stderr(process::Stdio::null()) + .spawn(); - match command { - Ok(process) => { - println!("Updater started. (PID {:?})", process.id()); - quit_sender.notice(); - } - Err(err) => eprintln!("Updater couldn't be started: {err}"), - }; - } - _ => { - println!("Cannot obtain the setup file path"); + match command { + Ok(process) => { + println!("Updater started. (PID {:?})", process.id()); + quit_sender.notice(); + } + Err(err) => eprintln!("Updater couldn't be started: {err}"), + }; + } + _ => { + println!("Cannot obtain the setup file path"); + } } } + Some(player_command) if player_command.starts_with("mpv-") => { + let resp_json = serde_json::to_string( + &request.args.expect("Cannot have method without args"), + ) + .expect("Cannot build response"); + player_tx.send(resp_json).ok(); + } + Some(unknown) => { + eprintln!("Unsupported command {}({:?})", unknown, request.get_params()) + } + None => {} } - Some(player_command) if player_command.starts_with("mpv-") => { - let resp_json = serde_json::to_string( - &msg.args.expect("Cannot have method without args"), - ) - .expect("Cannot build response"); - player_tx.send(resp_json).ok(); - } - Some(unknown) => { - eprintln!("Unsupported command {}({:?})", unknown, msg.get_params()) - } - None => {} - } - } // recv + }); + + thread::sleep(Duration::from_millis(10)); }); // thread } fn on_min_max(&self, data: &nwg::EventData) {