Commit Graph
70 Commits
Author SHA1 Message Date
zarzet b0600c1ff7 refactor(library): remove the unreachable snapshot sub-branch of the incremental scan
The Dart incremental ingestion branch runs only when
PlatformBridge.supportsCoreBackend (Platform.isAndroid || Platform.isIOS)
is false. Its useSnapshotBridge flag required Platform.isAndroid, so it
could never be true there, and the native handlers for
scanSafTreeIncrementalFromSnapshot and
scanLibraryFolderIncrementalFromSnapshot are already gone.

Remove the flag, both PlatformBridge methods, and
LibraryDatabase.writeFileModTimesSnapshot(Map), whose only other user was
test/library_scan_snapshot_test.dart (deleted with it). The live native
snapshot path (writeNativeFileModTimesSnapshot) and the host-test branch
with _backfillLegacyFileModTimes are unchanged.
2026-10-19 01:18:02 +07:00
zarzet eeb010cd52 refactor(library): extract the ingestion store from library_database.dart
LibraryIngestionStore holds scan and incremental ingestion writes with
bodies unchanged. LibraryDatabase keeps thin delegating methods with the
same public signatures and passes in its connection and the lazy History
attachment. SQL text is unchanged.
2026-10-18 23:28:33 +07:00
zarzet 4887172926 refactor(services): mark test-only members and privatize file-local ones
Annotate members that only tests call with @visibleForTesting and make
helpers used only inside their own file private.
2026-10-18 22:12:29 +07:00
zarzet ec1b84e6de refactor(services): remove members with no caller
Delete Dart members that have no reference in lib, test or integration_test:
unused player configuration helpers and event streams, two PlatformBridge
wrappers, database and export helpers, the unused global logger and the
now pointless 'hide log' imports.
2026-10-18 22:12:29 +07:00
zarzet cdceda4585 fix(library): exclude inactive NAS entries and file aliases from duplicates 2026-10-10 02:16:32 +07:00
zarzet d89048409f fix(library): retry transient SQLite locks before scan transactions 2026-10-07 18:46:05 +07:00
zarzet 8ca9205cc4 fix(library): stage native scans before owner SQLite publication 2026-10-07 04:19:14 +07:00
zarzet d1de833184 perf(library): index scans and snapshots with native SQLite workers
Move full and incremental ingestion, source snapshots, path keys, and download exclusion to native transactions. Preserve schema 17, FTS, WAL coordination, cancellation, and Android SAF timestamp backfill.
2026-10-06 23:29:33 +07:00
zarzet a6c1409ef5 fix(library): remove deleted files from both library indexes 2026-10-06 00:30:45 +07:00
zarzet 2e17963804 refactor(library): isolate schema, row mapping and queue queries
Repair lookup counts during schema migration and resolve local FTS matches through the base table.
2026-10-05 03:28:48 +07:00
zarzet fb41a86f5a fix(library): match SAF documents across folder tree aliases 2026-10-03 13:40:39 +07:00
zarzet 253a19f17f fix(library): preserve folders during upgrades and load failures
Load configured sources before storage probes and the track index, retain visible configuration on failures, and expose loading and retry states. Wait for saved settings before legacy migration and recreate a missing source placeholder without replacing existing identities.
2026-10-02 18:34:57 +07:00
zarzet 9ea2647c39 feat(library): filter albums with missing tracks
Assess album completeness from the full local inventory, deduplicate track positions, and retain unknown status for ambiguous tags or disconnected sources. Add filters and count badges to Material and Mornye, including single-track albums and filtered search.
2026-10-02 17:03:07 +07:00
zarzet 718e13f24e feat(library): filter tracks missing verified ReplayGain tags 2026-10-02 01:24:26 +07:00
zarzet a21e46e6dd perf(library): trim scan ingestion writes
Fresh staging tables hold no path keys, so the first row for an id no longer issues a delete. Removing scanned paths now runs in one transaction with a subselect instead of committing a lookup and two deletes per chunk.
2026-10-01 19:06:25 +07:00
zarzet b3f7f59e2e refactor(library): remove unused history lookups and replacement paths 2026-09-30 21:43:54 +07:00
zarzet 6c22a91498 fix(library): preserve partial scans and ignore stale completion events 2026-09-30 20:57:48 +07:00
zarzet 369afa550b feat(library): search songs albums artists and playlists by relevance 2026-09-27 00:00:01 +07:00
zarzet c87c9ff848 feat(library): add Mornye browsing and contextual compact navigation
Add category browsing, a two-column Recently Added grid, and a dedicated download queue. Keep compact navigation icons and their animations aligned with the active tab.

Keep profile avatars circular and report lyric enrichment outcomes accurately, preserving existing lyrics and supporting instrumental markers.
2026-09-21 23:02:52 +07:00
zarzet 63e1a9b9c8 feat(ui): add Mornye glass theme and adaptive music player
Add a selectable Mornye theme with shared glass controls, dialogs, sheets, notifications, and collection headers while retaining Material components.

Update player artwork, lyrics transitions and highlighting, synchronized rolling time labels, volume controls, and compact navigation. Unify metadata, Library, search, and extension surfaces; preserve restored artwork and report extension load failures.

Validation: full Flutter analyzer clean; 713 tests passed and 4 skipped; pinned Dart formatting and diff checks passed; signed ARM64 and ARM32 release APKs built and verified.
2026-09-19 12:10:10 +07:00
zarzet 1ef81b4530 perf(library): batch album deletion and refresh once 2026-09-17 17:32:12 +07:00
zarzet aa99726439 feat(backend): migrate production backend to Rust
Replace the Go backend with the Rust workspace and route Android/iOS through
UniFFI bindings. Include the migrated extension runtime, network, providers,
media metadata, downloads and library operations, with version 5.0.0+147.

Remove Go sources, adapters, native build selection and CI dependencies.
Build Rust unconditionally and lock the iOS Rust pod using a relative path.
Retain legacy source and migration evidence in a local ignored archive.

Validation: Android Kotlin compile and 53 native tests; Swift Rust-branch
equivalence and syntax; CocoaPods install; workflow, shell, Ruby, plist and
diff checks. Reuse the preceding 100 Rust tests, fmt/Clippy and five-ABI
build checkpoint; no new APK or iOS application build for this cleanup.

Known follow-up: URL/URLSearchParams globals are missing from the Rust JS
runtime. A controlled extension replay confirms a URL-resolution regression;
this commit does not fix that runtime gap.
2026-09-14 22:58:27 +07:00
zarzet 62ef538690 perf(library): page missing-file cleanup with safe SAF batch checks 2026-09-06 02:34:49 +07:00
zarzet d95d070565 perf(library): stream normalized scan timestamp snapshots 2026-09-06 02:34:49 +07:00
zarzet c3b2214e2a perf: optimize library scans and extension runtime 2026-08-28 21:00:11 +07:00
zarzet 357190757c fix(database): cache unavailable FTS5 capability 2026-08-28 18:47:54 +07:00
zarzet 45d9bbd7b2 perf: harden download and persistence lifecycle 2026-08-28 12:51:03 +07:00
zarzet 1a7c20111a feat(library): add lyrics filtering and metadata actions 2026-08-27 01:44:06 +07:00
zarzet 19591bdf19 feat(metadata): show explicit badges in track titles 2026-08-22 23:08:40 +07:00
zarzet 60624436a6 feat(library): support multiple storage folders 2026-08-22 11:15:42 +07:00
zarzet 7fc27e8314 perf(library): stream scans and optimize queue queries 2026-08-18 11:03:05 +07:00
zarzet 2612da81c3 fix(db): prevent concurrent startup locks 2026-08-08 00:01:00 +07:00
zarzet 7f277f7a88 fix(library): backfill local audio bitrates 2026-07-28 12:51:28 +07:00
zarzet d0dca1a101 refactor(library): split row models and queue union SQL into part files 2026-07-27 00:32:42 +07:00
zarzet cb8064a317 refactor(dart): consolidate duplicated lookup, filename, and search-provider helpers 2026-07-26 18:52:26 +07:00
zarzet 97758908ca refactor(library): drop unwired DB pagination providers 2026-07-26 18:44:06 +07:00
zarzet 87376ee73b fix(metadata): resolve audio format and local artwork 2026-07-22 15:55:30 +07:00
zarzet 491998e5c5 feat(convert): optionally retain original files 2026-07-16 08:27:24 +07:00
zarzet efa3ff3d66 perf(playback): batch local playlist resolution 2026-07-16 08:09:12 +07:00
zarzet 10d59bc2b6 perf(library): trim startup lookup index 2026-07-15 22:17:17 +07:00
zarzet 79bb3214ae perf(library): streamline search and grouped counts 2026-07-15 22:16:10 +07:00
zarzet cd6cf05227 refactor(providers): remove dead lookup/history APIs and dedup provider resolution
Delete the verbatim-duplicate LocalLibraryLookup class + provider, the unused
localLibrarySummaryProvider, notifier search()/getCount()/existsInLibrary
wrapper, and the now-orphaned LibraryDatabase.search(); the notifier's async
getById/findExisting lookups (the real ones) stay. Drop dead
download-history removeBySpotifyId/getDatabaseCount and the orphaned
history deleteBySpotifyId, and fold the byte-identical addToHistory/
adoptNativeHistoryItem into one _persistHistoryItem keeping distinct log text.
Delete dead extension_provider members (ensureSpotifyWebExtensionReady,
enabledExtensions, getExtension, resolveProviderDisplayName, URLHandler.matchesURL,
Extension.hasBrowseCategories) and collapse resolveEffectiveDownloadService/
MetadataProvider into one predicate-parameterized helper.
2026-07-14 18:50:52 +07:00
zarzet 4e0cae9c20 refactor: extract shared helpers for repeated low-level patterns
Dart:
- notification_service: single _details() builder replaces 13 copies
  of the NotificationDetails block
- platform_bridge: _invokeMap() for 34 invoke+decode call sites,
  _cachedInvoke() unifies the three TTL/in-flight cache scaffolds
- ffmpeg_service: _promoteTempOutput(), _appendCoverInputArgs(),
  single _writeReplayGainTags() and _convertToLossless() for the
  ALAC/FLAC twins
- sqlite_helpers.dart: shared openAppDatabase/path-key/migration
  helpers for the three database classes
- library_collections: parametrized wishlist/loved/favorite CRUD
- extension_provider: one predicate-based replacedBuiltIn* lookup

Go:
- extension runtime: parseGojaHeaders/coerceGojaBody/doExtensionHTTP
  shared by httpGet/httpPost/httpRequest/shortcuts/fetch
- exports_metadata: applyAudioMetadataToResult + successMethodJSON,
  APE edit path reuses audioMetadataFromEditFields
- lyrics: lrclibGet() for both LRCLib fetchers
- extension_store: drop hand-rolled strings helpers
2026-07-12 14:10:23 +07:00
zarzet ee5ab1a751 fix(queue): resolve local album tracks by album_key
Load queue local album tracks from album_key when available and fall back
to album/artist matching with empty album_artist handling. Thread album_key
through grouped local album rows for more reliable navigation.
2026-06-30 06:20:37 +07:00
zarzet b2074dfd02 feat: cap bit depth/sample rate on lossless conversion + WAV/AIFF
- LosslessConversionQuality model with bit depth/sample rate caps,
  applied only when they reduce source quality
- FFmpegService probes sample rate and appends codec-specific args
  (-ar, -sample_fmt, -bits_per_raw_sample) for FLAC/ALAC/WAV/AIFF
- Batch + single-track convert sheets expose quality cap options
- Persist real converted bit depth/sample rate to history/library DB
- track_metadata: recognize and convert to WAV/AIFF targets
- convertedAudioQualityLabel reflects actual output quality
2026-06-29 06:46:19 +07:00
zarzet c1c0494912 perf: optimize queue/library pagination, counts, scan, and extension runtime
- Queue/Library: switch from growing-limit refetch to offset-based append pagination per filter/search/sort, with page cache reassembly and protected-page eviction to avoid top-of-list gaps

- Queue/Library: watch only the active filter page instead of all/singles/albums simultaneously; filter mode follows PageView swipe and tab tap

- library_database: combine three union-based count queries into a single round-trip

- library_scan: parallel scan of non-CUE audio files with a bounded 2-4 worker pool, preserving order, progress, and cancellation; CUE handling stays sequential

- extension_manager: cache compiled goja.Program per extension and RunProgram per isolated download instead of re-reading and re-parsing index.js
2026-06-26 22:39:22 +07:00
zarzet b8b670642c feat(audio): add WAV and AIFF support + settings-style metadata menu
WAV/AIFF: library scan, quality probe, native tag read/write via embedded ID3 chunk (RIFF id3 / AIFF ID3), cover art, ReadFileMetadata, ExtractLyrics, and FLAC<->WAV/AIFF conversion (PCM, bit-depth preserved via ffprobe). Treat WAV/AIFF as lossless across all convert sheets (no bitrate picker, Lossless labels) via isLosslessConversionTarget. Native MIME maps for SAF. Redesign the track metadata three-dot menu to a settings-style grouped card with a single divider above Share.
2026-06-12 21:10:37 +07:00
zarzet 2a2d817314 feat: add AAC lossy target and toggle for Apple Music eLRC word sync
The HIGH-quality lossy format picker can now produce an AAC/M4A 320 kbps output alongside MP3 and Opus. FFmpegService.convertM4aToLossy/convertAudioFormat, the Dart queue pipeline, the Kotlin finalizer, and the library database format helper all route .m4a through a unified aac codec path and tag the resulting file with the M4A metadata writer. The Lossy Format setting gains a new option, and the track metadata convert dialog lists AAC next to the other targets.

Apple Music lyrics gain a 'eLRC word sync' switch (default off). When disabled the pax-to-LRC formatter strips inline word timestamps, producing line-synced LRC that is safer for players that choke on eLRC; enabling it restores the previous word-by-word behaviour. The change propagates through SetLyricsFetchOptions and invalidates the global lyrics cache on toggle.

Broad l10n migration: roughly 400 previously hardcoded English strings across queue, settings, track metadata, repo, audio analysis, setup and extension screens now live in the ARB catalog, with matching plural/placeholder forms. No behaviour change beyond localisation. Existing and new unit tests (lyrics eLRC toggle and Dart settings round-trip) pass.
2026-05-12 02:23:04 +07:00
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 d664d46ca4 feat: detect FLAC/ALAC/EAC3/AC3/AC4 codecs inside MP4 containers
GetM4AQuality now recognizes fLaC, alac, ec-3, ac-3, and ac-4 sample entries and parses the MP4 FLACSpecificBox so library entries carry the real codec rather than the container extension. The AudioQuality struct exposes Codec and Bitrate fields (with an estimator for compressed streams), and ReadFileMetadata publishes format + audio_codec so Flutter and Kotlin can make format decisions based on the actual stream.

Downstream: library_scan labels M4A-family items as flac/alac/eac3/ac3/ac4/m4a, zeroes the bitrate for lossless formats, and the filter UI + quality badges use the codec-derived format instead of only the file extension. Scans and SAF importers also accept .mp4 and .aac file extensions. New unit tests cover codec name mapping and MP4 FLACSpecificBox decoding.
2026-05-10 22:14:47 +07:00