fix(player): three defects from review, and one duplicate removed
Publish Documentation / Build & publish docs to gitea-pages (push) Canceled after 0s
🏗️ Build and Test JellyTau / Run Tests (push) Successful in 21m37s
🏗️ Build and Test JellyTau / Supply Chain (push) Successful in 2m55s
Traceability Validation / Check Requirement Traces (push) Successful in 23s
Build & Release / Run Tests (push) Successful in 15m34s
🏗️ Build and Test JellyTau / Android Compile Check (push) Successful in 4m26s
Build & Release / Build Linux (push) Successful in 20m58s
Build & Release / Build Windows (push) Successful in 16m10s
Build & Release / Build Android (push) Successful in 31m25s
Build & Release / Create Release (push) Successful in 1m5s
Publish Documentation / Build & publish docs to gitea-pages (push) Canceled after 0s
🏗️ Build and Test JellyTau / Run Tests (push) Successful in 21m37s
🏗️ Build and Test JellyTau / Supply Chain (push) Successful in 2m55s
Traceability Validation / Check Requirement Traces (push) Successful in 23s
Build & Release / Run Tests (push) Successful in 15m34s
🏗️ Build and Test JellyTau / Android Compile Check (push) Successful in 4m26s
Build & Release / Build Linux (push) Successful in 20m58s
Build & Release / Build Windows (push) Successful in 16m10s
Build & Release / Build Android (push) Successful in 31m25s
Build & Release / Create Release (push) Successful in 1m5s
Verified each against the code before acting; four of the five findings held, one did not. DR-253 — a deferred seek outlived its file. `seek` holds a position while MPV has nothing loaded and `FileLoaded` applies it (DR-241), but neither `load` nor `stop` discarded it. Scrub near the end of a transcoded item — which re-opens the stream — then skip to the next item before the reload completes, and the old position lands on the new item. It starts wherever the previous one was scrubbed to, silently. Both lifecycle points clear it now. DR-254 — a per-playback quality ceiling outlived its playback. The override is process-wide and describes one playback: dropping to 720p for a struggling episode says nothing about the next. Every advance the frontend drives clears it through player_play_item, but the background audio-only advance loads the next episode in Rust and skipped all three clearing sites — so every later episode stayed capped, with nothing in the UI explaining why. DR-255 — `playable_url` was a byte-identical copy of `playback_url`, added for the cross-platform open path. The original is `#[cfg(target_os = "android")]`, so it does not exist in a Linux build and nothing warned. Two matches over MediaSource meant a new variant could be handled in one and forgotten in the other. The gate is gone and the copy with it. The fifth finding — that the comment on `video_audio_codecs` describes a renderer switch the code no longer has — does not hold. `get_player_status` hard-codes Android to Native, but `experimentalNativeVideo` is still live in VideoPlayer.svelte as a suppressor that can force HTML5 even when Rust says native. The switch exists, so the narrow codec list is still doing its job. Both correctness fixes are red-then-green. The tests are wiring assertions in the style of UT-218: what matters is the call site, and reaching these at runtime needs a live MPV handle or a repository, a server and a player. That technique now appears three times and is worth watching — it pins call sites, not behaviour. The review's sharpest point is one it raised as redundancy: MpvPlayer already handles DR-253 correctly, resetting deferred state on every open, and the old path had to be patched separately. That is the drift two parallel engines produce, and the argument for finishing DR-248/249 rather than leaving LegacyPlayer in place indefinitely. 795 Rust tests, 1088 frontend, every CI check green locally.
This commit is contained in:
@@ -582,7 +582,7 @@ impl PlayerController {
|
||||
backend.open(OpenRequest::new(
|
||||
item.clone(),
|
||||
StreamSelection::for_queued_item(
|
||||
item.playable_url(),
|
||||
item.playback_url(),
|
||||
item.transport,
|
||||
item.needs_transcoding,
|
||||
),
|
||||
@@ -1995,6 +1995,15 @@ impl PlayerController {
|
||||
&self,
|
||||
next_episode_id: &str,
|
||||
) -> Result<(), String> {
|
||||
// A new episode is a new playback, so a ceiling chosen for the previous
|
||||
// one does not carry into it. Every advance the frontend drives goes
|
||||
// through `player_play_item` and is cleared there; this one loads the
|
||||
// next episode in Rust and would otherwise keep the old cap forever,
|
||||
// with nothing in the UI saying why. Cleared before the URL is built,
|
||||
// since that is what reads it.
|
||||
// TRACES: UR-074 | DR-254
|
||||
crate::repository::online::clear_playback_quality_override();
|
||||
|
||||
let repo = self
|
||||
.repository
|
||||
.lock_safe()
|
||||
@@ -2313,6 +2322,39 @@ impl Default for PlayerController {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
/// Advancing to the next episode drops a per-playback quality override.
|
||||
///
|
||||
/// The override is process-wide and describes *one* playback: a viewer who
|
||||
/// drops to 720p for a struggling episode has said nothing about the next
|
||||
/// one. `player_play_item`, `player_play_queue` and `player_play_tracks`
|
||||
/// all clear it, so every advance the frontend drives is covered — but the
|
||||
/// background audio-only advance loads the next episode in Rust and skips
|
||||
/// all three, so every later episode stayed capped at the old quality with
|
||||
/// nothing in the UI saying so.
|
||||
///
|
||||
/// A wiring assertion, like UT-218 and UT-225: the call site is what
|
||||
/// matters, and reaching it at runtime needs a repository, a server and a
|
||||
/// live player.
|
||||
///
|
||||
/// TRACES: UR-074 | DR-254 | UT-226
|
||||
#[test]
|
||||
fn test_background_episode_advance_clears_the_quality_override() {
|
||||
let src = include_str!("mod.rs");
|
||||
let start = src
|
||||
.find("fn advance_to_next_episode_audio_only")
|
||||
.expect("advance_to_next_episode_audio_only not found");
|
||||
let rest = &src[start..];
|
||||
let end = rest.find("\n pub ").unwrap_or(rest.len());
|
||||
let body = &rest[..end];
|
||||
|
||||
assert!(
|
||||
body.contains("clear_playback_quality_override"),
|
||||
"the background episode advance does not clear the per-playback \
|
||||
quality override, so a ceiling chosen for one episode silently \
|
||||
caps every episode after it"
|
||||
);
|
||||
}
|
||||
|
||||
/// Stopping clears a background-audio handoff.
|
||||
///
|
||||
/// This was verified by listening to a tablet, which is not a test. The
|
||||
|
||||
Reference in New Issue
Block a user