From 04add15691578316ff3da5b5a07d05b314488370 Mon Sep 17 00:00:00 2001 From: salmonik Date: Sat, 16 May 2026 11:43:32 +0200 Subject: Add scrobbling support (Last.fm + ListenBrainz) and rsp settings reload MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Full Last.fm API implementation with MD5 signing (track.updateNowPlaying, track.scrobble) - Full ListenBrainz API implementation (/1/submit-listens for playing_now and single) - ScrobbleState in daemon tracking track, start time, and unix timestamp per play - Scrobble threshold: ≥30s play AND (≥half duration OR ≥4 min) - Duration extraction from symphonia codec params into AudioMetadata and Track - Token validation at daemon startup and on config change - ListenBrainz token valid (user: Salmonium) command to re-read config from disk (avoids token in shell history) - Desktop notifications for token validation results - Empty-credential filtering (empty string = no-op) --- src/queue.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'src/queue.rs') diff --git a/src/queue.rs b/src/queue.rs index d42203f..8f98173 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -13,6 +13,7 @@ pub struct AudioMetadata { pub title: Option, pub artist: Option, pub album: Option, + pub duration_secs: Option, } pub fn extract_metadata(path: &Path) -> Option { @@ -28,7 +29,22 @@ pub fn extract_metadata(path: &Path) -> Option { .format(&hint, mss, &Default::default(), &Default::default()) .ok()?; - let mut metadata = AudioMetadata::default(); + // Extract duration from format track's codec parameters + let duration_secs = probed + .format + .tracks() + .iter() + .find(|t| t.codec_params.n_frames.is_some() && t.codec_params.time_base.is_some()) + .and_then(|t| { + let n_frames = t.codec_params.n_frames?; + let tb = t.codec_params.time_base?; + Some(n_frames as f64 * tb.numer as f64 / tb.denom as f64) + }); + + let mut metadata = AudioMetadata { + duration_secs, + ..Default::default() + }; if let Some(meta) = probed.metadata.get() && let Some(rev) = meta.current() @@ -82,6 +98,7 @@ pub struct Track { pub title: Option, pub artist: Option, pub album: Option, + pub duration_secs: Option, } impl Track { @@ -93,6 +110,7 @@ impl Track { title: metadata.and_then(|m| m.title.clone()), artist: metadata.and_then(|m| m.artist.clone()), album: metadata.and_then(|m| m.album.clone()), + duration_secs: metadata.and_then(|m| m.duration_secs), } } -- cgit v1.2.3