Commit Graph
53 Commits
Author SHA1 Message Date
zarzet 7bf6f32802 fix(extensions): serialize verification callbacks 2026-07-28 14:09:06 +07:00
zarzet 7f277f7a88 fix(library): backfill local audio bitrates 2026-07-28 12:51:28 +07:00
zarzet 80d9d87870 perf(metadata): avoid SAF copies during quality probes 2026-07-28 01:50:41 +07:00
zarzet 26570792a9 fix(library): persist lossless bitrate metadata 2026-07-28 00:50:20 +07:00
zarzet 454ef53977 feat(library): add quality label display modes 2026-07-27 17:25:39 +07:00
zarzet e4cd52f29f feat(library): highlight high-bitrate quality badges 2026-07-27 16:58:58 +07:00
zarzet af6ca12e85 fix(download): swap local track variant actions 2026-07-27 14:31:39 +07:00
zarzet 9b4a960324 fix(home): keep search visible during provider changes 2026-07-27 14:24:57 +07:00
zarzet 1cdf4b4547 fix(library): retain records when deletion fails 2026-07-27 14:24:39 +07:00
zarzet 73846c40ca fix(download): complete quality variants reliably 2026-07-27 14:22:24 +07:00
zarzet 856e2cbb9e fix(audio): wait for complete analyzer metrics 2026-07-27 14:19:52 +07:00
zarzet 50c8f0e454 refactor(ffmpeg): remove dead conversion cluster and speculative decryption fields 2026-07-26 18:44:08 +07:00
zarzet c497449549 feat(library): M3U8 playlist import and export
The playlist import picker now also accepts .m3u/.m3u8; EXTINF entries
feed the same Deezer enrichment pipeline as CSV. Collection screens
(wishlist, loved, playlists) gain an export action that resolves each
track to its local file, writes SAF entries relative to the download
tree root so external players and DAP copies resolve them, skips
tracks without a file, and shares the .m3u8 via the system sheet.
2026-07-26 17:56:38 +07:00
zarzet 5f5186a23e fix(reenrich): preserve release identity in batch actions 2026-07-26 12:28:00 +07:00
zarzet c4abbf8743 fix(verification): recover signed-session bootstrap failures 2026-07-26 11:09:18 +07:00
zarzet a5f2760023 refactor(android): extract native finalization policies 2026-07-26 01:14:14 +07:00
zarzet 20081b14bd refactor(dart): extract queue search and media policies 2026-07-26 01:13:52 +07:00
zarzet 1cfda41ef2 feat(audio): use VHQ settings for SoXR resampling 2026-07-25 15:58:42 +07:00
zarzet 19787ed461 fix(config): refresh announcements and remote content 2026-07-25 15:43:25 +07:00
zarzet eabafecaeb fix(navigation): open download queue from snackbar actions 2026-07-24 22:19:03 +07:00
zarzet 01c08322c1 fix(extensions): preserve explicit track metadata 2026-07-24 21:32:13 +07:00
zarzet 4713be02de chore(settings): graduate native download worker 2026-07-24 12:23:36 +07:00
zarzet 89dcef8ffd test(native-worker): add Android regression gates 2026-07-24 12:16:41 +07:00
zarzet 0cbe51ad19 fix(analysis): render full-track spectrograms 2026-07-23 18:24:14 +07:00
zarzet 5856729e9a fix(library): persist records before download completion 2026-07-23 18:00:22 +07:00
zarzet c35b11a155 fix(library): preserve downloaded file records 2026-07-23 17:40:09 +07:00
zarzet 5481f8e8cf feat(ui): smooth download progress updates 2026-07-23 13:36:22 +07:00
zarzet cdbb0d6bd7 fix(notifications): restore verification alert sound 2026-07-23 13:23:54 +07:00
zarzet c9114fef01 fix(android): sanitize restored installation state 2026-07-23 10:39:31 +07:00
zarzet 480a96bd0e fix(queue): clarify and coalesce verification progress 2026-07-23 09:57:02 +07:00
zarzet f0988070bf fix(convert): simplify output names and harden writes 2026-07-22 15:56:07 +07:00
zarzet 87376ee73b fix(metadata): resolve audio format and local artwork 2026-07-22 15:55:30 +07:00
zarzet 4d77a0b178 fix(ui): honor disabled hero animations 2026-07-22 15:54:50 +07:00
zarzet b69f67a786 fix(download): name variants by measured quality 2026-07-16 09:20:35 +07:00
zarzet 0961c09a6d feat(download): preserve quality variants 2026-07-16 08:45:01 +07:00
zarzet 491998e5c5 feat(convert): optionally retain original files 2026-07-16 08:27:24 +07:00
zarzet 5763a087c9 perf(queue): persist and index incremental updates 2026-07-16 08:09:28 +07:00
zarzet 82ba586b72 feat(convert): support same-format lossless re-encoding 2026-07-15 22:03:32 +07:00
zarzet 17dbcc1338 perf(library): lazy-load collections and dispose queries 2026-07-15 21:32:43 +07:00
zarzet d20f569678 test(download): cover playlist_position in payload serialization test 2026-07-10 10:12:14 +07:00
zarzet d736e5aafe refactor(download): remove concurrent download option
The download API only permits one request at a time, so parallel
downloads are removed to avoid wasted/blocked API calls. Downloads
now always run sequentially (one track at a time).

- Drop concurrentDownloads from AppSettings + JSON serialization
- Remove setConcurrentDownloads and the settings UI (1-5 chips + warning)
- Strip optionsConcurrent* l10n keys from all ARBs and regenerate
- Rework queue worker into _processQueueSequential (single active download)
- Update marketing copy and adjust tests
2026-06-06 21:58:45 +07:00
zarzet 56a89c5fc6 fix: harden download errors and re-enrich sidecars 2026-06-02 00:58:40 +07:00
zarzet 1b8d6ce7fa feat: enhanced audio analysis with loudness, clipping, and spectral cutoff
Audio Analysis Enhancements:
- Display codec name and container format
- Show decoded sample format (s16, s32, fltp, etc.)
- Add LUFS integrated loudness measurement (broadcast standard)
- Add true peak measurement (dBTP)
- Detect and count clipping samples per channel
- Estimate spectral cutoff frequency (helps detect fake upscales)
- Show per-channel statistics (Peak, RMS, DR, Clip count)

UI Improvements:
- MetricChip now handles long text with ellipsis
- Constrained max width for better layout

Cache version bumped to 4 to force rescan with new metrics.
2026-05-14 16:28:49 +07:00
zarzet 60f1df1488 refactor: use audio_conversion_utils in downloaded_album_screen
- Replace inline format detection with convertibleAudioSourceFormat()
- Replace inline conversion rules with canConvertAudioFormat()
- Add unit tests for Dolby format detection and conversion rules
2026-05-14 15:49:27 +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 7845ac8be5 feat: show remote-config launch announcement on app start
Introduce AppRemoteConfigService which fetches a platform/version/locale-aware JSON payload from api.zarz.moe/v1/spotiflac-mobile/config and caches it in SharedPreferences. main_shell shows a one-shot announcement dialog (respecting dismissible, CTA, time window and version gates) when no update prompt is pending; dismissed IDs are persisted so each announcement surfaces only once.

Tweaks bundled in: the service health dot loses its blur halo in favour of solid Material 3 tones, and AppInfo gains the remote config endpoint constant. The share listener and SAF migration hook stay synchronous inside the post-frame callback so share-intent URLs never race the network-bound checks.

New unit tests cover the announcement CTA/active-window rules.
2026-05-11 01:37:10 +07:00
zarzet 81547013f9 fix: gate M4A to FLAC conversion on a codec probe in every branch
The SAF and local post-download branches used to rush an ffmpeg 'M4A to FLAC' remux whenever the output extension was .flac, which silently upscaled AAC or EAC3 streams into a lossless container. Each branch now mirrors the native worker by probing the primary audio codec before converting: lossless sources (and true FLAC-in-MP4 files) stay in their native container with the right extension, while genuine ALAC/WAV payloads still get remuxed.

Add an outputExt field to DownloadRequestPayload so the Go backend always knows the user-requested container, and use it together with _shouldRequestContainerConversion to pick the right behaviour for shouldPreserveNativeM4a and the Kotlin finalizer. Decryption descriptors no longer force M4A preservation on their own; the codec probe already makes that call correctly.
2026-05-11 00:52:02 +07:00
zarzet cfc8e699f3 perf: optimize native worker snapshot writes with delta mode
Replace full-queue snapshot writes on every progress tick with a
lightweight delta mode that only includes the active item.

- Progress tick (1s loop) now writes item_delta with only the
  active item instead of serializing the entire queue
- Full compact_items snapshots are reserved for important events:
  start, pause/resume/cancel, item boundaries, finish, and
  service stop/destroy
- Compact items omit large static fields (item_json, track_name,
  artist_name) that Dart already has from queue restore
- Snapshot always carries item_ids for adoption correlation
- Dart-side _applyAndroidNativeWorkerSnapshot handles item_delta
  as a single-item fallback when items array is absent
- Dart-side _tryAdoptAndroidNativeWorkerSnapshot reads item_ids
  as fallback when items is not present
- Add deferred SAF publish: native worker writes to cache, runs
  all finalization locally, then publishes once to SAF at the end
- Forward defer_saf_publish through DownloadRequestPayload
2026-05-05 03:17:38 +07:00
zarzet 6b342aeac6 feat: add experimental Android native download worker
Introduce a service-owned download worker that offloads the full
download-and-finalize pipeline to DownloadService on Android, keeping
downloads alive independently of the Flutter UI process.

Key changes:
- Extract SAF download logic from MainActivity into SafDownloadHandler
- Add NativeDownloadFinalizer for Kotlin-side decryption, format
  conversion, metadata embedding, ReplayGain, post-processing, and
  history persistence
- Extend DownloadService with native queue management (start, pause,
  resume, cancel) using coroutine-based worker with AtomicFile snapshots
- Add Dart-side orchestration: snapshot polling, run-id correlation,
  adoption on app restart, and fallback to Dart queue
- Forward embedReplayGain, tidalHighFormat, and postProcessingEnabled
  through Go backend DownloadRequest struct
- Add nativeDownloadWorkerEnabled setting with UI toggle
- Make DownloadQueueLookup collections unmodifiable
2026-05-05 02:41:00 +07:00
zarzet a4dc776bfb chore: update default lyrics providers and about links 2026-05-04 15:51:57 +07:00