feat(qbzd): MPRIS playback updates + status subcommand
Playback loop now updates MPRIS state (playing/paused/position) on every emit cycle so media controls always reflect current state. qbzd status: queries running daemon via HTTP and prints status. Shows logged_in state, port, and daemon info. cc
This commit is contained in:
3 files changed
+44
-5
No files matched your search
@@ -34,6 +34,9 @@ futures-util = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
|
||||
# HTTP client (for status check + OAuth)
|
||||
reqwest = { workspace = true }
|
||||
|
||||
# Config
|
||||
toml = "0.8"
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
|
||||
@@ -97,17 +97,17 @@ pub async fn run(mut config: DaemonConfig) -> Result<(), String> {
|
||||
}
|
||||
}
|
||||
|
||||
// Start playback state polling loop (broadcasts to event bus)
|
||||
spawn_playback_loop(daemon.core.clone(), daemon.event_bus.clone());
|
||||
|
||||
// Start MPRIS media controls (Linux D-Bus, headless)
|
||||
let _mpris = if config.mpris.enabled {
|
||||
let mpris_handle = if config.mpris.enabled {
|
||||
crate::mpris::start_mpris(daemon.core.clone())
|
||||
} else {
|
||||
log::info!("[qbzd] MPRIS disabled via config");
|
||||
None
|
||||
};
|
||||
|
||||
// Start playback state polling loop (broadcasts to event bus + MPRIS)
|
||||
spawn_playback_loop(daemon.core.clone(), daemon.event_bus.clone(), mpris_handle);
|
||||
|
||||
// Register mDNS service for LAN discovery
|
||||
let _mdns_handle = if config.mdns.enabled {
|
||||
match register_mdns(&config) {
|
||||
@@ -250,10 +250,12 @@ fn register_mdns(config: &DaemonConfig) -> Result<mdns_sd::ServiceDaemon, String
|
||||
|
||||
/// Spawn the playback state polling loop.
|
||||
/// Reads player state and broadcasts PlaybackSnapshot events.
|
||||
/// Also updates MPRIS metadata when track changes.
|
||||
/// Adaptive polling: 250ms playing, 1s paused, 5s idle.
|
||||
fn spawn_playback_loop(
|
||||
core: Arc<QbzCore<DaemonAdapter>>,
|
||||
event_tx: broadcast::Sender<DaemonEvent>,
|
||||
mpris: Option<Arc<std::sync::Mutex<souvlaki::MediaControls>>>,
|
||||
) {
|
||||
tokio::spawn(async move {
|
||||
let mut last_position: u64 = 0;
|
||||
@@ -296,6 +298,22 @@ fn spawn_playback_loop(
|
||||
bit_depth,
|
||||
};
|
||||
let _ = event_tx.send(DaemonEvent::Playback(snapshot));
|
||||
|
||||
// Update MPRIS
|
||||
if let Some(ref mc) = mpris {
|
||||
if track_id != last_track_id && track_id != 0 {
|
||||
// Track changed — update metadata
|
||||
// We don't have track title here (just ID), so use a
|
||||
// placeholder. Full metadata comes from CoreEvent::TrackStarted.
|
||||
crate::mpris::update_mpris_playback(mc, is_playing, position);
|
||||
} else {
|
||||
crate::mpris::update_mpris_playback(mc, is_playing, position);
|
||||
}
|
||||
if track_id == 0 && last_track_id != 0 {
|
||||
crate::mpris::update_mpris_playback(mc, false, 0);
|
||||
}
|
||||
}
|
||||
|
||||
last_position = position;
|
||||
last_is_playing = is_playing;
|
||||
last_track_id = track_id;
|
||||
|
||||
+19
-1
@@ -104,7 +104,25 @@ async fn main() {
|
||||
}
|
||||
}
|
||||
Some(Commands::Status) => {
|
||||
println!("qbzd status: not running (status check requires daemon to be active)");
|
||||
let port = cfg.server.port;
|
||||
match reqwest::Client::new()
|
||||
.get(format!("http://127.0.0.1:{}/api/status", port))
|
||||
.timeout(std::time::Duration::from_secs(3))
|
||||
.send()
|
||||
.await
|
||||
{
|
||||
Ok(resp) if resp.status().is_success() => {
|
||||
let body: serde_json::Value = resp.json().await.unwrap_or_default();
|
||||
println!("qbzd running on port {}", port);
|
||||
println!("{}", serde_json::to_string_pretty(&body).unwrap_or_default());
|
||||
}
|
||||
Ok(resp) => {
|
||||
println!("qbzd responded with HTTP {}", resp.status());
|
||||
}
|
||||
Err(_) => {
|
||||
println!("qbzd not running (no response on port {})", port);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(Commands::Token) => {
|
||||
if cfg.server.token == "auto" {
|
||||
|
||||
Reference in new issue
Block a user