mirror of
https://github.com/spotiflacapp/SpotiFLAC-Mobile.git
synced 2026-06-01 03:15:17 +07:00
35 KiB
35 KiB
Changelog
[3.5.1] - 2026-02-08
Performance
- Removed PaletteService (palette_generator) from all screens for faster navigation and reduced memory usage
- Album, Playlist, Downloaded Album, Local Album, and Track Metadata screens now use blurred cover art as header background instead of dominant color extraction
- Removed
palette_generatordependency - App startup now renders immediately (
runApp) while service initialization runs asynchronously in eager init - Main shell provider subscriptions now use field-level
select(...)to reduce unnecessary rebuilds - Settings persistence now uses single-flight + queued save coalescing to avoid redundant disk writes
- Progress polling cadence adjusted to 800ms for download queue, local library scan progress, and Go log polling
- Android foreground download service progress updates are throttled (change-based updates + 5s heartbeat)
- SAF history repair is now batched (
20items per batch) and capped per launch (60) to reduce startup I/O spikes - Incremental library scan now builds final item list in-memory instead of reloading from database
- Local cover images in queue/library use direct
Image.filewitherrorBuilderinstead ofFutureBuilderexistence check - CSV parser
_parseLinerewritten: correct escaped-quote handling, no quote characters in output - Removed unused legacy screen files (
home_screen.dart,queue_screen.dart,settings_screen.dart,settings_tab.dart) - Incremental local library scan now merges delta results in-memory and sorts once, avoiding full-state reload churn
- Queue local cover rendering now uses direct
Image.file+errorBuilder(removed repeated async file-exists checks)
Added
- Auto-cleanup orphaned downloads on history load (files that no longer exist are automatically removed from history)
Changed
- Removed legacy screen files that were no longer used after the tab/part refactor:
lib/screens/home_screen.dartlib/screens/queue_screen.dartlib/screens/settings_screen.dartlib/screens/settings_tab.dart
Fixed
- CSV parser now correctly handles escaped quotes (
"") inside quoted fields during import
[3.5.0] - 2026-02-07
Highlights
- SAF Storage (Android 10+): Proper Storage Access Framework support for download destination (content URIs)
- Select download folder via SAF tree picker
- Downloads now write to SAF file descriptors (
/proc/self/fd/*) instead of raw filesystem paths - Works around Android 10+ scoped storage permission errors
- Modern Onboarding Experience: Completely redesigned Setup and Tutorial screens
Added
- Home feed disk caching via SharedPreferences for instant restore on app startup
- SAF display path resolver in native Android layer (converts tree URIs to readable paths)
- New settings fields for storage mode + SAF tree URI
- SAF platform bridge methods: pick tree, stat/exists/delete, open content URI, copy to temp, write back to SAF
- SAF library scan mode (DocumentFile traversal + metadata read)
- Incremental library scanning for filesystem and SAF paths (only scans new/modified files and detects removed files)
- Force Full Scan action in Library Settings to rescan all files on demand
- Downloaded files are now excluded from Local Library scan results to prevent duplicate entries
- Legacy library rows now support
file_mod_timebackfill before incremental scans (faster follow-up scans after upgrade) - Library UI toggle to show SAF-repaired history items
- Scan cancelled banner + retry action for library scans
- Android DocumentFile dependency for SAF operations
- Post-processing API v2 (SAF-aware, ready to replace v1)
- Donate page in Settings with Ko-fi and Buy Me a Coffee links
- Per-App Language support on Android 13+ (locale_config.xml)
- Interactive tutorial with working search bar simulation and clickable download buttons
- Tutorial completion state is persisted after onboarding
- Visual feedback animations for page transitions, entrance effects, and feature lists
- New dedicated welcome step in setup wizard with improved branding
Changed
- Download pipeline supports
output_path+output_extfor Go backend - Tidal/Qobuz/Amazon/Extension downloads use SAF-aware output when enabled
- Post-processing hooks run for SAF content URIs (via temp file bridge)
- File operations in Library/Queue/Track screens now SAF-aware (
open,exists,delete,stat) - Local Library scan defaults to incremental mode; full rescan is available via Force Full Scan
- Local library database upgraded to schema v3 with
file_mod_timetracking for incremental scan cache - Platform channels expanded with incremental scan APIs (
scanLibraryFolderIncremental) on Android and iOS - Android platform channel adds
getSafFileModTimesfor SAF legacy cache backfill - Android build tooling upgraded to Gradle 9.3.1 (wrapper)
- Android build path validated with Java 25 (Gradle/Kotlin/assemble debug)
- SAF tree picker flow in
MainActivitymigrated to Activity Result API (registerForActivityResult) MainActivityhost migrated toFlutterFragmentActivityfor SAF picker compatibility- Legacy
startActivityForResult/onActivityResultSAF picker path removed - Setup screen UI polish: smaller logo, thin outline borders on text fields
- Removed support section from About page (moved to Donate page)
- Qobuz squid.wtf region fallback for blocked regions
- Setup screen converted to PageView flow with animated progress bar and modern card layouts
- Tutorial screen aligned with Setup Screen design, updated typography and softened UI shapes
- Larger, more accessible navigation buttons for onboarding flow
- Reduced visual noise by removing unnecessary glow effects
Fixed
- Android 10+
permission deniedwhen writing to/storage/emulated/0(now handled via SAF) - SAF history repair: auto-resolve missing content URIs using tree + filename
- SAF download fallback: retry in app-private storage when SAF write fails
- Tidal DASH manifest writing when output path is a file descriptor (no invalid
.m4apath) - External LRC output in SAF mode
- Restored old-device renderer fallback while using
FlutterFragmentActivityby injecting shell args from a customFlutterFragment(--enable-impeller=falseon problematic devices) - Preserved Flutter fragment creation behavior (cached engine, engine group, new engine) while adding Impeller fallback support
- SAF tree picker result now consistently returns
tree_uripayload with persisted URI permission handling - SAF share file now copies to temp before sharing (fixes share from SAF content URI)
- Home feed not updating after installing extension with homeFeed capability (no longer requires app restart)
- Library scan hero card showing 0 tracks during scan (now shows scanned file count in real-time)
- Library folder picker no longer requires MANAGE_EXTERNAL_STORAGE on Android 10+ (uses SAF tree picker)
- One-time SAF migration prompt for users updating from pre-SAF versions
- Fixed
fileModTimepropagation across Go/Android/Dart so incremental scan cache is stored and reused correctly - Fixed SAF incremental scan key mismatch (
lastModifiedvsfileModTime) and normalized result fields (skippedCount,totalFiles) - Fixed incremental scan progress when all files are skipped (
scanned_filesnow reaches total files) - Removed duplicate
"removeExtension"branch in Android method channel handler (eliminates Kotlin duplicate-branch warning)
[3.4.2] - 2026-02-04
Improved
- Mobile Network Reliability: All providers (Qobuz, Tidal, Amazon, Deezer) now have retry logic with exponential backoff
- Increased API timeouts: 15s → 25s (Deezer, Qobuz, Tidal), 30s (Amazon)
- Up to 3 retry attempts per API call (500ms → 1s → 2s backoff)
- Retryable: timeout, connection reset/refused, EOF, HTTP 5xx, HTTP 429
- SongLink ID Extraction: Extract QobuzID/TidalID directly from SongLink URLs
- New fields in
TrackAvailability:QobuzID,TidalID - Qobuz/Tidal now use direct Track ID from SongLink instead of re-parsing URLs
- New fields in
- Qobuz Download Flow: New Strategy 3 - get QobuzID from SongLink before ISRC search
- Cache hit now uses
GetTrackByID()directly instead of searching again - Pre-warm cache tries SongLink first before direct ISRC search
- Cache hit now uses
- Tidal Download Flow: Use
availability.TidalIDdirectly from SongLink struct
[3.4.1] - 2026-02-04
Fixed
- Metadata Priority order now persists after app restart
- Download Provider Priority order now persists after app restart
[3.4.0] - 2026-02-03
Highlights
- Local Library Scanning (#117): Scan existing music collection to detect duplicates (FLAC, M4A, MP3, Opus, OGG)
- Duplicate Detection (#117): "In Library" badge on tracks matching by ISRC or track name + artist
- Unified Library Tab: History renamed to Library, shows Downloaded + Local Library tracks with source badges
Added
- Local Album Screen with cover art, disc grouping, and selection mode
- Albums tab shows local library albums with folder icon badge
- Singles filter includes local library singles
- Advanced library filters: Source, Quality, Format, Date
- Cover art extraction from embedded tags (FLAC, MP3, Opus/Ogg)
- "Already in Library" notification when downloading existing tracks
- Spotify secrets now stored in secure storage (
flutter_secure_storage) - Multi-Service Link Support: Share links from Deezer, Tidal, and YouTube Music (in addition to Spotify)
- Deezer: Full support for track, album, playlist, artist links
- Tidal: Track links converted via SongLink to Spotify/Deezer for metadata
- YouTube Music: Handled via ytmusic extension URL handler
- Local library tracks now open metadata screen on tap
Changed
- Extension HTTP sandbox enforces HTTPS and blocks private IPs
- Extension file sandbox validates paths with boundary-safe checks
Fixed
- Search filter bar now only appears after results load, not during loading
- MP3/Ogg metadata parsing (ID3v2 extended headers, Ogg packet reassembly)
- Library scan metadata (ISRC, disc number, release date)
- Cover cache robustness (size + mtime cache key)
- Local library selection and delete in list/grid views
- Albums/Singles count includes local library items
[3.3.6] - 2026-02-02
Added
- WiFi-Only Download Mode: Pause downloads on mobile data, auto-resume on WiFi (Settings > Download > Download Network)
- Added
connectivity_plus: ^6.0.3dependency
[3.3.5] - 2026-02-01
Same as 3.3.1 but fixes crash issues caused by FFmpeg.
Added
- Export Failed Downloads: Export failed downloads to TXT file for easy lookup on other platforms
- Auto-Export Setting: Option to automatically export failed downloads when queue finishes
Fixed
- FFmpeg Crash: Fixed crash issues during M4A to MP3/Opus conversion
- Service Selection Ignored: Fixed bug where selecting Qobuz/Amazon from service picker was ignored and always used Tidal instead
- iOS iCloud Drive Permission Error: Block iCloud Drive folder selection on iOS (Go backend cannot access iCloud due to sandboxing)
Changed
- Amazon Fallback Only: Amazon Music is now grayed out in service picker and can only be used as fallback provider
[3.3.1] - 2026-02-01
Added
- Clear All Queue Button: Cancel all queued downloads with one tap (#96)
- IDHS Fallback: Fallback link resolver when SongLink fails (rate limited 8 req/min)
- Lossy Bitrate Options: MP3 (320/256/192/128kbps), Opus (128/96/64kbps)
- Search Filters: Filter results by type (Tracks, Artists, Albums, Playlists)
- Album/Playlist Search: Deezer search now includes albums and playlists
- New Languages: Turkish (Kaan, BedirhanGltkn), Japanese (Re*Index.(ot_inc))
- Optional All Files Access: Android 13+ no longer requires full storage access; enable in Settings if needed
- Improved VPN Compatibility: Better HTTP/2 support for users behind VPN or restricted networks
Changed
- Amazon Download API: Switched to AfkarXYZ API
- Qobuz Download API: Added Jumo API as fallback
- Search Results: Reduced artist limit from 5 to 2
Fixed
- MP3 Download Error 403: Fixed 403 Forbidden error when downloading MP3 files (#108)
- Opus Cover Art: Implemented METADATA_BLOCK_PICTURE for proper cover embedding
- Deezer Pagination: Fixed >25 tracks only showing first 25 (#112)
- Duplicate Embed Lyrics Setting: Removed from Options page (#110)
[3.2.1] - 2026-01-22
Added
- Artist/Album + Singles Folder Structure: Singles go inside artist folder (
Artist/Album/,Artist/Singles/) - Embed Lyrics Button: Manually embed online lyrics into tracks from Track Info screen (preserves synced timestamps)
- Pause/Resume Button: Added pause and resume controls next to "Downloading" header in History screen
- Instrumental Detection: Tracks marked as instrumental on lrclib.net now show "Instrumental track" instead of "Lyrics not available"
Fixed
- Lyrics: Multi-artist tracks now search by primary artist first, then full string
- Lyrics: Metadata tags (
[ti:...],[ar:...],[by:...]) no longer shown in display - Lyrics: Embed button now correctly appears for tracks with online lyrics
- Lyrics: Manual embed preserves original timestamps instead of plain text
- iOS: Fixed "File not found" after 3.1.x → 3.2.0 update (container UUID migration)
- Home Feed: Greeting now uses device local time
- Deezer: Track position fallback to index+1 when API returns 0
- Localization: Fixed 16 ICU plural syntax warnings in Spanish & Portuguese
Performance
- Home Feed: Precomputed Quick Picks section flag and reduced per-page allocations; explore state now watched by field to cut rebuilds
- Home Recent: Cached recent-access aggregation and limited list allocations for recent downloads
- Settings/Theme/Recent: Cached SharedPreferences instance to avoid repeated
getInstance()calls - History/DB: Batched iOS path migration updates to reduce write overhead
- Download Queue: Reduced polling allocations and avoided double-load scheduling for history
- Misc: Precompiled regex in share intent, update dialog, extensions error parsing, log analysis, and LRC cleanup; faster palette cache hits and log filtering
[3.2.0] - 2026-01-22
Note: Starting from v3.2.0, changelogs will be concise.
Highlights
- Discography Download (Highly Requested): Download entire artist discography with album selection mode
- Home Feed / Explore: Personalized sections from spotify-web and ytmusic extensions
- SQLite History Database: O(1) lookups, non-blocking writes
Added
- Discography download with options: All, Albums Only, Singles Only, or Select Albums
- Artist navigation from album screen (tap artist name)
- Home feed sections with pull-to-refresh
- YT Music Quick Picks swipeable UI
gobackend.getLocalTime()API for extensions- Track duration in home feed items
- Release date badge in album info card
Improved
- Album track list shows track number instead of cover image
- Download buttons with more rounded corners
- Downloaded songs in Recent show primary-colored subtitle
Fixed
- Home feed timezone detection
- Track duration 0:00 when downloading from home feed
Extensions
- spotify-web v1.8.1: Home feed, artist_id support
- ytmusic v1.6.1: Home feed, artist_id support
[3.1.3] - 2026-01-19
Added
- External LRC Lyrics File Support: Option to save lyrics as separate .lrc files for compatibility with external music players
- New "Lyrics Mode" setting in Settings > Download > Lyrics section
- Three modes available:
- Embed in file (default): Lyrics stored inside FLAC metadata
- External .lrc file: Save lyrics as separate .lrc file next to audio file
- Both: Embed and save external .lrc file
- Perfect for players like Samsung Music that prefer external .lrc files
- LRC files include metadata headers (title, artist, by:SpotiFLAC-Mobile)
- Works with all download services (Tidal, Qobuz, Amazon)
- CSV Import Quality Selection: Choose audio quality when importing CSV playlists
- Quality picker now appears before adding CSV tracks to download queue
- Select between FLAC qualities (Lossless, Hi-Res, Hi-Res Max) or MP3
- Respects "Ask quality before download" setting - uses default quality if disabled
- Persistent Cover Image Cache: Album/track cover images now cached to persistent storage instead of temporary directory
- Cover images no longer disappear when app is closed or device restarts
- Cache stored in
app_flutter/cover_cache/directory (not cleared by system) - Maximum 1000 images cached for up to 365 days
- Covers are cached when displayed in History, Home, Album, Artist, or any other screen
- New
CoverCacheManagerservice withclearCache()andgetStats()methods for future cache management
- Extended Metadata from Deezer Enrichment: Track downloads now include label, copyright, and genre metadata from Deezer
- New fields in
ExtTrackMetadata:label,copyright,genre - Metadata fetched during
enrichTrack()via Deezer album API - Embedded as FLAC Vorbis comments:
GENRE,ORGANIZATION(label),COPYRIGHT - Works for both extension downloads and built-in provider downloads (Tidal, Qobuz, Amazon)
- New fields in
- Track Metadata Screen Extended Info: Genre, label, and copyright now displayed in track metadata screen
- Added
genre,label,copyrightfields toDownloadHistoryItemmodel - Metadata is stored in download history and persists across app restarts
- New localization strings:
trackGenre,trackLabel,trackCopyright
- Added
**utils.randomUserAgent()for Extensions**: New utility function for extensions to get random browser User-Agent strings- Returns modern Chrome User-Agent format:
Chrome/{120-145}.0.{6000-7499}.{100-299}withWindows NT 10.0 - Useful for extensions that need to rotate User-Agents to avoid detection
- Returns modern Chrome User-Agent format:
Fixed
- Portuguese Language Bug: Fixed locale parsing for languages with country codes (e.g., pt_PT, es_ES)
- App now correctly loads Portuguese and Spanish translations
- Updated Portuguese label to "Português (Brasil)"
- VM Race Condition Panic: Fixed
panic during execution: runtime error: index out of range [-2]crash when switching search providers- Root cause: Goja VM was being accessed concurrently by multiple goroutines without synchronization
- Added
VMMu sync.MutextoLoadedExtensionstruct - Added mutex lock/unlock to ALL
ExtensionProviderWrappermethods:SearchTracks,GetTrack,GetAlbum,GetArtistEnrichTrack,CheckAvailability,GetDownloadURL,DownloadCustomSearch,HandleURL,MatchTrack,PostProcess
- Prevents race conditions when rapidly switching between extension search providers
- Tidal Release Date Fallback: Fixed missing release date in FLAC metadata when downloading from Tidal
- Now uses Tidal API's release date when
req.ReleaseDateis empty - Ensures release date is always embedded in downloaded files
- Now uses Tidal API's release date when
- Extended Metadata for M4A→FLAC Conversion: Fixed genre, label, and copyright not being embedded when converting Amazon M4A to FLAC
- Flutter now extracts extended metadata from Go backend response
- Passes
genre,label,copyrightparameters to_embedMetadataAndCover() - Tags correctly embedded during FFmpeg conversion
- Extended Metadata for MP3 Conversion: Genre, label, and copyright now embedded in MP3 files when converting from FLAC
- Added
genre,label,copyrightparameters to_embedMetadataToMp3() - Tags embedded as ID3v2:
GENRE,ORGANIZATION(label),COPYRIGHT
- Added
Extensions
- spotify-web Extension: Updated to v1.7.0
- Added
getMetadataFromDeezer()function to fetch extended metadata:- ISRC from track
- Label from album
- Copyright (generated as "YEAR LABEL")
- Genre from album genres
- Release date
enrichTrack()now returns all extended metadata to Go backend- Replaced all hardcoded User-Agent strings with
utils.randomUserAgent()
- Added
Performance
- Faster App Startup: Notification, Share Intent, and Cover Cache Manager initialization now run in parallel
- Download Queue Polling: Batched progress updates reduce rebuilds and list allocations during active downloads
- Queue Item Updates: Status/progress updates now skip no-op changes and update by index for fewer allocations
- Directory Creation: Download output folders are created once per path, reducing repeated I/O for albums/singles
- Search Results Rendering: Single-pass filtering avoids repeated
indexOfcalls for large result sets - Queue Lookups in UI: O(1) lookup for queue status in Home/Album/Playlist/Artist track lists
- History Filtering: Album/single counts and grouping are computed once per build
- Downloaded Album View: Tracks are grouped by disc in one pass to reduce filtering overhead
- Track Metadata Screen:
- Palette extraction deferred until after transition; reduced sample size for smoother navigation
- File stat uses a single syscall and only triggers state updates on change
- Static regex/month table avoids repeated allocations
- Cover precached before opening metadata from history/queue/recents
- Flutter Provider Optimizations:
- Cache
SharedPreferencesinstance inDownloadHistoryNotifierandDownloadQueueNotifierto avoid repeatedgetInstance()calls - Precompile regex for folder name sanitization and year extraction (top-level
final) - Use
indexWhereinstead offirstWherewith placeholder object to reduce allocations in queue processing
- Cache
- Flutter UI Optimizations:
- Selective
ref.watch()fordownloadQueueProvider(watch onlyqueuedCountoritemsinstead of entire state) - Pass
Trackdirectly to_buildTrackTile()instead of index lookup inside builder - Pass
historyItemsas parameter to_buildRecentAccess()to avoidref.read()inside method
- Selective
- M4A Metadata Embedding: Streaming implementation reduces memory usage for large files
- Uses
os.Open()+ReadAtinstead ofos.ReadFile()(no full file load into memory) - Atomic file replacement via temp file + rename for safer writes
- New helper functions:
findAtomInRange(),readAtomHeaderAt(),copyRange()
- Uses
Backend
- Deezer ISRC Fetching: Uses ISRCs already present in payloads and caches them, cutting extra API calls
- SearchAll Allocation: Preallocated slices to reduce allocations during Deezer search
- HTTP Client Helper: Refactored HTTP client creation to use
NewHTTPClientWithTimeout()helper function acrosslyrics.go,qobuz.go,tidal.go
Technical
- Go Backend Changes:
go_backend/extension_providers.go: AddedLabel,Copyright,Genrefields toExtTrackMetadata; added mutex locks to all provider methodsgo_backend/extension_manager.go: AddedVMMu sync.MutextoLoadedExtensionstructgo_backend/extension_runtime.go: Addedutils.randomUserAgentfunctiongo_backend/extension_runtime_utils.go: AddedrandomUserAgent()implementationgo_backend/httputil.go: UpdatedgetRandomUserAgent()to use modern Chrome versionsgo_backend/tidal.go: Added release date fallback logicgo_backend/exports.go: AddedGenre,Label,Copyrightfields toDownloadResponse
- Flutter Changes:
lib/services/cover_cache_manager.dart: New persistent cache manager for cover images (365 days, 1000 images max)lib/widgets/cached_cover_image.dart: Wrapper widget for CachedNetworkImage with persistent cachelib/main.dart: AddedCoverCacheManager.initialize()to app startuplib/screens/*.dart: All 11 screens updated to use persistent cache manager for CachedNetworkImagelib/providers/download_queue_provider.dart: Updated_embedMetadataAndCover()to accept and embed genre, label, copyright; addedgenre,label,copyrightfields toDownloadHistoryItemlib/screens/track_metadata_screen.dart: Display genre, label, copyright in metadata gridlib/l10n/arb/app_en.arb: AddedtrackGenre,trackLabel,trackCopyrightlocalization strings
Dependencies
- Added
flutter_cache_manager: ^3.4.1(explicit dependency for persistent cache) - Added
path: ^1.9.0(for cache directory path handling)
[3.1.2] - 2026-01-19
Added
- New Languages: Added Spanish (es) and Portuguese (pt) translations
- Spanish: Credits 125 (@credits125)
- Portuguese: Pedro Marcondes (@justapedro)
- Russian: Владислав (@odinokiy_kot)
- Quick Search Provider Switcher (#76): Dropdown menu in search bar for instant provider switching
- Tap the search icon to reveal a dropdown menu with all available search providers
- Shows default provider (Deezer based on metadata source setting) at the top
- Lists all enabled extensions with custom search capability
- Displays extension icons when available
- Checkmark indicates currently selected provider
- Search hint text updates immediately when switching providers
- Re-triggers search automatically if there's existing text in the search bar
- Eliminates need to navigate to Settings > Extensions > Search Provider
- Extension Button Setting Type (#74): New setting type for extension actions
- Extensions can define
buttontype in manifest settings - Triggers JavaScript function when tapped (e.g., start OAuth flow)
- Useful for authentication, manual sync, or any custom action
- Extensions can define
- Genre & Label Metadata (#75): Downloaded tracks now include genre and record label information
- Fetches genre and label from Deezer album API for each track
- Embeds GENRE, ORGANIZATION (label), and COPYRIGHT tags into FLAC files
- Works automatically when Deezer track ID is available (via ISRC matching)
- Supports all download services (Tidal, Qobuz, Amazon) and extension downloads
- MP3 Quality Option (#69): Optional MP3 download format with FLAC-to-MP3 conversion
- New "Enable MP3 Option" toggle in Settings > Download > Audio Quality
- When enabled, MP3 (320kbps) appears as a quality option alongside FLAC options
- Available in both the quality picker dialog and default quality settings
- Works with all services (Tidal, Qobuz, Amazon) and extensions
- MP3 Metadata Embedding: Full metadata support for MP3 files
- Cover art embedded using ID3v2 tags
- Synced lyrics embedded (fetched from lrclib.net)
- All metadata preserved: title, artist, album, album artist, track/disc number, date, ISRC
- Automatic tag conversion from Vorbis comments (FLAC) to ID3v2 (MP3)
- Dominant Color Header: Album, Playlist, Downloaded Album, and Track Metadata screens now feature dynamic header backgrounds
- Extracts dominant color from cover art using
palette_generator - Creates a gradient from dominant color to theme surface color
- Smooth 500ms color transition animation
- Extracts dominant color from cover art using
- Larger Cover Art: Cover images on detail screens are now 50% of screen width (previously 140px fixed)
- More prominent album artwork display
- Larger shadow and rounded corners (20px radius)
- Higher resolution cover caching
- Sticky Title: Title appears in AppBar when scrolling past the info card
- Smooth fade-in animation (200ms) when scrolling down
- Title hidden when header is expanded (shows in info card instead)
- AppBar uses theme color (surface) for clean, native look
- Works on Album, Playlist, Downloaded Album, Track Metadata, and Artist screens
- Artist Name in Album Screen: Album info card now displays artist name below album title
- Extracted from first track's artist metadata
- Styled with
onSurfaceVariantcolor for visual hierarchy
- Disc Separation for Multi-Disc Albums (#70): Downloaded albums with multiple discs now display tracks grouped by disc
- Visual disc separator header showing "Disc 1", "Disc 2", etc.
- Tracks sorted by disc number first, then by track number
- Single-disc albums display normally without separators
- Fixes confusion when albums have duplicate track numbers across discs
- Album Grouping in Recents (#70): Downloads now show as albums instead of individual tracks in the Recent section
- Prevents flooding the recents list when downloading full albums
- Groups tracks by album name and artist
- Tapping navigates directly to the downloaded album screen
- Shows the most recent download time for each album
Changed
- FFmpeg FLAC-to-MP3 Conversion: Improved conversion process
- MP3 files now saved in the same folder as FLAC (no separate MP3 subfolder)
- Original FLAC file automatically deleted after successful conversion
- New
embedMetadataToMp3()method for MP3-specific tag embedding
- Sticky Header Theme Integration: AppBar background uses
colorScheme.surfaceinstead of dominant color when collapsed- Dark theme: Black background with white text
- Light theme: White background with black text
- Matches modern app behavior for better readability
Fixed
- MP3 Quality Display in Track Metadata: Fixed incorrect quality display for MP3 files
- MP3 files now show "320kbps" instead of FLAC's bit depth/sample rate
- History no longer stores FLAC audio specs for converted MP3 files
- Both File Info badges and metadata grid show correct MP3 quality
- Empty Catch Blocks: Fixed analyzer warnings for empty catch blocks
download_queue_provider.dart: Added comments explaining why polling errors are silently ignoredtrack_provider.dart: Added comments explaining why availability check errors are silently ignoredffmpeg_service.dart: Added proper error logging for temp file cleanup failures
- Russian Plural Forms: Fixed ICU syntax warnings in Russian localization
- Removed redundant
=1clauses that were overridingoneplural category - Affected 10 plural strings including track counts and delete confirmations
- Plurals now correctly handle Russian grammar (1 трек, 2 трека, 5 треков)
- Removed redundant
Dependencies
- Added
palette_generator: ^0.3.3+4for cover art color extraction
[3.1.1] - 2026-01-17
Added
- Lyrics Caching: Lyrics are now cached for 24 hours to reduce API calls and improve performance
- Thread-safe cache with automatic expiration
- Cache key based on artist, track, and duration
- Log indicator shows "(cached)" when lyrics are served from cache
- Lyrics Duration Matching: Improved lyrics accuracy with duration-based matching
- Compares track duration with lrclib.net results
- 10-second tolerance to handle version differences (radio edit, remaster, etc.)
- Prioritizes synced lyrics over plain text when duration matches
- Falls back gracefully if no duration match found
- Deezer Cover Art Upgrade: Cover art from Deezer CDN now automatically upgraded to maximum quality
- Detects Deezer CDN URLs (
cdn-images.dzcdn.net) - Upgrades cover resolution to 1800x1800 (max available)
- Works alongside existing cover upgrade
- Detects Deezer CDN URLs (
- Live Search for Extensions: Search-as-you-type functionality for extension search
- 800ms debounce delay to prevent excessive API calls
- Minimum 3 characters required before searching
- Concurrency control to prevent race conditions in extension runtime
- Queues pending searches if a search is already in progress
- Russian Language Support: Added Russian (Русский) translation - 99% complete
- Translated via Crowdin community contributions
- Covers all UI elements, settings, and error messages
Fixed
- ISRC Index Race Condition: Fixed repeated index rebuilding during parallel downloads
- Added per-directory build lock using
sync.Mapandsync.Mutex - Double-check locking pattern ensures index is built only once
- Significantly improves performance during CSV import with many tracks
- Added per-directory build lock using
- Queue Tab Scroll Exception: Fixed Flutter rendering exception with NestedScrollView
- Disabled Material 3 stretch overscroll indicator that caused
_StretchControllerassertion - Wrapped NestedScrollView with ScrollConfiguration to prevent
setState during builderrors - Issue was especially noticeable during rapid queue updates (CSV import)
- Disabled Material 3 stretch overscroll indicator that caused
- CSV Import: Fixed CSV export not being parsed correctly
- Added support for
Artist Name(s)header (with parentheses) - Added support for
Track URIheader for track IDs - Added
artistsandtrack_idas alternative header names - Now correctly parses "Liked Songs" and playlist exports
- Added support for
[3.1.0] - 2026-01-16
Added
- Recent Access History: Quick access to recently visited content when tapping the search bar
- Artist Screen Redesign: Full-width header, monthly listeners, top tracks section
- Extension Store Update Badge: Badge indicator showing available extension updates
- Extension Compatibility Warning: Warning for extensions requiring newer app version
- Year in Album Folder Name: New folder structure options with release year
- Extension Album/Playlist/Artist Support: Extensions can now return collections in search
- Odesli Integration: YouTube Music extension can now match tracks to Deezer/Tidal/Qobuz
- Download Cancel: Properly stops in-flight downloads
Changed
- Search bar behavior improved with recent access history
Fixed
- Multiple extension-related fixes for artist, album, and playlist handling
- UI fixes for search, settings, and navigation
[3.0.0] - 2026-01-14
Extension System (Major Feature)
SpotiFLAC 3.0 introduces a powerful extension system that allows third-party integrations for metadata, downloads, and more.
- Extension Store: Browse and install extensions directly from the app
- Web Extension: Metadata provider for personalized playlists
- Extension Capabilities: Custom search, URL handlers, thumbnail ratios, post-processing
- Extension APIs: Full HTTP, storage, file, and crypto support
- Security: Sandboxed JavaScript runtime with permission-based access
Added
- Album folder structure settings
- Separate singles folder option
- Year in album folder name
- Parallel API calls for faster downloads
- Swipeable history filters
Fixed
- Tab edge overscroll
- Extension duplicate load error
- Settings item highlight on swipe
- Back gesture freeze on Android 13+
- Bottom overflow in dialogs
- Japanese artist name matching
- Multi-artist matching
- Max resolution cover download
- Various extension-related fixes
_For older versions, see GitHub Releases_