Commit Graph
550 Commits
Author SHA1 Message Date
zarzet eabafecaeb fix(navigation): open download queue from snackbar actions 2026-07-24 22:19:03 +07:00
zarzet 01c08322c1 fix(extensions): preserve explicit track metadata 2026-07-24 21:32:13 +07:00
zarzet 4713be02de chore(settings): graduate native download worker 2026-07-24 12:23:36 +07:00
zarzet fa432266f3 chore(settings): graduate backup and restore from beta 2026-07-24 11:40:09 +07:00
zarzet 5481f8e8cf feat(ui): smooth download progress updates 2026-07-23 13:36:22 +07:00
zarzet c9114fef01 fix(android): sanitize restored installation state 2026-07-23 10:39:31 +07:00
zarzet 480a96bd0e fix(queue): clarify and coalesce verification progress 2026-07-23 09:57:02 +07:00
zarzet f0988070bf fix(convert): simplify output names and harden writes 2026-07-22 15:56:07 +07:00
zarzet 87376ee73b fix(metadata): resolve audio format and local artwork 2026-07-22 15:55:30 +07:00
zarzet b2726db84a fix(queue): bridge completed downloads into library 2026-07-22 15:55:07 +07:00
zarzet 4d77a0b178 fix(ui): honor disabled hero animations 2026-07-22 15:54:50 +07:00
zarzet bd6e2b4472 fix(ui): restore plain loading skeleton on extension album screen
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.
2026-07-21 07:35:03 +07:00
zarzet 0961c09a6d feat(download): preserve quality variants 2026-07-16 08:45:01 +07:00
zarzet 491998e5c5 feat(convert): optionally retain original files 2026-07-16 08:27:24 +07:00
zarzet ca8a6a8849 perf(shell): gate backdrop blur by device capability 2026-07-16 08:08:49 +07:00
zarzet 79bb3214ae perf(library): streamline search and grouped counts 2026-07-15 22:16:10 +07:00
zarzet 6f566a9f03 perf(player): isolate high-frequency playback updates 2026-07-15 22:09:20 +07:00
zarzet 82ba586b72 feat(convert): support same-format lossless re-encoding 2026-07-15 22:03:32 +07:00
zarzet 6f121c7543 fix(queue): open completed batch track metadata 2026-07-15 21:45:35 +07:00
zarzet 35128a6bfc fix(reenrich): always clean temporary artifacts 2026-07-15 21:33:13 +07:00
zarzet 17dbcc1338 perf(library): lazy-load collections and dispose queries 2026-07-15 21:32:43 +07:00
zarzet 7f1c3e60db fix(player): prevent stale playback and clean SAF temps 2026-07-15 21:32:07 +07:00
zarzet ca1c95346f refactor(settings): share the provider-priority reorder page
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.)
2026-07-14 18:55:48 +07:00
zarzet 87bf33321b refactor(search): remove unreachable artist/album/playlist result path
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.
2026-07-14 18:41:13 +07:00
zarzet 11567fee7d refactor(reenrich): share the FFmpeg re-enrich apply flow
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.
2026-07-14 18:33:18 +07:00
zarzet fd423e3095 perf(player): narrow now-playing queue sheet to shuffle-mode selector
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.
2026-07-14 18:26:33 +07:00
zarzet e2a2e8a914 refactor(library): drop dead pre-pagination filter pipeline in queue tab
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.
2026-07-14 17:53:58 +07:00
zarzet 4ffd5cc701 feat(ui): shimmer skeletons for the home feed artist/album sections
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.
2026-07-14 17:01:14 +07:00
zarzet 3833a66bf6 fix(metadata): send Accept-Language on Deezer requests so names follow the app language
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
2026-07-14 15:46:33 +07:00
zarzet 9ac10dd6bb feat(player): ReplayGain/R128 volume normalization for the built-in player
Reads REPLAYGAIN_TRACK_GAIN (album gain fallback) from FLAC/MP3/M4A tags
and converts Opus R128_TRACK_GAIN/R128_ALBUM_GAIN (Q7.8, -23 LUFS) to the
same ReplayGain dB representation on the Go side, then attenuates the
internal player's volume per track. Off by default; toggle lives under
Library > Playback. Opus header output gain is left to the decoder, which
already applies it.

Closes #465
2026-07-14 15:18:42 +07:00
zarzet c6e012b23d fix(ui): fetch covers via the Go backend instead of raw HttpClient
Both call sites used a bare dart:io HttpClient with no timeout, retry, or
cache; downloadCoverToFile provides all three (plus the singleflight cover
cache) and can hang-proof the embed path.
2026-07-14 12:49:57 +07:00
zarzet 11979b12e2 perf(ui): skip the shell backdrop blur on low-end devices
The sigma-18 BackdropFilter re-filters everything scrolling underneath
every frame. The startup runtime profile now feeds a lowEndDevice
provider; those devices get an opaque bottom bar instead.
2026-07-14 12:05:56 +07:00
zarzet 5a1e898795 fix(shell): rail overflow on short viewports and tab reset on rotation
- NavigationRail scrolls inside its SafeArea when the viewport is too
  short for four labeled destinations (landscape phone with the mini
  player), instead of overflowing by its natural height
- the PageView subtree carries a GlobalKey so swapping the body
  between rail and bottom-bar layouts at the 600dp breakpoint moves
  the element instead of remounting it - rotation no longer snaps
  back to the home tab and kept-alive tab state survives
2026-07-14 09:10:17 +07:00
zarzet b3cad6e526 feat(settings): toggle to disable Hero animations
A single HeroMode above the router disables shared-element flights
app-wide (mini player expand, track metadata cover) for users who
prefer no motion or want to save the overlay rendering cost. On by
default; persisted in AppSettings.
2026-07-14 09:10:16 +07:00
zarzet 1893f3faa8 feat(tablet): clamp remaining screens - settings, queue lists, wizard
- SettingsGroup centers itself at 720dp via its incoming constraint
  (not screen width, so groups inside clamped sheets are never
  over-inset; unbounded-width contexts guarded); priority scaffold,
  bespoke hero cards, and donate page get the same inset - all 18+
  settings pages clamp from shared widgets
- queue list-mode, local/downloaded album track lists, favorite
  artists, home recent-access, and extension detail sections adopt
  wideListInset like the screens fixed earlier
- track metadata content clamped at 720dp, which also tames the
  width-derived spectrogram height
- announcement dialog capped at 480dp wide; setup wizard and
  tutorial content at 560dp with a 400dp action button
2026-07-14 09:10:16 +07:00
zarzet 8966de6d0a revert(ui): drop hero cover flights from home/search to detail screens
The list-to-header cover flights read as a gimmick and add per-frame
cost during navigation. Removed the heroTag plumbing end to end
(recents, search rows, discography cards, Album/Artist/Downloaded
headers, motion banners). Kept: the mini player expand hero, the
library-to-track-metadata hero, real loading headers, and the
loading crossfade.
2026-07-14 09:10:14 +07:00
zarzet 556399d354 feat(ui): surface the audio-analysis cache in cache management 2026-07-14 09:10:05 +07:00
zarzet f5b5af6eea feat(tablet): navigation rail, landscape now-playing, clamped track lists
- shell switches to a NavigationRail at >=600dp (same destinations,
  badges included); the bottom bar and its blur stay phone-only while
  the mini player remains anchored bottom in both modes
- now-playing gains a two-pane landscape layout (artwork left,
  metadata/controls right) built from the same widget pieces
- track lists on album/playlist/library-folder screens and search
  result sections center at 720dp via wideListInset instead of
  stretching across the full tablet width; explore carousels stay
  full-bleed intentionally
2026-07-14 09:10:00 +07:00
zarzet 8f7ede4730 feat(tablet): clamp dialogs and sheets, adaptive selection-card columns
- bottom sheets capped at 640dp via bottomSheetTheme (centered on
  wide screens, no effect on phones)
- update dialog and SAF migration dialog text capped at 480/400dp
- selection action cards and service chips grow from 2 to up to 4
  columns based on available width instead of stretching
2026-07-14 09:09:59 +07:00
zarzet a10aeb76ee fix(ui): match loading-header placeholder heights to the loaded rows
The subtitle/meta placeholders were shorter than the real artist and
meta rows, so the cover shifted ~12px during the loading-to-content
crossfade and read as a ghosted double image.
2026-07-14 09:09:58 +07:00
zarzet 2d9df7e1bc fix(ui): hero continuity for motion (Apple Music) headers
Albums/artists with a motion banner have no square cover, so the Hero
had no destination: the open flight ended nowhere and there was no
return flight. The Hero now rides the full-bleed banner, flying the
still cover image as the shuttle so the video doesn't restart mid-air.
2026-07-14 09:09:57 +07:00
zarzet 16e2046d0f fix(ui): defer loaded-screen swap until the push transition settles
With cached albums the fetch finishes mid-Hero-flight; the crossfade
then mounted a second hero with the same tag, which the flight doesn't
adopt — a static cover appeared next to the flying one. The extension
screens now keep the loading header until the route animation completes.
2026-07-14 09:09:56 +07:00
zarzet 306c34a32a style(ui): unify header meta rows on the dot-separated album style
Track metadata, playlist, and library folder headers dropped their pill
badges for the same HeaderMetaRow/HeaderMetaItem used by the album
screen.
2026-07-14 09:09:56 +07:00
zarzet 93b173cff8 feat(ui): real headers on extension loading screens so hero covers fly on open
Opening an extension album/artist showed a bare skeleton, so the cover
had no Hero destination and only flew on the way back. Loading states
now render the actual header (title + Hero cover in its final slot,
skeleton placeholders for the remaining rows) above the shimmering list.
SkeletonCrossfade mutes the outgoing child's heroes to avoid duplicate
tags if the user pops mid-fade.
2026-07-14 09:09:54 +07:00
zarzet 9a205987ee feat(metadata): album-style header with centered square cover
The full-bleed cover made the Hero flight from square list thumbnails
morph into a fullscreen rect and back. The header now blurs the cover
as a backdrop and shows a centered rounded square (album header layout),
so the flight stays square end to end.
2026-07-14 09:09:53 +07:00
zarzet a11cff8b28 fix(player): pause motion header video when its screen is hidden
The banner only reacted to app lifecycle, so the HEVC decoder and HLS
stream kept running behind covered routes and inactive tabs. It now
follows TickerMode: the Navigator already disables it for covered
routes, and the shell disables it for non-current tabs (which also
mutes stray animations on kept-alive pages).
2026-07-14 09:09:52 +07:00
zarzet 23488a9c05 feat(player): fade the sheet in place on close, letting only the cover travel
Button/back pops no longer slide the sheet down: it dissolves where it
stands while the Hero artwork flies back to the mini player. Drag
dismissals keep sliding from the finger's position since freezing a
half-dragged sheet mid-screen would look broken.
2026-07-14 09:09:51 +07:00
zarzet 8610f18ff7 fix(player): dissolve the sheet while it slides down on close
After the content fade, popping left an empty dark scaffold traveling
the full screen. The sheet now fades out over the second half of the
pop; opening and dragging keep it fully opaque.
2026-07-14 09:09:51 +07:00
zarzet 7a0be15359 fix(artist): drop blurBackground stretch mode on the header
Dormant until bouncing physics enabled real overscroll; the stretch zoom
stays, but blurring the artist image while it grows looked broken.
2026-07-14 09:09:50 +07:00
zarzet 8a332c566a fix(player): fade Now Playing content with the expand transition
The app bar and body slid up as a block while the Hero artwork flew its
own path, so the title/controls looked detached from the cover. Content
now fades in over the last 60% of the transition (and fades out first on
pop/drag), leaving only the sheet surface and the flying cover in motion.
2026-07-14 09:09:50 +07:00