New Features
- ui: Smooth download progress updates
- download: Preserve quality variants
- convert: Optionally retain original files
- convert: Support same-format lossless re-encoding
- ui: Shimmer skeletons for the home feed artist/album sections
- player: ReplayGain/R128 volume normalization for the built-in player
- net: DoH fallback, uTLS pool flush on cleanup, retry hardening
- ui: Crossfade the app content on orientation and rail-breakpoint changes
- settings: Toggle to disable Hero animations
- tablet: Clamp remaining screens - settings, queue lists, wizard
- player: Swipe the mini player away to stop playback
- ui: Surface the audio-analysis cache in cache management
- tablet: Navigation rail, landscape now-playing, clamped track lists
- tablet: Clamp dialogs and sheets, adaptive selection-card columns
- ui: Real headers on extension loading screens so hero covers fly on open
- metadata: Album-style header with centered square cover
- player: Fade the sheet in place on close, letting only the cover travel
- ui: Crossfade skeleton into content on extension detail screens
- ui: Shared-element hero covers from search and recents to detail screens
- player: Expand transition from mini player with drag-to-dismiss
- ui: Fluid bouncing scroll physics on capable devices
- android: Request highest display refresh rate at current resolution
- download: Resume interrupted downloads via Range/If-Range
- update: Force update when 3+ stable releases behind
- artist: Shimmer skeleton for album and popular-track cover placeholders
- ui: Add shared widgets, utils, and models for deduplicated screens
- metadata: Route MP3, Ogg, and M4A edits to the native writers
- metadata: Add native Ogg/Opus vorbis comment editor
- metadata: Add native M4A ilst tag editor
- metadata: Add native MP3 ID3v2 tag editor
- ios: Run verification and OAuth through ASWebAuthenticationSession
- downloads: Support up to 3 concurrent downloads
- downloads: Detect lost SAF grant and offer folder re-selection
- metadata: Surface explicit content flag with [E] badge
- extensions: Complete signed-session grants from help dialog
Bug Fixes
- navigation: Open download queue from snackbar actions
- extensions: Preserve explicit track metadata
- lyrics: Reject mismatched search results
- native-worker: Harden background queue handling
- analysis: Render full-track spectrograms
- library: Persist records before download completion
- library: Preserve downloaded file records
- notifications: Restore verification alert sound
- android: Sanitize restored installation state
- queue: Clarify and coalesce verification progress
- download: Accelerate verified extension resumes
- download: Persist and reconcile history reliably
- convert: Simplify output names and harden writes
- metadata: Resolve audio format and local artwork
- queue: Bridge completed downloads into library
- ui: Honor disabled hero animations
- ui: Restore plain loading skeleton on extension album screen
- logging: Keep cursors valid after rollover
- extensions: Bound file reads
- extensions: Bound cookie runtime state
- download: Name variants by measured quality
- queue: Open completed batch track metadata
- reenrich: Always clean temporary artifacts
- player: Prevent stale playback and clean SAF temps
- extensions: Harden package and runtime lifecycle
- extensions: Surface swallowed signed-session verification in provider fallback
- metadata: Send Accept-Language on Deezer requests so names follow the app language
- download: Skip AC-4 repair for non-MP4 decrypt output
- ui: Fetch covers via the Go backend instead of raw HttpClient
- go: Build with Go 1.26.5 for aligned cgo frames, safe on arm32
- shell: Rail overflow on short viewports and tab reset on rotation
- player: Don't auto-resume after interruption while backgrounded
- reenrich: Keep release identity when the matched track is from another album
- extensions: Remove extension data dir on uninstall
- ui: Match loading-header placeholder heights to the loaded rows
- ui: Hero continuity for motion (Apple Music) headers
- ui: Defer loaded-screen swap until the push transition settles
- playlists: Keep imported playlist track order (#482)
- player: Pause motion header video when its screen is hidden
- player: Dissolve the sheet while it slides down on close
- artist: Drop blurBackground stretch mode on the header
- player: Fade Now Playing content with the expand transition
- network: Harden audit findings in transport, uTLS pool, and cover cache
- queue: Restore pre-refactor guarantees found by commit audit
- metadata: Shift stco/co64 chunk offsets when the ilst resizes
- finalizer: Stage conversion outputs and sync SAF streams
- download: Fsync staged downloads before the promote rename
- metadata: Write FLAC, M4A, and AC-4 tags atomically with fsync
- metadata: Stop the endless FLAC-write failures on misnamed files
- queue: Keep create-playlist label at natural size
- queue: Prevent header row overflow on narrow screens
- android: Stop double-destroying the shared audio_service engine
- renovate: Target main instead of nonexistent dev branch
- native-worker: Keep batches and retries intact across seam races
- downloads: Stage direct-mode outputs and serialize same-path writes
- saf: Stage all SAF downloads, serialize same-name writes, safe replace
- native-worker: Recover from dead runs and make queue replacement safe
- native-worker: Never delete a published download over bookkeeping failures
- extensions: Serialize storage/credential/salt file writes across runtimes
- queue: Keep just-completed tracks pinned while the batch is running
- downloads: Flush queue persistence when app is backgrounded
- downloads: Defer verification challenge until app is foregrounded
- extensions: Expire stale pending auth challenges
- metadata: Close FLAC file handles after parsing
- ios: Create security-scoped bookmark inside native folder picker session
- store: Harden extension repo registry handling
- download: Suffix batch filenames with track number when needed
- ui: Use State's context instead of stale parameter in CSV import (#460) (#460) by @A831ARD0
Performance
- go: Add runtime metrics and benchmarks
- dedupe: Batch isrc lookups under one lock
- library: Scan flac and m4a in one pass
- extensions: Invoke goja providers directly
- memory: Drop disposable caches on pressure
- cache: Defer cover cache maintenance
- queue: Persist and index incremental updates
- playback: Batch local playlist resolution
- shell: Gate backdrop blur by device capability
- startup: Bootstrap persisted app state
- library: Trim startup lookup index
- library: Streamline search and grouped counts
- player: Isolate high-frequency playback updates
- library: Lazy-load collections and dispose queries
- player: Narrow now-playing queue sheet to shuffle-mode selector
- embed: Native-first tag writes and overlapped fetches in finalization
- ui: Skip the shell backdrop blur on low-end devices
- db: Enable incremental auto-vacuum so cleared data shrinks the file
- android: Sweep stale cache files, relax scan poll, debounce worker snapshots
- disk: Byte-cap the cover disk cache with an init sweep
- net: Cache update-check and remote-config fetches with TTL and ETag
- net: Fetch embed covers once per release instead of per track
- analysis: Stream PCM from disk instead of buffering the whole track
- images: Decode completion-bridge cover precache at cell size
- scan: Stream SAF library scan results to a spill file
- memory: Release Go heap and decoded image caches on OS memory pressure
- lyrics: Bound the in-memory lyrics cache
- extensions: Reuse isolated goja download runtimes via per-extension pool
- ogg: Stream tag rewrites instead of buffering every page
- m4a: Stream tag rewrites instead of buffering the whole file
- Hoist per-call regex compiles to package level
- network: Enable gzip on the metadata transport
- tls: Enable session resumption on the stdlib transports
- network: Mobile stability and data-usage fixes from network audit
- native-worker: Stop shipping the full item payload on every poll
- dedupe: Make the ISRC index incremental, cover-free, and batch-stable
Refactoring
- library-scan: Reuse applyAudioMetadataToScan for M4A/MP3/APE
- settings: Share the provider-priority reorder page
- providers: Remove dead lookup/history APIs and dedup provider resolution
- search: Remove unreachable artist/album/playlist result path
- reenrich: Share the FFmpeg re-enrich apply flow
- backend: Drop dead exports/availability path and dedup Go tag writers
- library: Drop dead pre-pagination filter pipeline in queue tab
- Rename extension store bridge ABI to repo across all layers
- dart: Rename store provider and screen folder to repo
- go: Rename extension store files and internals to repo
- queue: Split download_queue_provider into concern part files
- queue: Extract shared payload, history, and polling helpers
- Unify extension call skeleton and download finalization paths
- screens: Migrate remaining screens to shared header and selection components
- screens: Share album-screen scaffolding and track flows
- ui: Consolidate duplicated sheets, rows, and cover widgets
- Extract shared helpers for repeated low-level patterns
- Remove dead code across Dart and Go layers
- playlist: Parse tracks via Track.fromBackendMap
- screens: Migrate playlist and library-folder headers to AlbumDetailHeader
- screens: Unify album header, selection UI, and batch engine
- models: Add Track.fromBackendMap and copyWith, dedupe track parsing
- settings: Replace 18 inline collapsing headers with SettingsSliverAppBar
- android: Consolidate SAF and filename helpers in SafDownloadHandler
- backend: Dedupe response builders and MusicBrainz fetch
- lyrics: Classify provider errors by type, not message text
- Dispatch on MusicServices constants instead of raw service ids
- metadata: Split _TrackMetadataScreenState into part files
- queue: Split _QueueTabState method clusters into part files
- ui: Share legacy provider-id resource helpers
- ui: Merge duplicated album/playlist track rows into TrackListTile
- providers: Split download history out of download_queue_provider
- backend: Split extension_providers.go into themed files
- backend: Split exports.go into themed files
- ui: Extract shared widgets, drop dead screens
Documentation
- contributing: Fix branch refs and build steps, add PR template
- Align contributing guide with actual branch and Riverpod pattern (#461) (#461) by @A831ARD0
Styling
- ui: Unify header meta rows on the dot-separated album style
Testing
- native-worker: Add Android regression gates
- backend: Use a valid descriptor in fd coverage
- extensions: Skip POSIX permission assertion on Windows
- download: Cover playlist_position in payload serialization test
- extensions: Add coverage for signed session HMAC auth flow (#462) (#462) by @A831ARD0
Dependencies
- deps: Update Flutter, Go, and native dependencies
- deps: Upgrade Gradle to 9.6.1, Kotlin to 2.4.0, lifecycle to 2.11.0
- deps: Update Go modules
- deps: Upgrade Flutter packages
Chores
- settings: Graduate native download worker
- settings: Graduate backup and restore from beta
- release: Bump version to 4.8.0
- ci: Drop the amd64 gomobile slice abiFilters already strips
- Track latest stable Flutter in all workflows
- Cache pub deps, filter jobs by path, and speed up release builds
- Bump version to 4.8.0-beta.1; fix ICU plural syntax in ar/fr
- Bump version to 4.8.0-beta.1
- backend: Align merged test file and fd stubs with lint conventions
- Enforce gofmt on the Go backend
- backend: Modernize interface{} to any, silence lint noise
- i18n: Localize folder-access download errors, drop em dashes
- Run flutter analyze/test and go vet/test on pull requests
- Update AltStore source to v4.7.1 by @github-actions[bot]
Revert
- ui: Drop hero cover flights from home/search to detail screens
New Contributors
- @rrbharath made their first contribution in #380
- @A831ARD0 made their first contribution in #461
- @HrBDev made their first contribution in #467
Full Changelog: v4.7.1...v4.8.0
Downloads
Android
- arm64:
SpotiFLAC-v4.8.0-arm64.apk(recommended for modern devices) - arm32:
SpotiFLAC-v4.8.0-arm32.apk(older devices)
iOS
- iOS:
SpotiFLAC-v4.8.0-ios-unsigned.ipa(sideload required)
Installation
Android: Enable "Install from unknown sources" and install the APK
iOS: Use AltStore, Sideloadly, or similar tools to sideload the IPA