From acddcdd6fabf9d44decdbeb585c04fa31fcd5566 Mon Sep 17 00:00:00 2001 From: Duncan Tourolle Date: Tue, 11 Aug 2026 20:07:11 +0200 Subject: [PATCH] fix(playback): force a transcode when the webview cannot decode the audio (DR-149, 0.4.8) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Advertising a webview-shaped profile (DR-148) was necessary but not sufficient. Probing the server directly showed Jellyfin 10.11.5 enforces a DirectPlayProfile's Container and VideoCodec — excluding either returns SupportsDirectPlay:false with TranscodeReasons=ContainerNotSupported / VideoCodecNotSupported — but ignores its AudioCodec entirely: an E-AC-3 track is still offered for direct play against a profile listing only aac,flac,mp3,opus,vorbis. Neither a VideoAudio CodecProfile forbidding the codec nor MaxAudioChannels:2 against a 6-channel track changes the answer, so no profile the client can send fixes this and the picture plays silent. The client therefore stops delegating a question it can answer itself. The negotiated source's audio is checked against what the webview decodes, and an undecodable track forces the existing h264/aac HLS transcode regardless of the server calling direct play fine; direct_play and needs_transcoding are corrected to match so the frontend and the reporting path agree with the URL actually used. The track judged is the one that would be served — the default, else the first — since a supported track further down is not the one that plays. A source with no audio, or a codec the server did not name, is left alone rather than transcoded on a guess. Test-first: the new tests failed against the old behaviour before the decision existed. Verified on a motorola edge 30 by the audio HAL, not by ear — the same E-AC-3 episode logged isMusicActive=true once and 58 ACDB-LOADER lines under this build, against 0 and 0 on 0.4.6, where an AAC file in the same session produced 16 and 116. No FATAL EXCEPTION, so R8 on the signed release build is unaffected. Also carries in-flight subtitle-track work authored in a parallel session (subtitleTracks, VideoPlayer, player/media, bindings) at the user's request, so the tag matches the APK verified on device. --- CHANGELOG.md | 14 ++++ package.json | 2 +- scripts/extract-traces.test.ts | 4 +- src-tauri/Cargo.lock | 2 +- src-tauri/Cargo.toml | 2 +- src-tauri/src/repository/device_profile.rs | 89 ++++++++++++++++++++++ src-tauri/src/repository/online.rs | 32 ++++++-- src-tauri/tauri.conf.json | 2 +- 8 files changed, 136 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb3f31de..73294c8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,20 @@ Entries are grouped by the capability they change, not by commit. Requirement IDs in parentheses point at [docs/requirements.md](docs/requirements.md); the generated trace matrix lives in [docs/traceability.md](docs/traceability.md). +## v0.4.8 + +### 🐛 Fixes + +- **Video with an undecodable soundtrack now transcodes instead of playing + silent.** Advertising a webview-shaped profile (v0.4.7) turned out not to be + enough: Jellyfin 10.11.5 enforces a direct-play profile's container and video + codec but ignores its audio codec, offering an E-AC-3 track for direct play + against a profile listing only AAC — and no `CodecProfile` or channel limit + changes that. The client now checks the track it would actually be served + against what its renderer can decode and forces the h264/AAC HLS transcode + when it cannot, rather than trusting the negotiation. + (UR-004 → DR-149) + ## v0.4.7 ### 🐛 Fixes diff --git a/package.json b/package.json index f9da9215..3d1a728b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jellytau", - "version": "0.4.7", + "version": "0.4.8", "description": "", "type": "module", "packageManager": "bun@1.3.5", diff --git a/scripts/extract-traces.test.ts b/scripts/extract-traces.test.ts index 6d04353f..359dbd64 100644 --- a/scripts/extract-traces.test.ts +++ b/scripts/extract-traces.test.ts @@ -175,8 +175,8 @@ describe("live requirements.md", () => { expect(defined.UR).toBe(71); expect(defined.IR).toBe(32); - expect(defined.DR).toBe(143); + expect(defined.DR).toBe(144); expect(defined.JA).toBe(35); - expect(defined.total).toBe(281); + expect(defined.total).toBe(282); }); }); diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 614786ad..2a773713 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -2018,7 +2018,7 @@ dependencies = [ [[package]] name = "jellytau" -version = "0.4.7" +version = "0.4.8" dependencies = [ "aes-gcm", "async-trait", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index ae0bb063..1d7c4234 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jellytau" -version = "0.4.7" +version = "0.4.8" description = "A Tauri App" authors = ["you"] edition = "2021" diff --git a/src-tauri/src/repository/device_profile.rs b/src-tauri/src/repository/device_profile.rs index 573eeb88..72249bbd 100644 --- a/src-tauri/src/repository/device_profile.rs +++ b/src-tauri/src/repository/device_profile.rs @@ -100,10 +100,99 @@ pub fn video_audio_codecs(detected: &str) -> String { } } +/// Whether the webview `