Rockbox open source high quality audio player as a Music Player Daemon
mpris rockbox mpd libadwaita audio rust zig deno
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

Improve scrobble tracking and prevent duplicates

Replace previous_index with current_scrobble_track and a
scrobbled_tracks HashSet. Monitor playback progress and spawn a scrobble
when elapsed/length >= 40% (and length > 30s). Use cloned track IDs to
avoid duplicate scrobbles and reset monitoring on track change or when
no track is playing. Clean up ffi import grouping.

+49 -18
+49 -18
crates/server/src/lib.rs
··· 13 13 use rockbox_sys::{self as rb, types::mp3_entry::Mp3Entry}; 14 14 use sqlx::{Pool, Sqlite}; 15 15 use std::{ 16 - collections::HashMap, 17 - ffi::c_char, 18 - ffi::c_int, 16 + collections::{HashMap, HashSet}, 17 + ffi::{c_char, c_int}, 19 18 sync::{Arc, Mutex}, 20 19 thread, 21 20 }; ··· 260 259 .unwrap(); 261 260 262 261 let mut metadata_cache: HashMap<String, Mp3Entry> = HashMap::new(); 263 - let mut previous_index = -10; // Arbitrary value to ensure the first track is scobbled 262 + 263 + let mut current_scrobble_track: Option<Track> = None; // The track we are monitoring for scrobble 264 + let mut scrobbled_tracks: HashSet<String> = HashSet::new(); // Simple unique ID to prevent duplicates (use track.id if available) 264 265 265 266 loop { 266 267 let mutex = GLOBAL_MUTEX.lock().unwrap(); ··· 277 278 278 279 let playback_status: AudioStatus = rb::playback::status().into(); 279 280 SimpleBroker::publish(playback_status); 281 + 280 282 match rb::playback::current_track() { 281 283 Some(current_track) => { 282 284 let hash = format!("{:x}", md5::compute(current_track.path.as_bytes())); ··· 284 286 rt.block_on(repo::track::find_by_md5(pool.clone(), &hash)) 285 287 { 286 288 let mut track: Track = current_track.into(); 287 - track.id = Some(metadata.id); 289 + track.id = Some(metadata.id.clone()); 288 290 track.album_art = metadata.album_art; 289 291 track.album_id = Some(metadata.album_id); 290 292 track.artist_id = Some(metadata.artist_id); 291 293 SimpleBroker::publish(track.clone()); 292 294 293 - if previous_index != rb::playlist::index() { 294 - let cloned_pool = pool.clone(); 295 - thread::spawn(move || { 296 - let rt = tokio::runtime::Builder::new_current_thread() 297 - .enable_all() 298 - .build() 299 - .unwrap(); 300 - match rt.block_on(scrobble(track.clone(), cloned_pool.clone())) { 301 - Ok(_) => {} 302 - Err(e) => eprintln!("{}", e), 295 + let track_changed = if let Some(ref current) = current_scrobble_track { 296 + current.path != track.path 297 + } else { 298 + true 299 + }; 300 + 301 + if track_changed { 302 + current_scrobble_track = Some(track.clone()); 303 + } 304 + 305 + // Check progress for scrobbling (only if we have a track to monitor) 306 + if let Some(ref monitored_track) = current_scrobble_track { 307 + if monitored_track.path == track.path { 308 + let elapsed_ms = track.elapsed; 309 + let length_ms = track.length; 310 + 311 + if length_ms > 30_000 && // optional: ignore very short tracks per Last.fm rules 312 + elapsed_ms as f64 / length_ms as f64 >= 0.40 313 + { 314 + if !scrobbled_tracks.contains(&metadata.id) { 315 + let cloned_pool = pool.clone(); 316 + let cloned_track = monitored_track.clone(); 317 + thread::spawn(move || { 318 + let rt = tokio::runtime::Builder::new_current_thread() 319 + .enable_all() 320 + .build() 321 + .unwrap(); 322 + match rt 323 + .block_on(scrobble(cloned_track, cloned_pool.clone())) 324 + { 325 + Ok(_) => {} 326 + Err(e) => eprintln!("{}", e), 327 + } 328 + }); 329 + scrobbled_tracks.insert(metadata.id.clone()); 330 + } 331 + } else { 332 + scrobbled_tracks.clear(); 303 333 } 304 - }); 334 + } 305 335 } 306 - previous_index = rb::playlist::index(); 307 336 } 308 337 } 309 - None => {} 338 + None => { 339 + current_scrobble_track = None; // reset on no track 340 + } 310 341 }; 311 342 312 343 let mut entries: Vec<Mp3Entry> = vec![];
macos/Rockbox.xcodeproj/project.xcworkspace/xcuserdata/tsirysandratraina.xcuserdatad/UserInterfaceState.xcuserstate

This is a binary file and will not be displayed.