The Go MusicBrainz genre and album-artist ISRC lookups only ran
automatically at download time. The tag editor's auto-fill section
gains a Fetch from MusicBrainz button that bridges both exports in one
channel call (sparing the 1 req/s budget) and fills the fields as
editable suggestions; nothing is written until Save.
The internal player handles setRepeatMode: repeat-one replays the
current track, repeat-all wraps the queue (and keeps a single-track
shuffle queue alive). The mode is broadcast in playback state,
persisted with the playback session, and toggleable from the Now
Playing transport row and the up-next sheet; a small shuffle toggle
joins the transport row for symmetry.
Every single-track enqueue now shows the shared added-to-queue
snackbar with a View action into Library (six call sites through one
helper). URL fetch failures in Home and via share intent gain a Retry
action; unrecognized-URL errors deliberately keep none since retrying
is deterministic.
The download-time ISRC index now also parses mp3, m4a, ogg, and opus
tags through the existing native readers instead of skipping everything
but .flac. Library settings gain a Review duplicates sheet that groups
history and local-library rows sharing an ISRC via SQL over the
attached databases (no filesystem walk, SAF-safe), shows quality
badges, and offers keep-best and per-copy delete with confirmation.
Track options gain an Open on... entry listing every streaming platform
song.link resolves for the track, served by a new generic
GetTrackPlatformLinks with its own memory cache beside the availability
cache (same request budget). The sheet is data-driven from the platform
map so the app core stays service-agnostic. Completes the bridge
wrappers already landed in 901fa34d; also carries the en strings for
the duplicate review sheet landing next.
Merge origin/l10n_main (PR #428) with a per-key three-way resolution:
Crowdin values win only where main did not change the key since the
last sync, so the unused-key removal, store->repo wording sweep, and
ICU plural fixes survive. Korean is restored at 98% completion and
mapped again in crowdin.yml and both language pickers; ar/hi/nl/zh-CN/
zh-TW stay dropped (37% and below).
Follow-up to the arb removal: unmap ar/hi/ko/nl/zh-CN/zh-TW in
crowdin.yml so crowdin pull does not recreate them, trim the dead
entries from both language pickers, and add the missing uk entry to
the setup-screen picker.
The playlist import picker now also accepts .m3u/.m3u8; EXTINF entries
feed the same Deezer enrichment pipeline as CSV. Collection screens
(wishlist, loved, playlists) gain an export action that resolves each
track to its local file, writes SAF entries relative to the download
tree root so external players and DAP copies resolve them, skips
tracks without a file, and shares the .m3u8 via the system sheet.
Queue, current index, position, and shuffle flag are snapshotted to a
new app_state playback_session table on pause, track change, and queue
mutation (never on position ticks). On launch the session is restored
paused into the mini player; the first play seeks to the saved
position. Plain file paths that no longer exist are dropped, an
explicit stop or mini-player dismiss clears the snapshot, and the
installation-restore guard wipes it alongside the download queue.
Queued rows get a skip-next button that reinserts the item ahead of all
other queued items, so the next free slot picks it up. During an active
native worker run the current batch keeps its order; the new order
applies from the next run.
The hero-era loading machinery survived the hero-flight revert: the
loading scaffold rendered the real cover, _RouteSettled deferred the
swap until the push transition ended, and SkeletonCrossfade faded
between the two. The loaded header decodes the cover under a different
mem-cache key (viewport-sized vs full-res), so opening an album from
the artist screen flashed the cover: shown, blanked, shown again.
Restore the pre-hero flow: shimmer skeleton while loading, hard cut to
AlbumScreen so the cover appears exactly once. Removes the now-unused
_AlbumLoadingScaffold and three imports that only existed for it.
The download- and metadata-provider priority pages were 99% identical. Extract
ProviderPriorityListPage taking the provider-list load/save callbacks and
labels; both pages become thin wrappers. (The metadata page's explicit
saveLabel was the same dialogSave the scaffold already defaults to.)
TrackState.searchArtists/searchAlbums/searchPlaylists were only ever written
empty (the metadata-search parse loops iterated const [] lists and customSearch
set []), so the home feed's separate artist/album/playlist result sections
never rendered. Drop the fields, the dead parse loops and _parseSearch* helpers,
the SearchArtist/Album/Playlist models, the sort caches/methods, and the
_Search* result widgets. The live tracks->buckets sections (_CollectionItemWidget
via artistItems/albumItems/playlistItems) and the search filter tabs are kept.
local_album and the queue batch action carried byte-identical ~110-line copies
of the FFmpeg re-enrich apply+SAF-writeback+temp-cleanup flow (differing only in
helper names). Extract one applyFfmpegReEnrichResult() so the SAF temp-file
cleanup — the class of bug fixed in fb7d101 — has a single source. The
track-detail copy stays: it drives snackbars and does not preserve metadata.
The sheet watched the whole playbackStateProvider, which re-emits every 500ms
during playback, rebuilding the entire ReorderableListView twice a second just
to recompute an unchanged shuffle toggle. Watch a .select of shuffleMode only.
The DB-backed pagination migration left the old in-widget filter/unification
pipeline behind three unused_element ignores; every helper, cache field, and
isolate entry point below them was reachable only from that dead root.
While the home feed loads, show section-shaped skeletons (album cards +
artist circles) instead of the generic track-row skeleton, and shimmer
each explore cover while its image loads — matching the artist screen.
Deezer localizes artist and genre names by IP geolocation when no language
is given, so users behind e.g. Arabic-region IPs got Arabic metadata on an
English device (VPN 'fixed' it). The app's display language is now pushed
to the backend and sent as Accept-Language, with an en-US default.
Fixes#480