Connected Devices: added remote control

This commit is contained in:
Ultradesu
2026-07-24 04:20:52 +03:00
parent 9352b493a1
commit 1065ea5afc
+60
View File
@@ -40,6 +40,9 @@ pub struct Runtime {
pub federation: Arc<crate::federation::Federation>,
/// When the last Federation-tab status snapshot was requested.
pub fed_status_at: Option<std::time::Instant>,
/// Coalesces urgent personal-device syncs after remote playback commands.
pub device_sync_running: Arc<std::sync::atomic::AtomicBool>,
pub device_sync_requested: Arc<std::sync::atomic::AtomicBool>,
/// Placeholder ids of federated tracks being downloaded right now.
pub fed_resolving: std::sync::Mutex<std::collections::HashSet<i64>>,
/// Caps concurrent artwork loads so they never starve the disk.
@@ -111,6 +114,8 @@ pub async fn run(
devices,
federation,
fed_status_at: None,
device_sync_running: Arc::new(std::sync::atomic::AtomicBool::new(false)),
device_sync_requested: Arc::new(std::sync::atomic::AtomicBool::new(false)),
fed_resolving: std::sync::Mutex::new(std::collections::HashSet::new()),
art_semaphore: Arc::new(tokio::sync::Semaphore::new(4)),
force_redraw: false,
@@ -380,9 +385,64 @@ fn record_control_playback_state(state: &mut AppState, runtime: &Runtime) {
if let Err(err) = runtime.devices.record_playback_command(&target, command) {
tracing::warn!(%err, target, "recording playback command failed");
state.status_message = Some(format!("device command failed: {err:#}"));
} else {
request_urgent_device_sync(runtime);
}
}
fn request_urgent_device_sync(runtime: &Runtime) {
use std::sync::atomic::Ordering;
runtime.device_sync_requested.store(true, Ordering::SeqCst);
if runtime
.device_sync_running
.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
.is_ok()
{
spawn_urgent_device_sync(
Arc::clone(&runtime.federation),
Arc::clone(&runtime.devices),
runtime.event_tx.clone(),
Arc::clone(&runtime.device_sync_running),
Arc::clone(&runtime.device_sync_requested),
);
}
}
fn spawn_urgent_device_sync(
federation: Arc<crate::federation::Federation>,
devices: Arc<crate::devices::DeviceSync>,
tx: mpsc::UnboundedSender<AppEvent>,
running: Arc<std::sync::atomic::AtomicBool>,
requested: Arc<std::sync::atomic::AtomicBool>,
) {
use std::sync::atomic::Ordering;
tokio::spawn(async move {
loop {
requested.store(false, Ordering::SeqCst);
match federation.device_sync_now().await {
Ok(()) => {}
Err(err) => {
tracing::debug!("urgent device sync failed: {err:#}");
}
}
let _ = tx.send(AppEvent::DeviceSyncStatus(devices.status()));
if !requested.swap(false, Ordering::SeqCst) {
break;
}
}
running.store(false, Ordering::SeqCst);
if requested.load(Ordering::SeqCst)
&& running
.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
.is_ok()
{
spawn_urgent_device_sync(federation, devices, tx, running, requested);
}
});
}
const ARTISTS_PREFETCH_MARGIN: usize = 24;
/// How many artist tiles one screen holds right now (grid geometry from the