Add a selectable Mornye theme with shared glass controls, dialogs, sheets, notifications, and collection headers while retaining Material components.
Update player artwork, lyrics transitions and highlighting, synchronized rolling time labels, volume controls, and compact navigation. Unify metadata, Library, search, and extension surfaces; preserve restored artwork and report extension load failures.
Validation: full Flutter analyzer clean; 713 tests passed and 4 skipped; pinned Dart formatting and diff checks passed; signed ARM64 and ARM32 release APKs built and verified.
Replace the Go backend with the Rust workspace and route Android/iOS through
UniFFI bindings. Include the migrated extension runtime, network, providers,
media metadata, downloads and library operations, with version 5.0.0+147.
Remove Go sources, adapters, native build selection and CI dependencies.
Build Rust unconditionally and lock the iOS Rust pod using a relative path.
Retain legacy source and migration evidence in a local ignored archive.
Validation: Android Kotlin compile and 53 native tests; Swift Rust-branch
equivalence and syntax; CocoaPods install; workflow, shell, Ruby, plist and
diff checks. Reuse the preceding 100 Rust tests, fmt/Clippy and five-ABI
build checkpoint; no new APK or iOS application build for this cleanup.
Known follow-up: URL/URLSearchParams globals are missing from the Rust JS
runtime. A controlled extension replay confirms a URL-resolution regression;
this commit does not fix that runtime gap.
Every settings-style page carried the same 40-line SliverAppBar block;
titles, custom leadings (discard-confirm back handlers), and action
buttons are preserved through the widget's slots.
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
Add a localized description header and Material 3 styling (rounded
corners, scrollable layout, surface color) to the album folder structure
picker in Files settings.
Add a playlist position token usable in filename templates, plumbed from the playlist screen through the download queue to the Go backend. Auto-prefix playlist downloads with the position when the template lacks the token. Includes Go filename test.
- app: clear displayFeatures so bottom sheets/dialogs center on large/foldable screens
- edit metadata sheet: card sections, modern label-above inputs, elegant collapsible headers, removed title icon
- convert + batch convert: modern card-based sheets; shared BatchConvertSheet widget
- queue: keep selection toolbar hidden until modal close animation finishes
- 24-bit and In Library badges now use primary dynamic color
- artist skeleton: remove duplicate name/listeners lines, keep cover placeholder
- files settings: own filename-format controller in a StatefulWidget to fix use-after-dispose crash
The HIGH-quality lossy format picker can now produce an AAC/M4A 320 kbps output alongside MP3 and Opus. FFmpegService.convertM4aToLossy/convertAudioFormat, the Dart queue pipeline, the Kotlin finalizer, and the library database format helper all route .m4a through a unified aac codec path and tag the resulting file with the M4A metadata writer. The Lossy Format setting gains a new option, and the track metadata convert dialog lists AAC next to the other targets.
Apple Music lyrics gain a 'eLRC word sync' switch (default off). When disabled the pax-to-LRC formatter strips inline word timestamps, producing line-synced LRC that is safer for players that choke on eLRC; enabling it restores the previous word-by-word behaviour. The change propagates through SetLyricsFetchOptions and invalidates the global lyrics cache on toggle.
Broad l10n migration: roughly 400 previously hardcoded English strings across queue, settings, track metadata, repo, audio analysis, setup and extension screens now live in the ARB catalog, with matching plural/placeholder forms. No behaviour change beyond localisation. Existing and new unit tests (lyrics eLRC toggle and Dart settings round-trip) pass.
- Add deduplicateDownloads field to AppSettings (default: true)
- Add setDeduplicateDownloads() to SettingsNotifier
- Fix type mismatch in files_settings_page (Object → String cast)
- Run build_runner to regenerate settings.g.dart