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.
Use one non-generated language catalog in setup and appearance settings. Filter supported locale codes once and retain the resulting immutable picker list.
Extend ScrollEdgeFade with a horizontal axis and wrap the All/Albums/
Singles/Playlists chips so a right-edge fade signals the row scrolls
when chips overflow the screen.
Service ids ('deezer', 'tidal', 'qobuz', 'spotify', 'amazon', 'local')
were compared as raw string literals in every dispatch chain. Introduce
MusicServices constants and use them at the comparison/switch sites so
a typo becomes a compile error instead of a silently dead branch.
- Rename downloadProviderMatchesBuiltIn -> downloadProviderReplacesLegacyProvider
- Rename Tidal DASH ffmpeg helpers and lossy format pickers to generic names
- Add utils.decryptCTRSegments crypto API + raw/bytes file read path in extension runtime
- Update l10n strings/descriptions to drop hardcoded service names
- Bump version to 4.5.7+134
Introduce AppRemoteConfigService which fetches a platform/version/locale-aware JSON payload from api.zarz.moe/v1/spotiflac-mobile/config and caches it in SharedPreferences. main_shell shows a one-shot announcement dialog (respecting dismissible, CTA, time window and version gates) when no update prompt is pending; dismissed IDs are persisted so each announcement surfaces only once.
Tweaks bundled in: the service health dot loses its blur halo in favour of solid Material 3 tones, and AppInfo gains the remote config endpoint constant. The share listener and SAF migration hook stay synchronous inside the post-frame callback so share-intent URLs never race the network-bound checks.
New unit tests cover the announcement CTA/active-window rules.
- Add Duration field to AudioQuality for FLAC (streaminfo) and M4A (mvhd atom)
- Expose duration via ReadFileMetadata and extension runtime Go-backend API
- Pass duration_ms to extension CheckAvailability for better track matching
- Fix home tab showing empty state before extensions finish initializing by
keeping the search bar visible with a loading indicator until ready
- Refactor hasSearchProvider helper to account for built-in providers
- Refine homeEmptyTitle/Subtitle copy (EN + ID translations)
- Bump version to 4.5.0+127
- Bump app version to 4.3.0 (build 125)
- Extract createOrReuseDocumentFile() to handle SAF auto-rename races
between findFile() and createFile(), preferring the exact-named sibling
and discarding duplicate documents
- Add displayVersion getter using kDebugMode: debug shows 'Internal', release shows actual version
- Defer Spotify URL resolution in Deezer downloader until fallback is actually needed
- Unify download timeouts to 24h constant (connection-level timeouts still protect hung connections)
- Fix context shadowing in track metadata options menu and delete dialog
- Use addPostFrameCallback + mounted guards for safer sheet/dialog navigation
Version rolled back from v4.x to v3.7.0. After extensive work on v4's
internal streaming engine, smart queue, DASH pipeline, and media controls,
we realized v3 was already feature-complete. Adding more big features
only made maintenance increasingly difficult and the developer's life
miserable. Stripped back to what works: external player only, cleaner
codebase, sustainable long-term.
- Remove just_audio, audio_service, audio_session and entire internal
playback engine (smart queue, notification, shuffle/repeat, prefetch)
- Remove PlaybackItem model, MiniPlayerBar widget, notification drawables
- Remove playerMode setting (external-only now)
- Migrate MainActivity from AudioServiceFragmentActivity to
FlutterFragmentActivity
- Migrate Qobuz to MusicDL API
- Update changelog with v3.7.0 rollback explanation
- Add configurable YouTube Opus (96-256kbps) and MP3 (96-320kbps) bitrates
- Improve title matching with loose normalization for symbol-heavy tracks
- Add SpotubeDL engine v2 fallback for MP3 requests
- Improve filename sanitization in track metadata screen
- Bump version to 3.6.9+82