9.6 KiB
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; the generated trace matrix lives in docs/traceability.md.
v0.4.1
🐛 Fixes
- The lockscreen pause works while a video's audio plays in the background.
The handoff starts native audio and only then tears the WebView
<video>down — and that teardown fires a DOMpausethe frontend reports like any other, which left the controller believing webview media was still the player. Transport stayed aimed at it: pressing pause on the lockscreen sent a control command to a<video>that no longer existed while the native player carried on, and the element's parting position report dragged the displayed time backwards. A handoff is now tracked explicitly, so it hands transport to the native backend and ignores what the dying element still reports. A pause made from the lockscreen also survives the return to the app, instead of being undone by the play state captured when the handoff began. (UR-040, UR-005 → DR-052, DR-097)
v0.4.0
✨ Features
-
Favourites, across libraries. A
/library/favoritespage renders favourites from every library with All / Movies / Shows / Music scope tabs, reusing the standard grid so card shape still follows the media — a mixed All tab reads as posters, squares and thumbnails side by side. Home carries favourite rows below Recently Added, and a row with no items does not render at all, so a fresh install shows no empty rows. Server favourite state is mirrored into the local database as results are cached, so offline browsing sees the same favourites as the server; a toggle made offline is never overwritten by a stale server value before it has been pushed. (UR-067, UR-069 → DR-113, DR-114, DR-115, DR-117, DR-118) -
Search answers from the local index. The instant leg read only downloaded items, so with no downloads it returned nothing and every keystroke fell through to a full
Recursive=trueserver query. It now reads the whole synced catalog through the same availability CTEget_itemsuses, gated on the sameinclude_catalog_browseflag, so search and browse cannot diverge. The index also gained MusicArtist, Playlist and People — the very groups search sorts results into. Re-indexing moved from a frontend startup call to a Rust background task with a 6h TTL, so a long session no longer searches a stale catalog. (UR-065 → DR-108, DR-110, DR-111)
🐛 Fixes
-
Playback no longer restarts an episode at random on a flaky connection. Background audio-only playback of a video streams a progressive mp3 transcode over plain HTTP, which is chunked and so declares no length: when the connection dropped mid-episode, ExoPlayer saw end-of-input and reported
STATE_ENDED, indistinguishable from the real end. The app ran its end-of-episode logic mid-episode and playback parked inSTATE_ENDED, where the next play intent from the lockscreen, notification or a Bluetooth reconnect seeks an ended player to position 0 — surfacing as "the episode randomly restarted". The item's runtime is now what decides: an end reported well short of it re-opens the stream where it stopped. (UR-040 → DR-129) -
A network hiccup no longer kills playback outright. Music and video declare a length, so a cut connection reaches them as an error rather than a phantom end — and every error stopped the player. A recoverable error now gets one bounded attempt at re-opening the stream where it stopped, with a growing backoff, leaving the rest of the queue intact. On Linux, MPV additionally reconnects inside the demuxer so ordinary blips never surface at all, and
EndFile(ERROR)— previously a bare log line that left playback halted while the UI still showed "playing" — is now reported and recovered. (UR-004, UR-040 → DR-129, DR-130) -
The player no longer reads 0:00 as a track ends on Linux. MPV exposes
time-posanddurationas properties of the loaded file, so at EOF it unloads and both stop resolving — reporting zero at exactly the moment end-of-file handling asks where playback reached. The last reading seen while media was loaded is now kept and used as the fallback. (UR-005 → DR-130) -
Server-side deletions propagate to the local catalog.
DELETE FROM itemsexisted nowhere, so items removed on the server lingered locally forever. A post-crawl mark-and-sweep now removes them, scoped to crawled types, skipping downloaded items, and refusing to run after a partial crawl. Separately,items_ftsgrew a full duplicate index on every catalog pass; it is now a real upsert, with a migration rebuilding existing indexes. (DR-110) -
Android system bars and display cutout are handled correctly. (UR-066)
v0.2.0
✨ Features
-
Audio settings now work on Android. The equalizer, volume normalization and gapless playback controls in Settings › Audio previously rendered on Android and did nothing —
ExoPlayerBackendwas the only backend that never implementedset_audio_settings, and the trait's default silently reported success while applying nothing. All three now take effect:- Equalizer — the canonical 10-band ISO curve is resampled onto whatever bands the device's equalizer actually exposes (commonly 5), by nearest centre frequency.
- Volume normalization — via
LoudnessEnhancer. Note this is a gain stage, not a true EBU R128 normalizer like the Linuxdynaudnormpath, so it approximates rather than matches Linux behaviour. - Gapless playback — honours the setting via
pauseAtEndOfMediaItems(ExoPlayer is gapless by default, so this disables it when you turn it off).
The effects re-attach automatically when ExoPlayer rebuilds its audio sink on a format change, so the equalizer no longer stops applying part-way through a queue. (UR-027, UR-032, UR-033 → DR-030, DR-035, DR-036, IR-004)
⚠️ Not yet verified on a physical device.
AudioEffectavailability and band layouts vary by device and OEM ROM; where an effect is unavailable it is logged and skipped rather than crashing playback.
📋 Documentation
- Playback backend unification investigation. Six new specs in
docs/specs/ record why the playback backends cannot be unified
onto a single engine: every candidate (mpv, GStreamer, libVLC) fails the same
webview-compositing constraint, because WebKitGTK/WebView2/Android WebView each
own their compositor surface and native video cannot interleave with HTML.
Audio can unify; video cannot. Also specifies the Android native-video spike,
a Windows native audio backend, and the
libmpv2migration.
🐛 Corrected requirement statuses
These were documented as working and were not. No behaviour changed — the docs were wrong.
- Crossfade (UR-031, DR-034) was marked "Done (Linux only)". It is implemented
nowhere, and is architecturally blocked on mpv: its audio chain is
single-stream, and FFmpeg's
acrossfaderequires two inputs. Real crossfade would need two libmpv instances. - The platform parity matrix listed crossfade as a Linux/Android gap (it is neither) and omitted the equalizer (which was a genuine gap, now closed).
nativeAdapter.tscited tauri#10152 as blocking native Android video. That issue is a stale feature request; the capability shipped in September 2024. What remains unproven is SurfaceView-behind-WebView compositing, now tracked by a spec rather than asserted as an upstream blocker.
v0.1.2
✨ Features
- Search results are ordered by how well they match. A name that starts with the query now outranks one matching mid-word — typing "parks" finds "Parks and Recreation" before "Sparks of Love" — and at equal match quality a container outranks its contents, so a series lands above its own episodes. Ranking is applied to the instant cached results and to the merged cache+server list alike, so the list no longer reshuffles when server results arrive. (UR-060, DR-090)
- Separate Shows, Episodes and People result groups. The combined "TV Shows" group splits into Shows and Episodes so a show never competes with its own episodes for a slot, and a new People group means searching an actor's name reaches their bio page. Default order is Shows → Episodes → Movies → Songs → Albums → Artists → People; a group order saved before the split keeps the position it was dragged to. (UR-060, DR-091)
🐛 Bug Fixes
- The library header search bar works on every library page. It previously
searched in place and depended on
/libraryrendering results inline, so on any other/library/**route the results were fetched and never shown./searchis now the single surface that renders results, and the header bar hands its query and scope over via the URL. (UR-049, DR-063) - Video smaller than the window is scaled up to fit. Sizing only ever shrank oversized media, so a 480p source on a 1080p display played as a small picture in the middle of a black frame. The picture now fits whichever axis constrains it, in both directions, preserving aspect ratio. (UR-005)
📋 Requirements
Linux: 64-bit, GLIBC 2.29+ Android: 8.0+
v0.1.1 and earlier
Released before this file existed — see the git history and the release notes on each tag.