From ff4271181c45a1b8c6674280ca47aec97a1014a0 Mon Sep 17 00:00:00 2001 From: Psychotoxical <171614930+Psychotoxical@users.noreply.github.com> Date: Sat, 9 May 2026 01:47:02 +0200 Subject: [PATCH] diag(audio): trace ranged-stream supersedion + abort paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Skipping tracks while a RangedHttpSource is still in initial probe leaves no diagnostic trail today: RangedHttpSource::read returns Ok(0) on gen-mismatch (silent), ranged_download_task drops out the same way, and the `dl done` summary only fires under app_deprintln so release users never see partial/aborted downloads either. Add focused logs at the bail points without changing behaviour: - RangedHttpSource::read — log on each Ok(0) return that isn't the normal pos>=total_size EOF (superseded before/during wait, download done with no bytes ahead of cursor). Symphonia stops reading after Ok(0), so at most one log per source, no spam. - ranged_download_task — log on the gen-mismatch bail with track id + gen transition + downloaded/total bytes so we can tell "user skipped while downloading" apart from "stream stalled". - track_download_task — same gen-mismatch log for the legacy non-seekable path (consistency with ranged). - dl-done summary — split into release-visible `[stream] ranged dl ABORTED: …` (downloaded < total_size) vs the existing dev-only `[stream] dl done` for full completions. - audio_play — log on both supersedion-bail points around select_play_input so a silently-ending audio_play call leaves a trace. No semantics changed: every existing return / store / branch is intact. Pure additive logging to make the next reproduction of cucadmuh's ranged-stream toast diagnosable. --- src-tauri/src/audio/commands.rs | 12 ++++++++- src-tauri/src/audio/stream.rs | 46 ++++++++++++++++++++++++++++----- 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/src-tauri/src/audio/commands.rs b/src-tauri/src/audio/commands.rs index b26f82a0..2eae3de3 100644 --- a/src-tauri/src/audio/commands.rs +++ b/src-tauri/src/audio/commands.rs @@ -147,11 +147,21 @@ pub async fn audio_play( &app, ).await? { Some(input) => input, - None => return Ok(()), // superseded — bail + None => { + crate::app_deprintln!( + "[audio] audio_play superseded inside select_play_input: gen={} cur={} track_id={:?}", + gen, state.generation.load(Ordering::SeqCst), cache_id_for_tasks + ); + return Ok(()); + } }; if state.generation.load(Ordering::SeqCst) != gen { + crate::app_deprintln!( + "[audio] audio_play superseded after select_play_input: gen={} cur={} track_id={:?}", + gen, state.generation.load(Ordering::SeqCst), cache_id_for_tasks + ); return Ok(()); } diff --git a/src-tauri/src/audio/stream.rs b/src-tauri/src/audio/stream.rs index 129345e8..6b71b02d 100644 --- a/src-tauri/src/audio/stream.rs +++ b/src-tauri/src/audio/stream.rs @@ -289,6 +289,10 @@ pub(crate) struct RangedHttpSource { impl Read for RangedHttpSource { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { if self.gen_arc.load(Ordering::SeqCst) != self.gen { + crate::app_deprintln!( + "[stream] ranged-stream read EOF: superseded before first read (gen={} cur={} pos={}/{})", + self.gen, self.gen_arc.load(Ordering::SeqCst), self.pos, self.total_size + ); return Ok(0); } if self.pos >= self.total_size { @@ -303,6 +307,11 @@ impl Read for RangedHttpSource { let deadline = Instant::now() + Duration::from_secs(RADIO_READ_TIMEOUT_SECS); loop { if self.gen_arc.load(Ordering::SeqCst) != self.gen { + crate::app_deprintln!( + "[stream] ranged-stream read EOF: superseded mid-wait (gen={} cur={} pos={}/{} dl={})", + self.gen, self.gen_arc.load(Ordering::SeqCst), self.pos, self.total_size, + self.downloaded_to.load(Ordering::SeqCst) + ); return Ok(0); } let dl = self.downloaded_to.load(Ordering::SeqCst) as u64; @@ -321,6 +330,10 @@ impl Read for RangedHttpSource { self.pos += avail as u64; return Ok(avail); } + crate::app_deprintln!( + "[stream] ranged-stream read EOF: download done with no data ahead of cursor (pos={}/{} dl={})", + self.pos, self.total_size, dl + ); return Ok(0); } if Instant::now() >= deadline { @@ -654,6 +667,10 @@ pub(crate) async fn track_download_task( let mut byte_stream = response.bytes_stream(); while let Some(chunk) = byte_stream.next().await { if gen_arc.load(Ordering::SeqCst) != gen { + crate::app_deprintln!( + "[stream] track-stream dl superseded by skip: track_id={:?} gen={}→{}", + cache_track_id, gen, gen_arc.load(Ordering::SeqCst) + ); done.store(true, Ordering::SeqCst); return; } @@ -842,6 +859,10 @@ pub(crate) async fn ranged_download_task( let mut byte_stream = response.bytes_stream(); while let Some(chunk) = byte_stream.next().await { if gen_arc.load(Ordering::SeqCst) != gen { + crate::app_deprintln!( + "[stream] ranged dl superseded by skip: track_id={:?} gen={}→{} downloaded={}/{} bytes", + cache_track_id, gen, gen_arc.load(Ordering::SeqCst), downloaded, total_size + ); done.store(true, Ordering::SeqCst); return; } @@ -928,13 +949,24 @@ pub(crate) async fn ranged_download_task( done.store(true, Ordering::SeqCst); - crate::app_deprintln!( - "[stream] dl done: {} / {} bytes in {:.2}s ({} reconnects)", - downloaded, - total_size, - dl_started.elapsed().as_secs_f64(), - reconnects - ); + if downloaded < total_size { + crate::app_eprintln!( + "[stream] ranged dl ABORTED: {} / {} bytes in {:.2}s ({} reconnects, track_id={:?})", + downloaded, + total_size, + dl_started.elapsed().as_secs_f64(), + reconnects, + cache_track_id + ); + } else { + crate::app_deprintln!( + "[stream] dl done: {} / {} bytes in {:.2}s ({} reconnects)", + downloaded, + total_size, + dl_started.elapsed().as_secs_f64(), + reconnects + ); + } if downloaded == total_size && total_size > 0 && total_size <= TRACK_STREAM_PROMOTE_MAX_BYTES { if let Some(ref tid) = cache_track_id {