Commit graph

5 commits

Author SHA1 Message Date
zarzet
8e605cbd0f feat: persist codec format and bitrate in download history
Bump the history schema on both the Kotlin finalizer and the Dart database to v9, adding bitrate (kbps) and format (codec label) columns, and let the download flow fill them from backend/probe metadata so lossy downloads keep a 'AAC 256kbps' label instead of falling back to the stored placeholder. Library filtering and the track metadata screen now read format/bitrate directly from those columns, which also fixes mis-tagged quality badges after re-downloading a track at a different format.

Additional fixes bundled in: EditFileMetadata now routes ReplayGain writes through the M4A path whenever the file starts with ftyp (fixing .flac files that actually hold MP4 containers); GetM4AQuality falls back to the first trak/mdia/mdhd duration when mvhd is zero so EAC3 streams no longer report 0s; and both Kotlin and Dart reject bitrate values below 16 kbps to prevent probe noise from surfacing as '0 kbps' labels. New unit tests cover the EAC3 mdhd fallback and the mis-named M4A replaygain path.
2026-05-10 23:18:32 +07:00
zarzet
01a5b43613 perf: unify queue tab with DB-backed pagination and cross-database queries
Replace in-memory list merging in the queue tab with fully database-
backed pagination using ATTACH DATABASE to join library and history
tables in a single UNION ALL query.

Queue tab:
- Remove localLibraryAllItemsProvider and _queueHistoryStatsProvider
- Add _queueLibraryPageProvider and _queueLibraryCountsProvider backed
  by LibraryDatabase.getQueueTrackPage/getQueueCounts/getQueueAlbumPage
- Implement infinite scroll via _handleLibraryScrollNotification with
  _libraryPageLimit growing by 300 per batch
- Album/single/total counts computed via SQL GROUP BY aggregates

History database (v5 -> v8):
- v6: add idx_history_track_artist index
- v7: add history_path_keys table for cross-DB dedup, backfill from
  existing rows
- v8: add spotify_id_norm, isrc_norm, match_key normalized columns
  with indexes, backfill from existing data
- Add getAlbumTracks, findByTrackAndArtist, getGroupedCounts,
  existsTrack, findExistingTrack, existingTrackKeys batch lookup
- deleteBySpotifyId now returns deleted count for accurate totalCount
- All write paths maintain history_path_keys consistency

Library database (v7 -> v8):
- v8: add library_path_keys table for cross-DB dedup
- Add getQueueTrackPage, getQueueCounts, getQueueAlbumPage with
  ATTACH DATABASE for cross-DB UNION ALL queries
- Dedup local items against history via path_keys JOIN
- All write/delete paths maintain library_path_keys consistency

Download history provider:
- Load only 100 recent items into state.items at startup
- Store lookupItems as immutable List field instead of recomputing
  from maps on every access
- Add async fallback to DB in _putInMemoryHistory for items outside
  the 100-item window
- Add downloadHistoryPageProvider, downloadHistoryGroupedCountsProvider,
  downloadedAlbumTracksProvider, downloadHistoryBatchExistsProvider
- Add catchError to adoptNativeHistoryItem async block
- Fix removeBySpotifyId to query actual DB count instead of decrement

Screen migrations:
- album/artist/playlist/home screens use async DB lookups instead of
  sync in-memory state for track existence and playback resolution
- downloaded_album_screen uses downloadedAlbumTracksProvider
- library_tracks_folder_screen uses downloadHistoryBatchExistsProvider
  for skip-downloaded checks and cover resolution
2026-05-06 04:38:51 +07:00
zarzet
149cdc782d refactor: migrate local library from in-memory list to database-backed pagination
Replace the full in-memory List<LocalLibraryItem> in LocalLibraryState
with a lightweight lookup index (ISRCs, matchKeys, filePathById) and
database-backed FutureProvider.family pagination providers.

Database changes:
- Add library schema v7 with normalized lookup columns (track_name_norm,
  artist_name_norm, album_name_norm, album_artist_norm, match_key,
  album_key) and corresponding indexes
- Backfill normalized columns on migration from v6
- Add getPage, getPageCount, getAlbumPage, getAlbumCount, getLookupIndex,
  getCoverPaths, getByFilePath, findFirstByTrackAndArtist DB methods

Provider changes:
- LocalLibraryState no longer holds items list; uses totalCount and
  loadedIndexVersion for change tracking
- Deprecate synchronous getByIsrc/findByTrackAndArtist (return null);
  add async findExistingAsync, getByIsrcAsync, getById on notifier
- Add localLibraryPageProvider, localLibraryAlbumPageProvider,
  localLibraryAllItemsProvider family providers for paginated access
- Add localLibraryCoverProvider and localLibraryFirstCoverProvider
  for async cover path resolution from DB

Screen migrations:
- album/artist/playlist screens use findExistingAsync for playback
- library_tracks_folder_screen uses async cover providers and
  existsInLibrary for local library indicator
- queue_tab watches localLibraryAllItemsProvider instead of state.items
- library_settings_page uses state.totalCount
- playback_provider uses findExistingAsync

Track metadata screen:
- Replace pushReplacement navigation with in-place state swap using
  AnimatedSwitcher for smooth cross-fade transitions on track swipe
- Add metadataLoadGeneration counter to prevent stale async callbacks
- Reset all transient state (lyrics, cover, file check) on track change
2026-05-06 03:15:30 +07:00
zarzet
9a78798854 feat: improve library grid, image loading, and metadata filters
UI and UX improvements across the library and queue screens.

- Add pinch-to-zoom for library grid views with animated extent
  transitions via _AnimatedLibrarySliverGrid widget
- Replace fixed grid column count with responsive maxCrossAxisExtent
- Add smooth fade-in for cover images (local files via frameBuilder,
  network via CachedCoverImage fadeInDuration/fadeOutDuration params)
- Refactor track metadata swipe navigation from push+pop to
  pushReplacement to prevent route stack accumulation
- Convert adjacentHorizontalPageRoute to MaterialPageRoute subclass
  to support pushReplacement with proper transition semantics
- Add five new metadata completeness filters: missing track number,
  missing disc number, missing artist, incorrect ISRC format, and
  missing label
- Expose trackNumber, discNumber, isrc, and label on
  UnifiedLibraryItem for filter support
- Tighten metadata completeness definition to include all new fields
2026-05-05 04:22:24 +07:00
zarzet
3a7419ec9f refactor: split large screen files into part files and DRY platform bridge
- Extract home_tab.dart helpers/widgets into home_tab_helpers.dart and home_tab_widgets.dart using Dart part files
- Extract queue_tab.dart helpers/widgets into queue_tab_helpers.dart and queue_tab_widgets.dart using Dart part files
- Extract track_metadata_edit_sheet.dart from track_metadata_screen.dart using Dart part file
- Refactor _FileExistsListenableCache into a standalone class in queue_tab_helpers.dart
- Fix artist_screen.dart: replace unreliable findAncestorStateOfType with GlobalKey for _FetchingProgressDialog progress updates
- DRY platform_bridge.dart: extract common JSON decode patterns into reusable helper methods (_decodeRequiredMapResult, _decodeNullableMapResult, _decodeMapListResult, _decodeStringListResult)
2026-05-02 00:27:51 +07:00