Skip to content

Commit 1738f1f

Browse files
stiefenmclaude
andcommitted
fix(24): send position on mid-song connect via needs_position_sync flag
After TrackChanged emits 'start', set needs_position_sync flag. The next same-id Playing event sends a 'seek' notification with the actual position_ms so the Perl side can sync the progress bar for mid-song connects. Without this, the position was silently discarded as a "noisy re-emit" — the API poll returns stale progress_ms=0. The flag is an Arc<AtomicBool> shared across clones. It's cleared on change events and after the position sync fires. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent c264e7f commit 1738f1f

1 file changed

Lines changed: 22 additions & 2 deletions

File tree

src/spotty.rs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,10 @@ pub mod lms_connect {
248248
pub flush_tx: Option<watch::Sender<u64>>,
249249
/// Monotonically increasing generation counter; incremented on seek.
250250
pub seek_gen: Arc<AtomicU64>,
251+
/// Set after TrackChanged emits `start`; the next same-id `Playing`
252+
/// event will send a `seek` notification with the actual position so
253+
/// LMS can sync its progress bar for mid-song connects.
254+
pub needs_position_sync: Arc<AtomicBool>,
251255
}
252256

253257
impl LMS {
@@ -268,6 +272,7 @@ pub mod lms_connect {
268272
suppress_next_volume: Arc::new(AtomicBool::new(false)),
269273
flush_tx,
270274
seek_gen: Arc::new(AtomicU64::new(0)),
275+
needs_position_sync: Arc::new(AtomicBool::new(false)),
271276
}
272277
}
273278

@@ -293,6 +298,7 @@ pub mod lms_connect {
293298
// (the one passed to the event dispatcher) fires flush signals.
294299
flush_tx: None,
295300
seek_gen: Arc::clone(&self.seek_gen),
301+
needs_position_sync: Arc::clone(&self.needs_position_sync),
296302
}
297303
}
298304
}
@@ -327,11 +333,23 @@ pub mod lms_connect {
327333
// buffer-underrun re-emit. We emit `start` only on a clean
328334
// None -> Some transition; same-id re-emits are no-ops, and
329335
// a different id replaces the cursor with `change`.
330-
PlayerEvent::Playing { track_id, .. } => {
336+
// Exception: after TrackChanged sent `start`, the next same-id
337+
// Playing carries position_ms — send it as `seek` so the Perl
338+
// side can sync the progress bar for mid-song connects.
339+
PlayerEvent::Playing { track_id, position_ms, .. } => {
331340
let new_id = track_id.to_id().unwrap_or_default();
332341
match current_track.as_deref() {
333-
Some(prev) if prev == new_id.as_str() => { /* noisy re-emit */ }
342+
Some(prev) if prev == new_id.as_str() => {
343+
if self.needs_position_sync.load(Ordering::Acquire) {
344+
self.needs_position_sync.store(false, Ordering::Release);
345+
let secs = f64::from(*position_ms) / 1000.0;
346+
if secs > 1.0 {
347+
self.notify("seek", &format!("{secs:.3}"), "").await;
348+
}
349+
}
350+
}
334351
Some(_) => {
352+
self.needs_position_sync.store(false, Ordering::Release);
335353
let prev = current_track.replace(new_id.clone()).unwrap_or_default();
336354
self.notify("change", &new_id, &prev).await;
337355
}
@@ -403,10 +421,12 @@ pub mod lms_connect {
403421
match current_track.as_deref() {
404422
Some(prev) if prev == new_id.as_str() => { /* same track */ }
405423
Some(_) => {
424+
self.needs_position_sync.store(false, Ordering::Release);
406425
let prev = current_track.replace(new_id.clone()).unwrap_or_default();
407426
self.notify("change", &new_id, &prev).await;
408427
}
409428
None => {
429+
self.needs_position_sync.store(true, Ordering::Release);
410430
*current_track = Some(new_id.clone());
411431
self.notify("start", &new_id, "").await;
412432
}

0 commit comments

Comments
 (0)