The two folder-access failure messages are stored on items as fixed
English sentinels so code can match on them; translate the known
sentinels at display time (error dialog and failed-row message) via new
downloadErrorSafPermissionLost / downloadErrorFolderAccessLost keys.
Also replace em dashes in recently added comments.
A track that finished downloading dropped out of the active lead zone
and re-appeared in the history zone below every still-queued sibling,
visually jumping several positions during album downloads. The existing
completion bridge masked this for only 6 seconds.
Keep completion-bridge cells pinned in the lead zone for as long as
other downloads are still active, and hide their history rows while
pinned so tracks don't render twice. Once the queue goes idle the
bridge dismisses and items settle into their normal history position.
Queue writes are debounced by 350 ms and the only guaranteed flush ran
in the provider's onDispose, which never fires when the OS kills the
backgrounded process - the most recent queue mutations were silently
lost, so items could vanish after the app sat minimised for a while.
Expose flushQueuePersistence() on the queue notifier and call it from
the app lifecycle observer on AppLifecycleState.paused, the last
reliable callback before a potential process kill.
When a download hit a verification challenge while the app was in the
background, the browser launch was attempted immediately (blocked or
silently dropped by the OS) and the 5-minute grant timeout kept running,
so the item failed without the user ever seeing the CAPTCHA.
Wait for AppLifecycleState.resumed before opening the challenge and
before starting the grant timeout, and post a local notification asking
the user to return to the app. If the process is killed while waiting,
the persisted queue already restores the item as queued on next launch.
A persisted SAF tree grant can die silently (folder deleted or renamed,
SD card removed, app data reset, system revocation). Downloads then fail
with no recovery path beyond digging through Settings.
- Add isSafTreeAccessible bridge method: the grant must still be in the
system's persisted permission list and the tree writable
- Show a warning banner in Files & Folders when the saved SAF folder is
no longer accessible, with an inline re-select action
- In the download error dialog, replace Retry with a Re-select folder
action when the failure is a lost folder grant (Android SAF or iOS
bookmark); a successful re-pick retries the item automatically
- Share the folder-access error messages as constants so the UI can
match on them reliably
The download/library folder picker on iOS previously took the bare path
string returned by file_picker and rebuilt a URL from it to create a
security-scoped bookmark. By then the picker's access grant is gone, so
bookmark creation either failed (folder selection silently rejected) or
produced a bookmark that could not be re-opened, after which the first
download run overwrote the user's chosen folder back to app Documents.
- Add a native pickIosDirectory method channel that presents
UIDocumentPickerViewController itself and creates the bookmark inside
the delegate callback, while the security-scoped grant is active
- Use it for the download directory (settings + first-run setup) and
the local library folder pickers
- Stop overwriting the saved download directory when the bookmark fails
at queue start; fail queued items with a clear message instead
- Skip the launch-time iOS path normalizer when a bookmark exists, so
it no longer rewrites external folder paths and drops their bookmark
Fixes#454
Extract InLibraryBadge (pasted verbatim in five screens) and
playLocalIfAvailable (duplicated in three) into shared code, and delete
SearchScreen, LibraryPlaylistsScreen, and CollapsingHeader, which are
imported nowhere in lib/.
Port of the explicit-content indicator from SpotiFLAC-Web (issue #456):
- Go: add explicit to ExtTrackMetadata (parsed generically from any
extension via explicit/is_explicit/isExplicit), TrackMetadata, and
AlbumTrackMetadata; built-in Deezer maps explicit_lyrics and
explicit_content_lyrics == 1
- Dart: add explicit to the Track model with a tolerant
parseExplicitFlag helper wired into all backend track parsers
- UI: new ExplicitBadge rendered through buildQualityBadges on album,
playlist, search, and home track rows
- Persist registry URL inside store_cache.json and restore it on init so
the disk cache survives app restarts instead of being wiped every launch
- Fall back to the cached registry when the fetched body fails to parse,
matching the existing network/HTTP error fallbacks
- Detect HTML responses and surface a clear message instead of the raw
Go JSON error (invalid character '<') seen in issue #474
- Validate the registry loads before persisting a new URL, and roll the
backend back to the previous URL on failure, so a broken link never
survives a restart or Android auto-backup restore
Add a platform bridge to finish session grants on Android and iOS with
JSON success validation, let users paste callback URLs from the
clipboard, and auto-dismiss the verification help dialog after grant.
Mark multi-track queue additions as batch downloads and append
{track:02} - {title} when the configured format has no uniqueness
token, instead of relying on playlist position alone.
_importCsv guards every other post-await BuildContext use with the
State's own `mounted`/`this.context`, but one spot read the l10n
strings off the function's `context` parameter instead, requiring a
`// ignore: use_build_context_synchronously` to silence the analyzer.
Switching to `this.context` matches the surrounding guard and lets the
lint verify the usage on its own, so the suppression is no longer
needed.
Use DraggableScrollableSheet for single and batch convert flows and open
convert sheets with isScrollControlled so long option lists remain usable
on smaller screens.
Prefer the in-app browser for signed-session verification challenges,
normalize invalid saved modes to the new default, and keep the help
dialog modal until the user explicitly dismisses it.
Detect verification-required failures from the Android native worker,
cancel the worker, and route the item back through the interactive
verification flow with the correct service identifier.
Let users choose FFmpeg dithering when reducing bit depth and SoXr or
SWR resampling when changing sample rate across single-track and batch
lossless conversion flows.
Expose a root navigator for global dialogs, show a fallback help sheet
with copy and reopen actions when verification URLs cannot launch, and
schedule the same prompt after a timeout during pending grants.
Add localization keys for verification browser settings, manual
verification help dialog actions, and lossless conversion dithering and
resampler option labels.
Let users choose whether signed-session verification opens in the
external browser or in-app browser first, with automatic fallback to
the other mode when launch fails.
Key verification retry state by item and service so a completed challenge
for one provider does not block retries when another service also requires
verification for the same download.
Await backend lyrics provider sync when saving the priority page so
fetch order changes take effect immediately instead of only updating
local settings.
Capture lossless conversion labels before await boundaries in queue,
downloaded album, and track metadata flows to avoid BuildContext usage
across async gaps.
Add debug/info logging around audio focus interruptions, becoming-noisy
events, and user pause requests to make preview playback issues easier
to diagnose.
Keep header metadata and action buttons visible while track lists are
still loading, disable download-all when empty, and show consistent
placeholder artwork when cover art is missing.
Load queue local album tracks from album_key when available and fall back
to album/artist matching with empty album_artist handling. Thread album_key
through grouped local album rows for more reliable navigation.
Expose an awaitable lyrics settings sync on SettingsNotifier and call it
before metadata re-enrich, lyrics autofill, and queue/local embed flows so
the backend uses the latest provider order and fetch options.
Raise default extension health cache to 10 minutes with a 1-minute floor
and a shorter TTL for unknown status. Mirror the TTL rules in the Go
backend and stop force-refreshing health checks from the download
service picker on every open.
Pass localized lossless conversion labels through shared helpers and
replace hardcoded capped-lossless confirmation text in single-track and
batch convert flows across metadata, queue, and album screens.
Move hardcoded strings in library settings, announcement link errors,
and unknown track title/artist fallbacks to AppLocalizations. Sync
locale-dependent fallback strings from MainShell.
Add English template strings and Indonesian translations for playback,
audio conversion, library settings, metadata actions, store snackbars,
SongLink region names, and other previously hardcoded user-facing text.
Regenerate AppLocalizations from the updated ARB files.
Wire bit depth/sample rate caps and real converted-quality persistence
into the downloaded/local album batch conversion paths, matching the
queue tab behavior.
- search_screen caches PreviewPlayerController in initState to avoid
ref access during dispose
- extension_provider: convert enabledExtensions/searchProviders getters
to methods
- artist_screen: drop redundant null assertions on motion banner inputs
- Up Next sheet supports drag-to-reorder via ReorderableListView with
drag handles; MusicPlayerHandler.moveQueueItem keeps current track active
- Active lyric line centers higher in viewport (0.35) so it sits visually
centered instead of leaning low
- Audio focus rework: explicit music AudioContext, ignore duck
interruptions, drop the fragile ignore-complete timer, recover playback
on transient stop/complete during track switches
- LosslessConversionQuality model with bit depth/sample rate caps,
applied only when they reduce source quality
- FFmpegService probes sample rate and appends codec-specific args
(-ar, -sample_fmt, -bits_per_raw_sample) for FLAC/ALAC/WAV/AIFF
- Batch + single-track convert sheets expose quality cap options
- Persist real converted bit depth/sample rate to history/library DB
- track_metadata: recognize and convert to WAV/AIFF targets
- convertedAudioQualityLabel reflects actual output quality
- Consistent header design across all local screens: blurred cover,
black overlay, bottom gradient, centered square cover, adaptive title,
inline meta line, Play + Shuffle buttons
- Height normalized to 0.6x (clamp 400-580) everywhere
- PlaybackController with queue methods for albums/library/playlists
- Library tab play builds merged queue (downloaded + local together)
- Preview vs main player exclusivity
- Preview stops on bottom-nav tab switch
- Duration 0:00 fix, deleted track cleanup, Up Next sheet
- Animation utilities improvements
- DownloadWithExtensionFallback now immediately surfaces verification_required
when any provider needs verification (availability + download stages),
instead of letting later providers mask it
- classifyDownloadErrorType treats 'session is not authenticated' as
verification_required (Go + Dart side)
- parseExtensionAlbumValue.withTrackFallbacks() derives album artist,
release date, and audio traits from tracks when album-level missing
- albumAudioTraitsFromTracks detects dolby_atmos/hi_res_lossless/lossless
from per-track audio_quality/audio_modes fields
- parseBitDepthSampleRate parses '24bit/96kHz' style quality labels
Replace the pending-verification-search bookkeeping with an inline open-and-wait flow that retries the custom search once the session grant arrives (with timeout). Parse preview_url into Track, and carry headerVideoUrl through TrackState for URL-resolved tracks/albums/artists.
Add a MotionHeaderBanner (video_player) that plays a looping muted HLS header video with a static image fallback for artist, album, and playlist screens. The Go backend now exposes header_video, header_image, and audio_traits from extensions. Album/playlist headers show the release year and Dolby Atmos / Lossless badges inline, full date and song count in a footer, a centered square cover when no video is present, and a full-bleed video when one is.
Add a preview player provider and PreviewButton, a previewUrl field on Track (parsed from search and local redownload), preview buttons in search results, and stop the preview on navigation/tab change via per-tab navigator observers. Includes preview play/stop/unavailable localization strings.
Cache health results after each check, add a force flag to bypass the cache when the download picker opens or a service is selected, guard stale concurrent results via a per-extension request serial, and treat DNS lookup failures as indeterminate/transient.
Add a localized description header and Material 3 styling (rounded
corners, scrollable layout, surface color) to the album folder structure
picker in Files settings.