Commit Graph

208 Commits

Author SHA1 Message Date
zarzet 75a2bec8d5 chore: accessibility improvements, Semantics wrappers, and tooltip additions across screens 2026-03-08 15:08:13 +07:00
zarzet c35857bb61 fix(ios): local library scan fails on iOS due to missing security-scoped bookmark access 2026-03-08 14:57:13 +07:00
zarzet 2c897992c5 feat: resolve audio metadata from file, backfill placeholder quality labels with actual bit depth and sample rate 2026-03-08 04:28:16 +07:00
zarzet 7d5cb574c6 feat: move Amazon Music to extension, fix Deezer download timeout 2026-03-08 04:15:28 +07:00
zarzet 4a61ffea8d chore: update VirusTotal hash 2026-03-06 22:00:56 +07:00
zarzet 36a646e5c0 feat: add Deezer download service, Qobuz squid.wtf fallback, update changelog 2026-03-06 21:18:50 +07:00
zarzet 98abaf6635 v3.7.0: roll back from v4, remove internal player — v3 is already complete
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
2026-03-04 02:02:25 +07:00
zarzet 96d11b1d7d feat: add external player mode for local library playback 2026-02-27 14:38:45 +07:00
zarzet b3771f3488 fix: disable automatic spotify-web install during setup 2026-02-27 14:31:49 +07:00
zarzet a07c125454 feat: update collection actions for offline-first playback 2026-02-27 14:30:10 +07:00
zarzet 2fe8f659bc refactor: simplify setup flow and update collection actions 2026-02-27 13:48:47 +07:00
zarzet ab26d84632 chore: rebuild dev history without streaming-era commits 2026-02-27 13:48:44 +07:00
zarzet c89600591c feat: add love and add-to-playlist circular buttons to album screen
Flanks the Download All button with two circular icon buttons:
- Left: heart (favorite_border/favorite) toggles love for all tracks; turns red when all are loved
- Right: plus icon opens the playlist picker sheet for all album tracks
2026-02-20 03:30:34 +07:00
zarzet f1d57d89c7 refactor: extract duplicated code to shared utilities across Dart and Go
- Extract normalizeOptionalString() to lib/utils/string_utils.dart from download_queue_provider and track_metadata_screen
- Extract PrioritySettingsScaffold widget from lyrics and metadata priority pages, reducing ~280 lines of duplication
- Extract _ensureDefaultDocumentsOutputDir/_ensureDefaultAndroidMusicOutputDir in download queue provider
- Extract collectLibraryAudioFiles() and applyDefaultLibraryMetadata() in Go library_scan.go
- Extract plainTextLyricsLines() in Go lyrics.go, used by Apple Music, Musixmatch, and QQ Music clients
2026-02-19 19:49:58 +07:00
zarzet 83124875d3 fix: wrap collection folder list items in Card to match history item style in library tab 2026-02-19 19:31:18 +07:00
zarzet 9460e9faae fix: remove dividers and align content padding in playlist track list to match album screen 2026-02-19 19:26:08 +07:00
zarzet 882afd938b feat: add SongLink region setting and fix track metadata lookup with name+artist fallback
- Add configurable SongLink region (userCountry) setting with picker UI
- Pass songLinkRegion through download request payload to Go backend
- Go backend: thread-safe global SongLink region with per-request override
- Fix downloaded track not recognized in collection tap: add findByTrackAndArtist
  fallback in download history lookup chain (Spotify ID → ISRC → name+artist)
- Apply same name+artist fallback to isDownloaded check in track options sheet
- Add missing library_database.dart import for LocalLibraryItem
2026-02-19 19:16:55 +07:00
zarzet ab72a10578 feat: add multi-select to library folders, batch playlist picker, and Go backend FD safety
- Add multi-select support to library_tracks_folder_screen (wishlist, loved,
  playlist) with long-press to enter selection mode, animated bottom bar with
  batch remove/download/add-to-playlist actions, and PopScope exit handling
- Create batch showAddTracksToPlaylistSheet in playlist_picker_sheet with
  playlist thumbnail widget and cover image support
- Add playlist grid selection tint overlay in queue_tab
- Optimize collection lookups with pre-built _allPlaylistTrackKeys index and
  isTrackInAnyPlaylist/hasPlaylistTracks accessors
- Eagerly initialize localLibraryProvider and libraryCollectionsProvider
- Enable SQLite WAL mode and PRAGMA synchronous=NORMAL across all databases
- Go backend: duplicate SAF output FDs before provider attempts to prevent
  fdsan abort on fallback retries; close detached FDs after download completes
- Go backend: rewrite compatibilityTransport to try HTTPS first and only
  fallback to HTTP on transport-level failures, preventing redirect loops
- Go backend: enforce HTTPS-only for extension sandbox HTTP clients
2026-02-19 18:27:14 +07:00
zarzet e39756fa3f refactor: migrate persistence to SQLite, add strict provider mode, and optimize collection lookups
- Replace SharedPreferences with SQLite (AppStateDatabase, LibraryCollectionsDatabase) for download queue, library collections, and recent access history
- Add Set-based O(1) track containment checks for wishlist, loved, and playlist tracks
- Add batch addTracksToPlaylist with PlaylistAddBatchResult
- Go backend: strict mode locks download to selected provider when auto fallback is off
- Go backend: fix extension progress normalization (percent/100) and lifecycle tracking
- Go backend: case-insensitive provider ID matching throughout fallback chain
- Lyrics embedding now respects lyricsMode setting (embed/both/off)
- Debounced queue persistence to reduce write frequency
- Fix shouldUseFallback logic to not be gated by useExtensions
2026-02-19 16:40:03 +07:00
zarzet 8e794e1ef1 feat: Library tab redesign with playlists, drag-and-drop categorization, and pinned app bars 2026-02-19 15:55:24 +07:00
zarzet caf68c8137 redesign: full-screen cover art with parallax scroll across all detail screens
Replace blurred background + centered cover thumbnail with full-screen
cover art, dark gradient overlay, and parallax collapse mode for a
consistent Apple Music-inspired design across album, playlist, downloaded
album, local album, and track metadata screens. Remove select button UI
(users enter selection via long-press), upgrade cover resolution for
Spotify/Deezer CDN, and move track/album info into the overlay.
2026-02-19 00:28:12 +07:00
zarzet 4df96db809 feat: batch re-enrich for local tracks, SAF FD refactor, Ogg quality fix
- Replace batch Share action with batch Re-enrich in local album selection bar
  - Full native/FFmpeg re-enrich flow with SAF write-back support
  - Triggers incremental local library scan after completion to refresh metadata
- Queue tab: switch first selection action to Re-enrich when all selected items are local-only
- Refactor SAF FD handoff in MainActivity: drop detachFd/dup pattern, pass procfs
  path to Go and let Go re-open it to avoid fdsan double-close race conditions
- Handle /proc/self/fd/ path in output_fd.go: re-open via O_WRONLY|O_TRUNC instead
  of taking raw FD ownership
- Fix Ogg duration/bitrate calculation in audio_metadata.go:
  - Use float64 arithmetic and math.Round for accurate duration
  - Compute bitrate from file size / float duration at the source
  - Validate Ogg page header fields (version, headerType, segment table) to avoid
    false positives from payload bytes during backward scan
  - Guard against corrupted granule values (>24h duration, <8kbps bitrate)
- Rename trackReEnrich label from 'Re-enrich Metadata' to 'Re-enrich' across all
  13 locales and ARB files
- Update CHANGELOG.md with 3.7.0 entry
2026-02-18 19:29:59 +07:00
zarzet 5605930aef feat: add multi-select share and batch convert in downloaded/local album screens
- Add shareMultipleContentUris native handler in MainActivity for ACTION_SEND_MULTIPLE
- Add shareMultipleContentUris binding in PlatformBridge
- Add _shareSelected and _performBatchConversion methods to DownloadedAlbumScreen and LocalAlbumScreen
- Add batch convert bottom sheet UI with format/bitrate selection (MP3/Opus, 128k-320k)
- Add share & convert action buttons to selection bottom bar in both screens
- Add batch convert with full SAF support: temp copy, write-back, history update
- Add share/convert selection strings to l10n (all supported locales + app_en.arb)
- Add queue tab selection share/convert feature (queue_tab.dart)
- Update donate page
- Update go.sum with bumped dependency hashes
2026-02-18 18:05:48 +07:00
zarzet 813ed79073 refactor: conditionally show lyrics settings only when embed lyrics is enabled 2026-02-17 20:57:50 +07:00
zarzet 90f731ac1e fix: refine launcher icons and settings page presentation
Polish generated app icon handling and improve the settings page supporter section layout for better scalability and readability.
2026-02-17 18:26:20 +07:00
zarzet 8e6cbcbc2a feat: YouTube customizable bitrate, improved title matching, SpotubeDL engine fallback
- 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
2026-02-17 17:22:24 +07:00
zarzet 3e90b29d2b fix: improve lyrics error detection and add new donor
- Detect error JSON payloads from Apple Music and QQMusic proxies
- Add lyricsHasUsableText validation for usable lyrics content
- Skip empty word lines in synced lyrics parsing
- Add ClearAll method to lyrics cache
- Handle TimeoutException properly in track metadata screen
- Add laflame to recent donors list
2026-02-14 17:25:17 +07:00
zarzet f4934dcb28 feat: add lyrics source tracking, Paxsenix partner, and dedicated lyrics provider settings page
- Add getLyricsLRCWithSource to return lyrics with source metadata
- Display lyrics source in track metadata screen
- Improve LRC parsing to preserve background vocal tags
- Add dedicated LyricsProviderPriorityPage for provider configuration
- Add Paxsenix as lyrics proxy partner for Apple Music/QQ Music
- Handle inline timestamps and speaker prefixes in LRC display
2026-02-14 02:15:36 +07:00
zarzet 30973a8e78 feat: lyrics provider extensions, configurable lyrics cascade, and iOS method channel parity
Add lyrics_provider as a new extension type alongside metadata_provider and
download_provider. Extensions implementing fetchLyrics() are called before
built-in providers, giving user-installed extensions highest priority.

Built-in lyrics cascade is now configurable from Download Settings:
  - Reorderable provider list (LRCLIB, Musixmatch, Netease, Apple Music, QQ Music)
  - Per-provider options: Netease translation/romanization, Apple/QQ multi-person
    word-by-word speaker tags, Musixmatch language code
  - Provider order and options synced to Go backend on app start and on change

Go backend changes:
  - New lyrics_provider manifest type with validation (extension_manifest.go)
  - ExtensionProviderWrapper.FetchLyrics() with Goja JS bridge (extension_providers.go)
  - Configurable SetLyricsProviderOrder/GetLyricsProviderOrder cascade (lyrics.go)
  - LyricsFetchOptions struct for per-provider settings (lyrics.go)
  - Extracted tryLRCLIB() helper, randomized LRCLIB User-Agent (lyrics.go)
  - Refactored msToLRCTimestamp to separate msToLRCTimestampInline (lyrics.go)
  - New provider source files: lyrics_apple.go, lyrics_musixmatch.go,
    lyrics_netease.go, lyrics_qqmusic.go
  - JSON export functions for lyrics settings (exports.go)
  - hasLyricsProvider field in extension manager JSON output

Platform channels:
  - Android (MainActivity.kt): setLyricsProviders, getLyricsProviders,
    getAvailableLyricsProviders, setLyricsFetchOptions, getLyricsFetchOptions
  - iOS (AppDelegate.swift): same 5 method channel handlers for iOS parity

Flutter side:
  - Extension model: hasLyricsProvider field + Lyrics Provider capability badge
  - Settings model: lyricsProviders, lyricsIncludeTranslationNetease,
    lyricsIncludeRomanizationNetease, lyricsMultiPersonWordByWord,
    musixmatchLanguage fields with generated serialization
  - Settings provider: setters + _syncLyricsSettingsToBackend()
  - Download settings UI: provider picker, toggle switches, language picker
  - Platform bridge: lyrics provider/options methods

Docs: lyrics provider extension documentation in site/docs.html
CHANGELOG: updated with lyrics provider and search feature entries
2026-02-14 01:42:18 +07:00
zarzet 1407018d98 feat: advanced filename templates, low-RAM device profiling, responsive artist UI, and project site
- Add advanced filename template placeholders: {track_raw}, {disc_raw}, {date},
  formatted numbers {track:N}/{disc:N}, and date formatting {date:%Y-%m-%d}
  with strftime-to-Go layout conversion and robust date parser
- Pass date/release_date metadata to filename builder in all providers
  (Amazon, Qobuz, Tidal, YouTube, extensions) and Flutter download queue
- Detect ARM32-only / low-RAM Android devices at startup and reduce image
  cache size and disable overscroll effects for smoother experience
- Make artist screen selection bar responsive: compact stacked layout on
  narrow screens or large text scale; add quality picker before track download
- Add advanced tags toggle in download settings filename format editor
- Fix ICU plural syntax in DE/ES/PT/RU translations (one {}=1{...} -> one {...})
- Add filenameShowAdvancedTags l10n strings (EN, ID) and regenerate dart files
- Fix featured-artist regex: remove '&' from split separators
- Add Go filename template tests (filename_test.go)
- Add GitHub Pages workflow and static project site
2026-02-13 21:39:08 +07:00
zarzet 3c1e9d03a0 fix(ios): recover notification permission and path handling 2026-02-12 02:23:54 +07:00
zarzet cd6beaa7d4 feat: add filterContributingArtistsInAlbumArtist setting - new option to strip contributing artists from Album Artist metadata - applies to folder organization and metadata embedding - collapsible Artist Name Filters section in download settings UI 2026-02-12 01:06:08 +07:00
zarzet 3c3bbe516e v3.6.6: fix iOS downloads, metadata fallback, lossy quality display, audio duration accuracy 2026-02-12 00:32:40 +07:00
zarzet a1d1ab1f0f fix: preserve extended metadata during fallback, accurate lossy quality display, SAF improvements
- Add Genre/Label/Copyright fields to DownloadResult struct
- buildDownloadSuccessResponse now prefers service result metadata over request
- enrichRequestExtendedMetadata fetches Deezer metadata by ISRC before download
- Flutter sends copyright in download request payload
- History merge preserves existing genre/label/copyright on re-download
- Accurate MP3 duration via Xing/VBRI VBR headers, MPEG2/2.5 bitrate tables
- Accurate Opus/Vorbis duration via last Ogg page granule position
- Bitrate field added to LibraryScanResult, LocalLibraryItem, DB v4 migration
- Lossy formats display format+bitrate instead of fake 16-bit quality
- Local library file date uses fileModTime instead of scannedAt
- SAF URI recovery for transient FD paths after download
- Improved SAF repair and download history path matching in library scan
- Extract quality probe logic into reusable enrichResultQualityFromFile
2026-02-12 00:19:02 +07:00
zarzet abc599d7f9 refactor: migrate queue_tab cover resolver to shared service, add supporter 2026-02-11 12:31:47 +07:00
zarzet 9b27e86e0f fix: show library filter buttons while downloads are active
Previously filter/sort headers in All, Albums, and Singles tabs
were hidden when queue items existed, preventing users from
filtering their library (e.g. find MP3 tracks to re-download
as FLAC) during active downloads.
2026-02-11 08:01:11 +07:00
zarzet 9847594ca1 perf: parallel I/O, caching, and chunked DB operations (batch 3)
- Orphan cleanup: parallel file existence checks (chunk 16)
- LocalLibraryState: O(1) findByTrackAndArtist via _byTrackKey map
- Local library load: parallel DB + SharedPreferences fetch
- Legacy mod-time backfill: chunked parallel File.stat (chunk 24)
- Downloaded album screen: cache disc groups, quality, cover path
- Local album screen: cache common quality, map-based batch delete
- Cache management: parallel async init, chunked directory cleanup
- Cover resolver: throttled preview exists check (2.2s interval)
- History/Library DB: chunked SQL DELETE (500 per batch)
- Batch delete screens: O(1) item lookup via tracksById map
2026-02-11 02:40:09 +07:00
zarzet 84df64fcfe perf+security: polling guards, sensitive data redaction, SAF path sanitization
Go backend:
- Add sensitive data redaction in log buffer (tokens, keys, passwords)
- Validate extension auth URLs (HTTPS only, no private IPs, no embedded creds)
- Block embedded credentials in extension HTTP requests
- Tighten extension storage file permissions (0644 -> 0600)
- Sanitize extension ID in store download path
- Summarize auth URLs in logs to prevent token leakage

Android (Kotlin):
- Add sanitizeRelativeDir to prevent path traversal in SAF operations
- Apply sanitizeFilename to all user-provided file names in SAF

Flutter:
- Add sensitive data redaction in Dart logger (mirrors Go patterns)
- Mask device ID in log exports
- Add in-flight guard to progress polling (download queue + local library)
- Remove redundant _downloadedSpotifyIds Set, use _bySpotifyId map
- Remove redundant _isrcSet, use _byIsrc map
- Expand DownloadQueueLookup with byItemId and itemIds
- Lazy search index building in queue tab
- Bound embedded cover cache in queue tab (max 180)
- Coalesce embedded cover refresh callbacks via postFrameCallback
- Cache album track filtering in downloaded album screen
- Cache thumbnail sizes by extension ID in home tab
- Simplify recent access aggregation (single-pass)
- Remove unused _isTyping state in home tab
- Cap pre-warm track batch size to 80
- Skip setShowingRecentAccess if value unchanged
- Use downloadQueueLookupProvider for granular queue selectors
- Move grouped album filtering before content data computation
2026-02-11 02:02:03 +07:00
zarzet a9150b85b9 perf: memory and rebuild optimizations across app
- Bound Deezer cache with LRU eviction and periodic cleanup
- Configure Flutter image cache limits (240 entries / 60 MiB)
- Add ResizeImage wrapper for precacheImage calls
- Add memCacheWidth/cacheWidth to cover images across screens
- Add DownloadedEmbeddedCoverResolver as centralized cover service
- Throttle download progress notifications with dedup checks
- Normalize progress/speed/bytes values to reduce UI rebuilds
- Optimize queue list with per-item ConsumerWidget and RepaintBoundary
- Preserve derived indexes in LocalLibraryState.copyWith
- Skip non-error logs when detailed logging disabled
- Use async file stat and early-break loops in queue filters
2026-02-11 01:44:05 +07:00
zarzet 68e6c8be35 ui: improve cover preview in edit metadata sheet and user changes
- Cover preview enlarged from 120x120 to 160x160 with shadow and better styling
- Layout changed from Wrap to Row with Expanded for side-by-side covers
- Label moved below image with labelMedium typography
- Cover editor section moved to top of edit form
- Added embedded cover preview cache with LRU eviction in metadata screen
- Added current cover extraction and preview in edit metadata sheet
- Added metadata sync to download history after edits
- Added embedded cover extraction cache in queue tab for downloaded items
- Added SAF mod-time tracking for cover refresh after metadata changes
2026-02-11 01:13:24 +07:00
zarzet bd42655c0e fix: various improvements and fixes 2026-02-11 00:22:48 +07:00
zarzet fe1c96ea12 v3.6.5: audio format conversion, PC v7.0.8 backend merge, Amazon re-enabled 2026-02-10 23:35:41 +07:00
zarzet bae2bf63eb chore: remove Buy Me a Coffee references (account suspended) 2026-02-10 20:46:45 +07:00
zarzet f7be2c1e12 feat: primary artist only folders, fix notifications v20, fix SAF duplicate dirs
- Add 'Use Primary Artist Only' setting to strip featured artists from folder names
- Fix flutter_local_notifications v20 breaking changes (positional params)
- Fix SAF duplicate folder bug: synchronized ensureDocumentDir to prevent race condition creating empty folders with (1), (2) suffixes during concurrent downloads
2026-02-10 09:07:18 +07:00
zarzet df39d61ed4 feat: save cover art, save lyrics, re-enrich metadata with full SAF support + YouTube Cobalt provider with SpotubeDL fallback + metadata summary logging 2026-02-09 23:07:18 +07:00
zarzet 7ec5d28caf feat: add YouTube provider for lossy downloads via Cobalt API
- New YouTube download provider with Opus 256kbps and MP3 320kbps options
- SongLink/Odesli integration for Spotify/Deezer ID to YouTube URL conversion
- YouTube video ID detection for YT Music extension compatibility
- Parallel cover art and lyrics fetching during download
- Queue progress shows bytes (X.X MB) for streaming downloads
- Full metadata embedding: cover, lyrics, title, artist, album, track#, disc#, year, ISRC
- Removed Tidal HIGH (lossy AAC) option - use YouTube for lossy instead
- Bumped version to 3.6.0
2026-02-09 18:15:43 +07:00
zarzet 23f5aa11b0 feat: responsive layout tuning, cache management page, and improved recent access UX
- Add responsive scaling across album, artist, playlist, downloaded album, local album, queue, setup, and tutorial screens to prevent overflow on smaller devices
- Add new Storage & Cache management page (Settings > Storage & Cache) with per-category clear and cleanup actions
- Extract normalizedHeaderTopPadding utility for consistent app bar padding
- Improve home search Recent Access behavior: show when focused with empty input, hide stale results during active recent mode
- Add excluded-downloaded-count tracking to local library scan stats
- Add recentEmpty and recentShowAllDownloads l10n keys (EN + ID)
- Add full cache management l10n keys (EN + ID)
- Fix about_page indentation and formatting consistency
- Fix appearance_settings_page formatting
- Fix downloaded_album_screen and local_album_screen formatting and responsive sizing
2026-02-09 15:58:50 +07:00
zarzet 5fdf1df5df feat: cross-script transliteration matching for Tidal/Qobuz and skip-downloaded option for CSV import 2026-02-09 10:57:52 +07:00
zarzet f9dd82010f fix: skip M4A conversion for existing files and prevent empty SAF folders on duplicates 2026-02-08 15:44:05 +07:00
zarzet f0790b627d perf: optimize album, artist, and playlist screens
- Scope settingsProvider watches with select() for localLibrary flags

- Wrap popular track items in Consumer for scoped provider watches

- Apply dart format reformatting
2026-02-08 15:00:57 +07:00