@@ -13,9 +13,8 @@ use rockbox_sys::events::RockboxCommand;
1313use rockbox_sys:: { self as rb, types:: mp3_entry:: Mp3Entry } ;
1414use sqlx:: { Pool , Sqlite } ;
1515use std:: {
16- collections:: HashMap ,
17- ffi:: c_char,
18- ffi:: c_int,
16+ collections:: { HashMap , HashSet } ,
17+ ffi:: { c_char, c_int} ,
1918 sync:: { Arc , Mutex } ,
2019 thread,
2120} ;
@@ -260,7 +259,9 @@ pub extern "C" fn start_broker() {
260259 . unwrap ( ) ;
261260
262261 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)
264265
265266 loop {
266267 let mutex = GLOBAL_MUTEX . lock ( ) . unwrap ( ) ;
@@ -277,36 +278,66 @@ pub extern "C" fn start_broker() {
277278
278279 let playback_status: AudioStatus = rb:: playback:: status ( ) . into ( ) ;
279280 SimpleBroker :: publish ( playback_status) ;
281+
280282 match rb:: playback:: current_track ( ) {
281283 Some ( current_track) => {
282284 let hash = format ! ( "{:x}" , md5:: compute( current_track. path. as_bytes( ) ) ) ;
283285 if let Ok ( Some ( metadata) ) =
284286 rt. block_on ( repo:: track:: find_by_md5 ( pool. clone ( ) , & hash) )
285287 {
286288 let mut track: Track = current_track. into ( ) ;
287- track. id = Some ( metadata. id ) ;
289+ track. id = Some ( metadata. id . clone ( ) ) ;
288290 track. album_art = metadata. album_art ;
289291 track. album_id = Some ( metadata. album_id ) ;
290292 track. artist_id = Some ( metadata. artist_id ) ;
291293 SimpleBroker :: publish ( track. clone ( ) ) ;
292294
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 ( ) ;
303333 }
304- } ) ;
334+ }
305335 }
306- previous_index = rb:: playlist:: index ( ) ;
307336 }
308337 }
309- None => { }
338+ None => {
339+ current_scrobble_track = None ; // reset on no track
340+ }
310341 } ;
311342
312343 let mut entries: Vec < Mp3Entry > = vec ! [ ] ;
0 commit comments