Browse and listen to thousands of radio stations across the globe right from your terminal ๐ŸŒŽ ๐Ÿ“ป ๐ŸŽตโœจ
radio rust tokio web-radio command-line-tool tui

fix metadata not updated

Changed files
+48 -18
src
+45 -16
src/app.rs
··· 338 338 ) { 339 339 let new_state = cmd_rx.recv().await.unwrap(); 340 340 341 - // report metadata to OS 341 + let now_playing = new_state.now_playing.clone(); 342 + let name = new_state.name.clone(); 343 + // Report initial metadata to OS 342 344 send_os_media_controls_command( 343 345 self.os_media_controls.as_mut(), 344 346 os_media_controls::Command::SetMetadata(souvlaki::MediaMetadata { 345 - title: (!new_state.now_playing.is_empty()).then_some(&new_state.now_playing), 346 - album: (!new_state.name.is_empty()).then_some(&new_state.name), 347 + title: (!now_playing.is_empty()).then(|| now_playing.as_str()), 348 + album: (!name.is_empty()).then(|| name.as_str()), 347 349 artist: None, 348 350 cover_url: None, 349 351 duration: None, 350 352 }), 351 353 ); 352 - // report started playing to OS 354 + // Report started playing to OS 353 355 send_os_media_controls_command( 354 356 self.os_media_controls.as_mut(), 355 357 os_media_controls::Command::Play, 356 358 ); 357 - // report volume to OS 359 + // Report volume to OS 358 360 send_os_media_controls_command( 359 361 self.os_media_controls.as_mut(), 360 362 os_media_controls::Command::SetVolume(new_state.volume.volume_ratio() as f64), 361 363 ); 362 364 363 365 let new_state = Arc::new(Mutex::new(new_state)); 364 - 365 366 let id = id.to_string(); 366 367 let new_state_clone = new_state.clone(); 367 368 368 - thread::spawn(move || loop { 369 + // Background thread to update now_playing 370 + thread::spawn(move || { 369 371 let rt = tokio::runtime::Runtime::new().unwrap(); 370 372 rt.block_on(async { 371 - let mut new_state = new_state_clone.lock().unwrap(); 372 - // Get current playing if available, otherwise use state's value 373 - new_state.now_playing = get_currently_playing(&id).await.unwrap_or_default(); 374 - drop(new_state); 375 - std::thread::sleep(Duration::from_millis(10000)); 373 + loop { 374 + let mut new_state = new_state_clone.lock().unwrap(); 375 + // Get current playing if available, otherwise use default 376 + let now_playing = get_currently_playing(&id).await.unwrap_or_default(); 377 + if new_state.now_playing != now_playing { 378 + new_state.now_playing = now_playing; 379 + } 380 + drop(new_state); 381 + std::thread::sleep(Duration::from_millis(10000)); 382 + } 376 383 }); 377 384 }); 378 385 379 386 let mut fps = 0; 380 387 let mut framerate = 0; 381 388 let mut last_poll = Instant::now(); 389 + let mut last_metadata_update = Instant::now(); 390 + let mut last_now_playing = String::new(); 391 + const METADATA_UPDATE_INTERVAL: Duration = Duration::from_secs(1); // Check every second 382 392 383 393 loop { 384 394 let channels = if self.graph.pause { 385 395 None 386 396 } else { 387 397 let Ok(audio_frame) = self.frame_rx.recv() else { 388 - // other thread has closed so application has 389 - // closed 398 + // other thread has closed so application has closed 390 399 return; 391 400 }; 392 401 Some(stream_to_matrix( ··· 441 450 size.y += 8; 442 451 } 443 452 let chart = Chart::new(datasets.iter().map(|x| x.into()).collect()) 444 - .x_axis(current_display.axis(&self.graph, Dimension::X)) // TODO allow to have axis sometimes? 453 + .x_axis(current_display.axis(&self.graph, Dimension::X)) 445 454 .y_axis(current_display.axis(&self.graph, Dimension::Y)); 446 455 f.render_widget(chart, size) 447 456 } 448 457 }) 449 458 .unwrap(); 459 + 460 + // Update metadata only if needed and at a controlled interval 461 + if last_metadata_update.elapsed() >= METADATA_UPDATE_INTERVAL { 462 + let state = new_state.lock().unwrap(); 463 + if state.now_playing != last_now_playing { 464 + let now_playing = state.now_playing.clone(); 465 + let name = state.name.clone(); 466 + send_os_media_controls_command( 467 + self.os_media_controls.as_mut(), 468 + os_media_controls::Command::SetMetadata(souvlaki::MediaMetadata { 469 + title: (!now_playing.is_empty()).then_some(now_playing.as_str()), 470 + album: (!name.is_empty()).then_some(name.as_str()), 471 + artist: None, 472 + cover_url: None, 473 + duration: None, 474 + }), 475 + ); 476 + last_now_playing = state.now_playing.clone(); 477 + } 478 + last_metadata_update = Instant::now(); 479 + } 450 480 } 451 481 452 482 while let Some(event) = self ··· 481 511 } 482 512 } 483 513 } 484 - 485 514 fn current_display_mut(&mut self) -> Option<&mut dyn DisplayMode> { 486 515 match self.mode { 487 516 CurrentDisplayMode::Oscilloscope => {
+3 -2
src/play.rs
··· 1 - use std::{thread, time::Duration}; 1 + use std::{process, thread, time::Duration}; 2 2 3 3 use anyhow::Error; 4 4 use hyper::header::HeaderValue; ··· 167 167 let mut terminal = tui::init()?; 168 168 app.run(&mut terminal, cmd_rx, sink_cmd_tx, &id).await; 169 169 tui::restore()?; 170 - Ok(()) 170 + 171 + process::exit(0); 171 172 } 172 173 173 174 /// Command for a sink.