SpotiFLAC v3.1.3
What's New
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)
Downloads
Android
- arm64:
SpotiFLAC-v3.1.3-arm64.apk(recommended for modern devices) - arm32:
SpotiFLAC-v3.1.3-arm32.apk(older devices)
iOS
- iOS:
SpotiFLAC-v3.1.3-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