Rockbox open source high quality audio player as a Music Player Daemon
mpris rockbox mpd libadwaita audio rust zig deno
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

Improve network robustness, TLS and platform init

- Enable rustls aws-lc feature and install default crypto provider for
rustls 0.23+
- Build root cert store correctly and improve WebSocket connect error
handling
- Add retry logic for gRPC connections in rocksky client
- Initialize SDL audio from the event thread on Linux; avoid
reinitializing in sink
- Expand $HOME in PATH and use cp instead of mv in typesense setup
- Update deno submodule and apply minor whitespace fixes

+100 -23
+1 -1
apps/main.c
··· 449 449 sb_skin_init(); 450 450 viewportmanager_init(); 451 451 452 + 452 453 storage_init(); 453 454 pcm_init(); 454 455 dsp_init(); ··· 478 479 usb_init(); 479 480 usb_start_monitoring(); 480 481 #endif 481 - 482 482 #ifdef ROCKBOX_SERVER 483 483 server_init(); 484 484 broker_init();
+1 -1
apps/server_thread.c
··· 62 62 IF_COP(, CPU)); 63 63 64 64 sleep(HZ); /* Give it a chance to start */ 65 - 65 + 66 66 start_servers(); 67 67 68 68 /* Probably safe to say */
+1 -1
crates/rocksky/Cargo.toml
··· 26 26 tonic-web = "0.12.3" 27 27 tungstenite = { version = "0.26.2", features = ["rustls-tls-webpki-roots"] } 28 28 tokio-tungstenite = { version = "0.26.2", features = ["rustls-tls-webpki-roots"] } 29 - rustls = "0.23" 29 + rustls = { version = "0.23", features = ["aws-lc-rs"] } 30 30 webpki-roots = "0.26" 31 31 futures-util = "0.3.31" 32 32 tokio-stream = "0.1.17"
+83 -10
crates/rocksky/src/lib.rs
··· 42 42 } 43 43 44 44 pub async fn run_ws_session(token: String) -> Result<(), Error> { 45 + // Install the default crypto provider for rustls 0.23+ 46 + let _ = rustls::crypto::aws_lc_rs::default_provider().install_default(); 47 + 45 48 let rocksky_ws = 46 49 env::var("ROCKSKY_WS").unwrap_or_else(|_| "wss://api.rocksky.app/ws".to_string()); 47 50 48 - let root_store = rustls::RootCertStore { 49 - roots: webpki_roots::TLS_SERVER_ROOTS.to_vec(), 50 - }; 51 + let mut root_store = rustls::RootCertStore::empty(); 52 + root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); 51 53 let tls_config = rustls::ClientConfig::builder() 52 54 .with_root_certificates(root_store) 53 55 .with_no_client_auth(); 54 56 let connector = Connector::Rustls(Arc::new(tls_config)); 55 57 56 58 let (ws_stream, _) = 57 - connect_async_tls_with_config(&rocksky_ws, None, false, Some(connector)).await?; 59 + match connect_async_tls_with_config(&rocksky_ws, None, false, Some(connector)).await { 60 + Ok(stream) => stream, 61 + Err(e) => { 62 + eprintln!("WebSocket connection failed: {:?}", e); 63 + return Err(e.into()); 64 + } 65 + }; 58 66 println!("Connected to {}", rocksky_ws); 59 67 60 68 let (mut write, mut read) = ws_stream.split(); ··· 111 119 let host = env::var("ROCKBOX_HOST").unwrap_or_else(|_| "localhost".to_string()); 112 120 let port = env::var("ROCKBOX_PORT").unwrap_or_else(|_| "6061".to_string()); 113 121 let url = format!("tcp://{}:{}", host, port); 114 - let mut client = PlaybackServiceClient::connect(url).await?; 122 + 123 + // Retry gRPC connection up to 10 times with 1 second delay 124 + let mut client = None; 125 + for attempt in 1..=10 { 126 + match PlaybackServiceClient::connect(url.clone()).await { 127 + Ok(c) => { 128 + client = Some(c); 129 + break; 130 + } 131 + Err(e) if attempt < 10 => { 132 + eprintln!( 133 + "gRPC connection attempt {}/10 failed: {}. Retrying...", 134 + attempt, e 135 + ); 136 + tokio::time::sleep(std::time::Duration::from_secs(1)).await; 137 + } 138 + Err(e) => { 139 + return Err(anyhow!( 140 + "Failed to connect to Rockbox gRPC server after 10 attempts: {}", 141 + e 142 + )); 143 + } 144 + } 145 + } 146 + let mut client = client.unwrap(); 115 147 116 148 while let Some(msg) = read.next().await { 117 149 let msg = match msg { 118 150 Ok(m) => m.to_string(), 119 151 Err(e) => { 120 - eprintln!("Read error: {}", e); 121 - break; 152 + eprintln!("WebSocket read error: {:?}", e); 153 + return Err(anyhow!("WebSocket read error: {:?}", e)); 122 154 } 123 155 }; 124 156 ··· 172 204 let host = env::var("ROCKBOX_HOST").unwrap_or_else(|_| "localhost".to_string()); 173 205 let port = env::var("ROCKBOX_PORT").unwrap_or_else(|_| "6061".to_string()); 174 206 let url = format!("tcp://{}:{}", host, port); 175 - let mut client = PlaybackServiceClient::connect(url).await?; 207 + 208 + // Retry gRPC connection up to 10 times with 1 second delay 209 + let mut client = None; 210 + for attempt in 1..=10 { 211 + match PlaybackServiceClient::connect(url.clone()).await { 212 + Ok(c) => { 213 + client = Some(c); 214 + break; 215 + } 216 + Err(_) if attempt < 10 => { 217 + tokio::time::sleep(std::time::Duration::from_secs(1)).await; 218 + } 219 + Err(e) => { 220 + return Err(anyhow!( 221 + "Failed to connect to Rockbox gRPC server for track stream: {}", 222 + e 223 + )); 224 + } 225 + } 226 + } 227 + let mut client = client.unwrap(); 176 228 let mut stream = client 177 229 .stream_current_track(tonic::Request::new(StreamCurrentTrackRequest {})) 178 230 .await? ··· 205 257 let host = env::var("ROCKBOX_HOST").unwrap_or_else(|_| "localhost".to_string()); 206 258 let port = env::var("ROCKBOX_PORT").unwrap_or_else(|_| "6061".to_string()); 207 259 let url = format!("tcp://{}:{}", host, port); 208 - let mut client = PlaybackServiceClient::connect(url).await?; 260 + 261 + // Retry gRPC connection up to 10 times with 1 second delay 262 + let mut client = None; 263 + for attempt in 1..=10 { 264 + match PlaybackServiceClient::connect(url.clone()).await { 265 + Ok(c) => { 266 + client = Some(c); 267 + break; 268 + } 269 + Err(_) if attempt < 10 => { 270 + tokio::time::sleep(std::time::Duration::from_secs(1)).await; 271 + } 272 + Err(e) => { 273 + return Err(anyhow!( 274 + "Failed to connect to Rockbox gRPC server for status stream: {}", 275 + e 276 + )); 277 + } 278 + } 279 + } 280 + let mut client = client.unwrap(); 209 281 let mut stream = client 210 282 .stream_status(tonic::Request::new(StreamStatusRequest {})) 211 283 .await? ··· 246 318 println!("WebSocket session ended cleanly"); 247 319 } 248 320 Err(e) => { 249 - eprintln!("WebSocket session error: {}", e); 321 + eprintln!("WebSocket session error: {:?}", e); 322 + eprintln!("Error details: {:#?}", e); 250 323 } 251 324 } 252 325
+7 -5
crates/typesense/src/lib.rs
··· 7 7 pub mod types; 8 8 9 9 pub fn setup() -> Result<(), anyhow::Error> { 10 + let homedir = 11 + dirs::home_dir().ok_or_else(|| anyhow::anyhow!("Could not determine home directory"))?; 12 + 10 13 let path = format!( 11 - "{}:{}", 14 + "{}:{}/{}", 12 15 std::env::var("PATH").unwrap_or_default(), 13 - "~/.rockbox/bin" 16 + homedir.display(), 17 + ".rockbox/bin" 14 18 ); 15 19 let mut cmd = Command::new("sh") 16 20 .arg("-c") ··· 20 24 .stdout(std::process::Stdio::null()) 21 25 .spawn()?; 22 26 23 - let homedir = 24 - dirs::home_dir().ok_or_else(|| anyhow::anyhow!("Could not determine home directory"))?; 25 27 let data_dir = homedir.join(".config/rockbox.org/typesense"); 26 28 fs::create_dir_all(&data_dir)?; 27 29 ··· 85 87 86 88 Command::new("sh") 87 89 .arg("-c") 88 - .arg("mkdir -p ~/.rockbox/bin && mv typesense-server ~/.rockbox/bin && chmod +x ~/.rockbox/bin/typesense-server && rm -f typesense-server-*.tar.gz typesense-server.md5.txt") 90 + .arg("mkdir -p ~/.rockbox/bin && cp typesense-server ~/.rockbox/bin && chmod +x ~/.rockbox/bin/typesense-server && rm -f typesense-server typesense-server-*.tar.gz typesense-server.md5.txt") 89 91 .stdout(Stdio::inherit()) 90 92 .stderr(Stdio::inherit()) 91 93 .status()?;
+1 -5
firmware/target/hosted/sdl/pcm-sdl.c
··· 405 405 406 406 static void sink_dma_init(void) 407 407 { 408 - if (SDL_InitSubSystem(SDL_INIT_AUDIO)) 409 - { 410 - panicf("Could not initialize SDL audio subsystem!"); 411 - return; 412 - } 408 + /* SDL_INIT_AUDIO should already be initialized by the event thread on Linux */ 413 409 414 410 #ifdef SIMULATOR 415 411 int cnt = SDL_GetNumAudioDrivers();
+6
firmware/target/hosted/sdl/system-sdl.c
··· 134 134 /* let system_init proceed */ 135 135 SDL_SemPost((SDL_sem *)param); 136 136 137 + #if !defined(__WIN32) && !defined(__APPLE__) 138 + /* On Linux, initialize SDL_INIT_AUDIO after system_init can proceed 139 + * This must happen in the event thread but after the semaphore post */ 140 + SDL_InitSubSystem(SDL_INIT_AUDIO); 141 + #endif 142 + 137 143 /* finally enter the button loop */ 138 144 gui_message_loop(); 139 145