+45
-16
src/app.rs
+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
+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.