diff --git a/CHANGELOG.md b/CHANGELOG.md index 0de0ad1f..5b057082 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -98,6 +98,7 @@ https://github.com/librespot-org/librespot - [playback] Add metadata support via a `TrackChanged` event - [connect] Add `activate` and `load` functions to `Spirc`, allowing control over local connect sessions - [metadata] Add `Lyrics` +- [discovery] Add discovery initialisation retries if within the 1st min of uptime ### Fixed diff --git a/Cargo.lock b/Cargo.lock index 51682c4a..1f301939 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1355,6 +1355,7 @@ dependencies = [ "log", "rpassword", "sha1", + "sysinfo", "thiserror", "tokio", "url", diff --git a/Cargo.toml b/Cargo.toml index 98b8405b..1f380a1c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,6 +57,7 @@ hex = "0.4" log = "0.4" rpassword = "7.0" sha1 = "0.10" +sysinfo = { version = "0.29", default-features = false } thiserror = "1.0" tokio = { version = "1", features = ["rt", "macros", "signal", "sync", "parking_lot", "process"] } url = "2.2" diff --git a/src/main.rs b/src/main.rs index 48edf1c9..e8c2e66f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,6 @@ +use futures_util::StreamExt; +use log::{debug, error, info, trace, warn}; +use sha1::{Digest, Sha1}; use std::{ env, fs::create_dir_all, @@ -8,10 +11,7 @@ use std::{ str::FromStr, time::{Duration, Instant}, }; - -use futures_util::StreamExt; -use log::{error, info, trace, warn}; -use sha1::{Digest, Sha1}; +use sysinfo::{System, SystemExt}; use thiserror::Error; use url::Url; @@ -1646,6 +1646,7 @@ fn get_setup() -> Setup { async fn main() { const RUST_BACKTRACE: &str = "RUST_BACKTRACE"; const RECONNECT_RATE_LIMIT_WINDOW: Duration = Duration::from_secs(600); + const DISCOVERY_RETRY_TIMEOUT: Duration = Duration::from_secs(10); const RECONNECT_RATE_LIMIT: usize = 5; if env::var(RUST_BACKTRACE).is_err() { @@ -1664,18 +1665,43 @@ async fn main() { let mut session = Session::new(setup.session_config.clone(), setup.cache.clone()); + let mut sys = System::new(); + if setup.enable_discovery { - let device_id = setup.session_config.device_id.clone(); - let client_id = setup.session_config.client_id.clone(); - match librespot::discovery::Discovery::builder(device_id, client_id) - .name(setup.connect_config.name.clone()) - .device_type(setup.connect_config.device_type) - .port(setup.zeroconf_port) - .zeroconf_ip(setup.zeroconf_ip) - .launch() - { - Ok(d) => discovery = Some(d), - Err(err) => warn!("Could not initialise discovery: {}.", err), + // When started at boot as a service discovery may fail due to it + // trying to bind to interfaces before the network is actually up. + // This could be prevented in systemd by starting the service after + // network-online.target but it requires that a wait-online.service is + // also enabled which is not always the case since a wait-online.service + // can potentially hang the boot process until it times out in certain situations. + // This allows for discovery to retry every 10 secs in the 1st min of uptime + // before giving up thus papering over the issue and not holding up the boot process. + + discovery = loop { + let device_id = setup.session_config.device_id.clone(); + let client_id = setup.session_config.client_id.clone(); + + match librespot::discovery::Discovery::builder(device_id, client_id) + .name(setup.connect_config.name.clone()) + .device_type(setup.connect_config.device_type) + .port(setup.zeroconf_port) + .zeroconf_ip(setup.zeroconf_ip.clone()) + .launch() + { + Ok(d) => break Some(d), + Err(e) => { + sys.refresh_processes(); + + if sys.uptime() <= 1 { + debug!("Retrying to initialise discovery: {e}"); + tokio::time::sleep(DISCOVERY_RETRY_TIMEOUT).await; + } else { + debug!("System uptime > 1 min, not retrying to initialise discovery"); + warn!("Could not initialise discovery: {e}"); + break None; + } + } + } }; }