Release/0.43 - #431
Merged
Merged
Release/0.43#431
Conversation
Albums as the tenth media type: MusicBrainz search/browse with filters, ListenBrainz popularity and fresh releases, Cover Art Archive covers, per-collection listened-track tracker, offline export/import/backup. Rate limiting, 503 retries and cover-download hardening included. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
feat(app): add music as a media type backed by MusicBrainz
Music (MusicBrainz albums, shipped unreleased as MediaType.music) becomes the audio media type, and Podcast Index podcasts join it as a second AudioKind — the BookKind pattern. MigrationV64 drops the unreleased music tables and creates audio_cache / audio_tracks_cache / listened_tracks with the kind discriminator; podcast episodes key progress by the stable Podcast Index episode id (disc 0), since RSS season/episode numbers are unreliable. Search, trending browse with category/language filters, a trending discover row, an episode tracker with year/month spans, progress bars and whole-span toggles, statistics, export/import, backups and the selfhost proxy (server-side request signing) all cover the new type. Podcast Index key/secret: built-in pair via CI secrets, personal pair in Settings / the welcome wizard, carried by settings dumps. Also fixes the import stamping "now" into started/completed dates that were exported as null: the status restore's automatic date bump is now undone by the file's explicit nulls. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Li6WmsLcmfZqhMvF5Amvx
feat(app): add audio as a media type with music and podcasts
|
Coverage after merging release/0.43 into main will be
Coverage Report
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
On wide screens tags were a 32px right-edge rail with names rotated 90°, so the rail's height grew with name length and dozens of tags meant screens of vertical scrolling. Tags are now one chip row above the items grid: readable labels with per-collection item counts, multi-select, a reset chip, and the Group toggle. Overflow scrolls sideways through the shared ScrollableRowWithArrows (edge-fade arrows, wheel remap, drag). Narrow screens keep the filter-bar tag sheet. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
use_super_parameters: detail becomes super.detail in the AniList and Hardcover exception subclasses. unawaited_return_in_try_block: return await in downloadCollection, importFromFile and the LAN settings pull — without the await those try blocks never caught errors from the returned futures. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Feat/tags top bar
|
Coverage after merging release/0.43 into main will be
Coverage Report
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
…ted sorting Manage tags and the tag picker now share one body (TagSearchList): a search field with filter-as-you-type and quick-create, plus a sort menu (manual / A-Z / Z-A) persisted per profile that also orders the collection tag chip bar and the narrow-screen filter sheet. Picker rows carry the full edit set (colors, rename, delete, usage counts) next to an independent checkbox selection. Duplicate tag names can no longer crash: the create row hides on a case-insensitive match and GlobalTagDao.create adopts the existing row on a UNIQUE conflict. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Vz8jb5kwdf7sT2WKiYm1X4
feat(app): unify tag dialogs with search, in-place editing and persisted sorting
|
Coverage after merging release/0.43 into main will be
Coverage Report
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
… upload Settings gains a "Collection banner style" choice (Classic, Comic, Sticker album, Brutalist, Strips); every non-classic style shows a per-status summary of the collection. Picking a collection background now works on the web build: the bytes upload to the server image cache and render from its URL. The subfilter bar loses its bottom padding. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXoviavAwEqULzKsGkQ5vc
feat(app): selectable banner styles for rich collections and web hero upload
|
Coverage after merging release/0.43 into main will be
Coverage Report
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comments only — no production code changed. Verified by stripping every
comment from both revisions of all 435 touched Dart files and diffing.
- Translate the remaining Russian comments to English, or drop the ones
that only restated the declaration.
- Cut every comment block down to the 2-line cap (228 blocks in 134
files), keeping the WHY and dropping the WHAT.
- Delete file-header blocks, banner separators and boilerplate dartdoc
("Creates a [X].", "Provider for [X].").
- Clean the Cyrillic out of analysis_options.yaml, build.gradle.kts,
check.ps1 and win32_window.cpp.
Kept: verbatim API tokens the code greps for (Fantlab, Kinorium, TMDB
language names) with English prose around them.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011ALPN9hf7ALVq8Nyc9BRMU
|
Coverage after merging release/0.43 into main will be
Coverage Report
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Without it the request goes out chunked, which ListenBrainz rejects with a bare 400 — album popularity in the web build's audio search silently came back empty. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012DPWM1n8eQbkdcWUWrWXVn
…tering A seventh status for titles kept in the library but deliberately parked, so the "dropped" count stops collecting them. It carries no logic of its own: nothing transitions in or out of it, and only an authoritative downgrade (RetroAchievements) lets a tracker override it. The status dropdown above a collection and above All Items becomes multi-select: the menu stays open while ticking statuses, an item passes when it matches any of them, and the All Items choice is persisted per profile as the whole set, migrating once from the older single-value key. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012DPWM1n8eQbkdcWUWrWXVn
Feat/status ignore multi filter
|
Coverage after merging release/0.43 into main will be
Coverage Report
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
…ages In a browser nothing came back at all. The developer pair a desktop build carries as a --dart-define deliberately never ships to a tab, and no screen could put one on the server either, so the proxy answered every request with 503 "No ss_dev_id configured" while the app still believed the pair existed and offered the gallery. Settings -> API Keys now takes the devid/devpassword on web, keeps them where the proxy reads them, and the gallery and the quota button follow what the server actually holds. An entered pair outranks the built-in one, like every other key, so an imported config works the same on desktop. Media moved to the server's image cache too: the media host drops its CORS header on an error page, which blanked the whole gallery whenever ScreenScraper hiccupped, and the cache spares the account's daily quota. The cache itself no longer stores a 200 body the source did not label image/*, which otherwise served ScreenScraper's HTML outage notice as a broken cover for the year the entry is declared immutable. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsAQNHmsUNifLRJizjChCn
Replaces the old mockup set with a new one (12 shots, both themes) and adds the per-source icons the page needs as its own assets instead of linking into the app bundle. README points at the new file names. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsAQNHmsUNifLRJizjChCn
Feat/refresh mockups and site
|
Coverage after merging release/0.43 into main will be
Coverage Report
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The theme shots are retaken at 2029x1257, and the source-icon rows on the landing page gained TheTVDB, Kitsu, Google Books and Comic Vine. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Picking a new picture for a custom card — by URL or from disk — kept showing the old one. The cache file was named after the card, so the replacement landed on the name the previous cover already held, and Flutter's decoded-image cache is keyed by that same path. A picked cover now carries a token in its name, and the decoded copies of a replaced one are evicted. On web the delete goes through a new route on the server's image cache, where a stale copy would outrank the URL for every client. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Deleting a series leaves its watch marks in the database on purpose, so re-adding it restores the progress — but the statistics page counted them forever, and editing the marks of a live card was the only thing that made the numbers agree again. Every episode and track counter now ignores marks whose title is no longer in the collection, via an EXISTS predicate mirroring CollectionItem.usesEpisodeTrackerFor. Nothing is deleted, so a re-added series still comes back with its progress. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A TheTVDB series record states no episode count anywhere, so the progress badge had no denominator and seasons showed no episode totals. Both are now counted from the episode list, skipping specials; the list is fetched only when a season lacks its own count. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Three parts of the same surface, which share the files they touch: The collection banner now carries the title and a back control drawn in each style's own look, so the plain title row above a rich collection is gone and the name is no longer printed twice. The banner also stays up while items load or fail to load, so the way back never disappears. Strips reads as torn comic panels instead of an evenly sliced photo, the sticker album no longer overflows a narrow window, and the status breakdown is dots and counts rather than wrapped labels. Every card grid and its loading skeleton read one geometry helper, so placeholders honor the card-size setting and stop jumping to another size once the items arrive; search gains the landscape-phone density, the tablet breakpoint and the compact card form the collection grid already had. Three effects stop burning frames: a blurred-poster backdrop no longer follows the gyroscope, a tagged card's border highlight is static on phones, and every shimmer placeholder shares one clock instead of owning an AnimationController. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fix/custom cover refresh
|
Coverage after merging release/0.43 into main will be
Coverage Report
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Coverage after merging release/0.43 into main will be
Coverage Report
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Reject absolute and drive-qualified image ids that p.join would resolve outside the cache directory; send every forwarded body with an explicit Content-Length, including an empty one; extract the FIFO throttle from the proxy and pace coverartarchive.org fetches in the image cache. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PG1rmfsvYi61LmS9PS88bY
A 63-bit fnv1a64 id exceeds what a JS double holds exactly, so the web build rounded it and every keyed lookup missed. New fnv1a53 xor-folds the hash to 53 bits, bit-identical between the VM and dart2js; albums and the ListenBrainz feed now use it. MangaDex and Google Books keep fnv1a64 — their ids already shipped. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PG1rmfsvYi61LmS9PS88bY
The card's cover and blurred background render from the local cover cache on the first frame: the base path is memoized so a cached file resolves synchronously, and one shared decode width lets the grid, the card and the search sheet reuse a single decoded copy. On web the backdrop routes through the server image cache, a replaced hero is deleted from it, and exports skip the hero file there. "Refresh item" drops the decoded copies so the refetched cover shows without a restart. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PG1rmfsvYi61LmS9PS88bY
…en cleanups Listened counters intersect with the loaded track list, so imported marks outside the fetched window no longer inflate the header or flip a podcast to completed. Collapsed episode spans skip building their rows. In a track row the circle toggles the mark and a tap on the row unfolds an ellipsized title. The collection screen reuses one CollectionFilters instance and memoizes per-tag counts; the VGMaps panel gates on kVgMapsEnabled instead of a bare Platform check. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PG1rmfsvYi61LmS9PS88bY
Entries state what a feature or fix does — no rationale, no bug history, no comparisons with other sources; fixes get their own entry only for features shipped in a past release. Same-release tweaks fold into the feature entry. The finish skill's Phase 4 now enforces this. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PG1rmfsvYi61LmS9PS88bY
|
Coverage after merging release/0.43 into main will be
Coverage Report
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The DAO surface changed (AudioDao, listen-count stats, renamed getCollectionIdsWithStatuses), so a mixed client/server pair must fail with the explicit protocol error instead of opaque per-method failures. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XgdzMthon3rnVVYTLnjw49
|
Coverage after merging release/0.43 into main will be
Coverage Report
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
… lookup Sort keys are computed once per item instead of inside the comparator, with a deterministic id tie-breaker for duplicate names. The All Items card watches selection through bool-valued selects so a toggle rebuilds one card instead of the screen; the bulk bar owns its own selection watch. Platform chips load via one getPlatformsByIds call instead of a per-id loop. Grid cards are wrapped in RepaintBoundary. A new core test checks every model constructor parameter against its toDb keys. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012T8oQLW3qG2Vbw7WNg9U11
perf(app): keyed sorting, scoped selection rebuilds, batched platform…
|
Coverage after merging release/0.43 into main will be
Coverage Report
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
added 2 commits
August 20, 2026 19:27
The key was declared twice in every locale but pt; the later "Select tags" entry won, so the earlier one was dead weight that silently swallowed edits. Removing it moves the generated getter next to its tagPicker siblings and leaves the resolved value unchanged.
A release branch matched both the push and the pull_request trigger, so each push to it queued a push run and a synchronize run of its own PR to main — two Analyze and two Test jobs for the same commit. Push now watches main only; a release branch is covered by its PR, which gates the merge anyway.
|
Coverage after merging release/0.43 into main will be
Coverage Report
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Coverage after merging release/0.43 into main will be
Coverage Report
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
feat(app): add music as a media type backed by MusicBrainz
feat(app): replace the vertical tag sidebar with a horizontal chip bar
feat(app): unify tag dialogs with search, in-place editing and persist