# Changelog All notable changes to JellyTau are documented here. 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.5.0 ### ✨ Features - **Android video can render on the device's own video surface.** Settings → Video Playback → **Native Video** (experimental, off by default) hands decoding to ExoPlayer, which draws into a surface composited *behind* a transparent WebView, with the player controls layered on top of it. The backend had reported "this platform has a native video surface" on Android all along, but the frontend threw that answer away in two separate places, so the path had never actually run. Both are lifted. The setting can only ever *suppress* the backend's choice, never override it upward: turning it off forces the web player even where native is available, and turning it on does nothing on platforms whose backend never offered it — Linux cannot composite behind its webview, so it stays on the web player either way. Verified playing on a physical device. Still unverified: the mini-player transition, audio-track switching on the native path, and whether hardware decoding measurably improves battery or CPU — so the toggle stays off by default. (UR-003, UR-004 → DR-150) ### 🐛 Fixes - **The video surface now reaches the screen at all.** The player built its video surface, handed it to ExoPlayer, and then never added it to the view hierarchy, because the Activity reference it needed was never supplied — so native video would have decoded to a surface nobody could see, whatever else was fixed. This also silently disabled picture-in-picture for video, which gated on that same never-attached surface. (UR-003, UR-041 → DR-151) - **Platform playback support is no longer guessed from the browser user agent.** The frontend re-derived "does this platform decode audio natively" by string-matching `navigator.userAgent` — a second copy of a decision the backend already makes, free to drift out of step with the backends it was describing. The backend now reports its own capabilities and the frontend consumes them. (UR-003, UR-005 → DR-152) ### 🔧 Internal - **The git tag is now the single source of truth for a release version.** The version lived in four files that had to be edited in lockstep, and the release workflow rewrote exactly one of them — so a tagged build produced an installer named for the tag wrapped around package metadata naming the *previous* release, and the Linux job, which had no version step at all, shipped whatever happened to be committed. `scripts/set-version.sh` now writes all four from one argument and every release job calls it with the tag. The Android `versionCode` is derived in the same place, guarded by tests for the property that actually matters: it must increase monotonically and stay above the value already installed in the field, or Android silently refuses the update. (DR-153) ## 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 - **Video plays with sound on devices that ship a Dolby decoder.** The audio codec list sent to Jellyfin came from `MediaCodecList`, which describes ExoPlayer — but video does not play through ExoPlayer: it renders in the webview `