Commit Graph

1161 Commits

Author SHA1 Message Date
zarzet
3fd13e9930 fix(ui): match GridSkeleton cover height with actual album cards 2026-03-27 04:39:29 +07:00
zarzet
0b20cb895e fix: conditionally show cover header in artist skeleton and add showCoverHeader param to ArtistScreenSkeleton 2026-03-27 04:35:22 +07:00
zarzet
8979210804 fix: null check crash in SpectrogramView when spectrum loaded from PNG cache 2026-03-27 04:24:19 +07:00
zarzet
e9b24712c5 feat: cache spectrogram as PNG for instant loading on subsequent views 2026-03-27 04:21:11 +07:00
zarzet
3d6e5615fa Revert "docs: move badges below screenshots in README"
This reverts commit 198ed5ce6f.
2026-03-27 03:56:57 +07:00
zarzet
fc7220b572 docs: update VirusTotal hash for v4.1.0 2026-03-27 03:54:31 +07:00
zarzet
198ed5ce6f docs: move badges below screenshots in README 2026-03-27 03:53:31 +07:00
zarzet
b48462a945 fix: add artist_album_flat case to SAF relative output dir builder 2026-03-26 18:31:00 +07:00
zarzet
0f327cd1f6 feat: add flat singles folder option (Artist/song.flac without Singles subfolder) v4.1.0 2026-03-26 18:15:37 +07:00
zarzet
4f2e677e8b fix: improve skeleton visibility and artist header for light mode 2026-03-26 17:32:54 +07:00
zarzet
79a69f8f70 chore: clean up codebase 2026-03-26 16:43:56 +07:00
zarzet
bf0f4bdf3e fix: store URL input flash on startup and FLAC metadata fallback for mismatched files
Load saved registry URL before first state update to prevent brief
flash of the setup screen when the store tab initializes.

Add Ogg/Opus fallback in readFileMetadata when FLAC parsing fails,
handling files saved with .flac extension that contain opus data.
2026-03-26 16:26:14 +07:00
zarzet
5e1cc3ecb5 refactor: extract YouTube download to ytmusic extension and fix UI issues
Remove built-in YouTube/Cobalt download pipeline from Go backend and
Dart frontend. YouTube downloading now requires the ytmusic-spotiflac
extension (with download_provider capability).

Go backend:
- Delete youtube.go (745 lines) and youtube_quality_test.go
- Remove DownloadFromYouTube, IsYouTubeURLExport,
  ExtractYouTubeVideoIDExport from exports.go
- Remove YouTube routing from DownloadTrack and DownloadByStrategy

Dart frontend:
- Remove YouTube from built-in services, bitrate settings, quality UI
- Remove youtubeOpusBitrate/youtubeMp3Bitrate from settings model
- Add migration 7: default service youtube -> tidal
- Remove YouTube l10n keys from all 14 arb files and regenerate
- Update _determineOutputExt to handle opus_/mp3_ quality strings
- Add SAF opus/mp3 metadata embedding in unified branch
- Fix TweenSequence assertion crash (t outside 0.0-1.0)
- Fix store URL TextField styling consistency

Extension changes (gitignored, in extension/YT-Music-SpotiFLAC/):
- Add download_provider type, qualityOptions, network permissions
- Implement checkAvailability and download via SpotubeDL/Cobalt
2026-03-26 16:17:57 +07:00
zarzet
d4b37edc2f feat: add animation utilities and fix regressions in UI refactor
- Add animation_utils.dart with skeleton loaders, staggered list animations,
  animated checkboxes, badge bump, download success overlay, and shared
  page route helper
- Replace CircularProgressIndicator with shimmer skeleton loaders across
  album, artist, playlist, search, store, and extension screens
- Unify page transitions via slidePageRoute (MaterialPageRoute) for
  Android predictive back gesture support
- Extract AnimatedSelectionCheckbox with configurable unselectedColor
  to preserve original transparent/opaque backgrounds per context
- Add swipe-to-dismiss on download queue items with confirmDismiss
  dialog for active downloads to prevent accidental cancellation
- Add Hero animations for cover art transitions between list and detail
- Add AnimatedBadge bump on navigation bar badge count changes
- Add DownloadSuccessOverlay green flash on download completion
- Restore fine-grained ref.watch(.select()) in _CollectionTrackTile
  to avoid full list rebuilds on download history changes
- Fix DownloadSuccessOverlay re-flashing on widget recreation by
  initialising _wasSuccess from initial widget state
- Remove orphan Hero tag in search_screen that had no matching pair
- Chip borderRadius updated from 8 to 20 for consistency
2026-03-26 13:38:07 +07:00
zarzet
9483614bc7 feat: cache audio analysis results and fix total samples metric 2026-03-26 02:17:18 +07:00
zarzet
a73f2e1a13 feat: auto-select recommended download service based on content source 2026-03-26 01:44:11 +07:00
zarzet
091e3fadd9 feat: add audio quality analysis widget and fix USLT lyrics detection 2026-03-26 01:11:29 +07:00
zarzet
5340ca7b16 chore: bump version to 4.1.0+117 2026-03-25 23:23:14 +07:00
zarzet
85d3e58a26 fix: hi-res cover art for Tidal/Qobuz and album metadata override 2026-03-25 23:17:45 +07:00
zarzet
1125c757fe fix: remove unintended home reset on tab switch 2026-03-25 22:33:04 +07:00
zarzet
66d714d368 fix: unify search bar, filter chips, tab navigation, and clean up comments 2026-03-25 22:27:22 +07:00
zarzet
49c2501fbc refactor: use pointer returns and unified forceRefresh in ExtensionStore 2026-03-25 21:47:31 +07:00
zarzet
e487817f21 feat: add sorting options for search results 2026-03-25 21:40:36 +07:00
zarzet
d8bbeb1e67 perf: use Tidal/Qobuz metadata for Deezer track resolution 2026-03-25 21:18:47 +07:00
zarzet
9693616645 fix: route tidal/qobuz items from Recent Access to built-in screens instead of extension screens 2026-03-25 20:50:33 +07:00
zarzet
0423e36d34 chore: bump version to 3.9.1+116 2026-03-25 20:08:53 +07:00
zarzet
c8d605fdee fix: add ValueListenableBuilder for embedded cover refresh and localize hardcoded queue strings 2026-03-25 20:05:24 +07:00
zarzet
03fd734048 perf: lazy extension VM init, incremental startup maintenance, and UI optimizations
- Defer extension VM initialization until first use with lockReadyVM() pattern to eliminate TOCTOU races and reduce startup overhead
- Add validateExtensionLoad() to catch JS errors at install time without keeping VM alive
- Teardown VM on extension disable to free resources; re-init lazily on re-enable
- Replace full orphan cleanup with incremental cursor-based pagination across launches
- Batch DB writes (upsertBatch, replaceAll) with transactions for atomicity
- Parse JSON natively on Kotlin side to avoid double-serialization over MethodChannel
- Add identity-based memoization caches for unified items and path match keys in queue tab
- Use ValueListenableBuilder for targeted embedded cover refreshes instead of full setState
- Extract shared widgets (_buildAlbumGridItemCore, _buildFilterButton, _navigateWithUnfocus)
- Use libraryCollectionsProvider selector and MediaQuery.paddingOf for fewer rebuilds
- Simplify supporter chip tiers and localize remaining hardcoded strings
2026-03-25 19:55:02 +07:00
zarzet
da9d64ccfd chore: update VirusTotal hash in README 2026-03-25 17:15:36 +07:00
zarzet
02e64b7a3c Merge remote-tracking branch 'origin/main' 2026-03-25 17:12:06 +07:00
zarzet
a435009d4d fix(qobuz): skip SongLink when ISRC is already available 2026-03-25 17:09:54 +07:00
github-actions[bot]
9ca73a99a6 chore: update AltStore source to v3.9.0 2026-03-25 09:29:51 +00:00
zarzet
4974284760 fix(l10n): consolidate Crowdin locale files and fix ICU plural warnings
- Replace app_es-ES.arb, app_pt-PT.arb, app_tr-TR.arb (hyphen format)
  with properly named app_es_ES.arb, app_pt_PT.arb, app_tr.arb
- Fix @@locale values to match Flutter filename convention (underscore)
- Fix ICU plural syntax: remove redundant 'one {}' before '=1{...}'
  in es_ES, pt_PT, tr translations
- Regenerate l10n output files
v3.9.0
2026-03-25 16:12:37 +07:00
Zarz Eleutherius
a0306bd345 Merge pull request #258 from zarzet/l10n_dev
New Crowdin updates
2026-03-25 16:08:16 +07:00
zarzet
ea7e594c68 Merge remote-tracking branch 'origin/dev' into l10n_dev
# Conflicts:
#	lib/l10n/arb/app_es-ES.arb
#	lib/l10n/arb/app_id.arb
#	lib/l10n/arb/app_pt-PT.arb
#	lib/l10n/arb/app_tr-TR.arb
2026-03-25 16:08:10 +07:00
Zarz Eleutherius
d00a84f1b9 New translations app_en.arb (Indonesian) 2026-03-25 16:02:56 +07:00
Zarz Eleutherius
58b6203681 New translations app_en.arb (Chinese Simplified) 2026-03-25 16:02:54 +07:00
Zarz Eleutherius
d299144c47 New translations app_en.arb (Russian) 2026-03-25 16:02:53 +07:00
Zarz Eleutherius
40b224e5a1 New translations app_en.arb (Dutch) 2026-03-25 16:02:51 +07:00
Zarz Eleutherius
7021e5493f New translations app_en.arb (Japanese) 2026-03-25 16:02:49 +07:00
Zarz Eleutherius
68bbc8a259 New translations app_en.arb (German) 2026-03-25 16:02:47 +07:00
zarzet
be94a59441 chore: bump version to 3.9.0+115, add new translators
- Bump app version from 3.8.8 to 3.9.0 (build 115)
- Add 4 new Crowdin translators: unkn0wn (Indonesian), lunching1272
  (Chinese Simplified), Сергей Ильченко (Russian), Girl-lass (Chinese
  Simplified)
2026-03-25 15:47:08 +07:00
zarzet
3a73aee1b7 feat: add home feed provider setting, fix Qobuz cover URL propagation
- Add homeFeedProvider field to AppSettings with picker UI in extensions page
- Update explore_provider to respect user's home feed provider preference
- Add normalizeCoverReference() and normalizeRemoteHttpUrl() to filter
  invalid cover URLs (no scheme, no host, protocol-relative)
- Apply cover URL normalization across all screens and providers to
  prevent 'no host specified in URI' errors from Qobuz
- Propagate CoverURL from QobuzDownloadResult through Go backend so
  cover art is available even when request metadata is incomplete
2026-03-25 15:46:22 +07:00
zarzet
c91154ea3e feat: add built-in search provider in settings, fix bottom sheet overflow 2026-03-25 15:46:12 +07:00
zarzet
4f365ca7fe feat: add built-in Tidal/Qobuz search with recommended service picker
- Add SearchAll() for Tidal and Qobuz in Go backend (tracks, artists, albums)
- Add searchTidalAll/searchQobuzAll platform routing for Android and iOS
- Add Tidal/Qobuz options to search provider dropdown in home tab
- Show (Recommended) label and auto-select service in download picker
2026-03-25 13:52:57 +07:00
zarzet
98fdc0ed7c feat: restore Tidal HIGH (AAC 320kbps) lossy quality option (closes #242)
Requested by @okinaau in issue #242 — brings back the ability to
download tracks in lossy format for users on low storage devices.

HIGH quality fetches the AAC M4A stream directly from the Tidal server
(no lossless download + re-encode), then converts to MP3 or Opus via
FFmpeg based on the tidalHighFormat setting (mp3_320, opus_256, or
opus_128).

- go_backend/tidal.go: restore outputExt .m4a, filename logic,
  duplicate-check guard, HIGH M4A lyrics/LRC handling, and
  bitDepth=0/sampleRate=44100 for HIGH quality result
- settings.dart + settings.g.dart: re-add tidalHighFormat field
  (default mp3_320) with JSON serialization
- settings_provider.dart: re-add setTidalHighFormat(), remove
  migration that force-migrated HIGH to LOSSLESS
- download_queue_provider.dart: restore HIGH conversion logic for
  both SAF and non-SAF paths using FFmpegService.convertM4aToLossy
- download_settings_page.dart: restore Lossy 320kbps quality tile,
  format sub-picker tile, _getTidalHighFormatLabel helper, and
  _showTidalHighFormatPicker bottom sheet
- l10n: add 10 keys (downloadLossy320, downloadLossyFormat,
  downloadLossy320Format, downloadLossy320FormatDesc, downloadLossyMp3,
  downloadLossyMp3Subtitle, downloadLossyOpus256/Subtitle,
  downloadLossyOpus128/Subtitle) to ARB and all 13 generated files
2026-03-22 23:33:32 +07:00
zarzet
12be560cb8 feat: add M4A metadata/cover embed support across all Flutter screens
Add FFmpegService.embedMetadataToM4a() for writing tags and cover art
into M4A files via FFmpeg. Fix two bugs in the same function:
- Remove '-disposition:v:0 attached_pic' which is only valid for
  Matroska/WebM containers and causes FFmpeg to error on MP4/M4A
- Apply same fix to _convertToAlac which had the identical issue

Add M4A handling (isM4A branch) to all four embed call-sites:
track_metadata_screen (lyrics embed, re-enrich, edit metadata sheet,
format conversion), queue_tab, local_album_screen, and
downloaded_album_screen.

Add 'LYRICS'/'UNSYNCEDLYRICS' to _mapMetadataForTagEmbed so existing
lyrics survive a re-enrich cycle on M4A/MP3/Opus files.

Preserve existing lyrics before overwriting tags in the edit metadata
sheet (best-effort readFileMetadata before FFmpeg pass).

Extract mergePlatformMetadataForTagEmbed() into lyrics_metadata_helper
to deduplicate the identical metadata-mapping loops that existed in
queue_tab, local_album_screen, downloaded_album_screen, and
track_metadata_screen.

Wire ensureLyricsMetadataForConversion into the format conversion path
in track_metadata_screen so lyrics are carried through conversions.

Add ISRC and LABEL/ORGANIZATION mappings to _convertToM4aTags.
2026-03-22 23:01:32 +07:00
zarzet
4cf885a52e feat: populate M4A metadata in ReadFileMetadata and library scan
ReadFileMetadata now fills all tag fields (title, artist, album, ISRC,
lyrics, genre, label, copyright, composer, comment, track/disc number)
for M4A files using the new ReadM4ATags helper, matching the existing
behavior for FLAC, MP3, and Ogg.

scanM4AFile reads tags via ReadM4ATags instead of falling back to the
filename, and applies applyDefaultLibraryMetadata for missing fields
(consistent with FLAC/MP3 scan path).

Remove the '&& ext != ".m4a"' guard in cover cache so M4A cover art
is extracted and cached during library scans.
2026-03-22 23:00:55 +07:00
zarzet
c57c8a4267 feat: implement full M4A tag read engine with atom path fallback and freeform fix
Add ReadM4ATags() that parses all standard iTunes atoms (title, artist,
album, album artist, date, genre, composer, comment, copyright, lyrics,
track/disc number) and freeform '----' atoms (ISRC, label, lyrics).

Fix two pre-existing bugs in the M4A atom traversal:
- findM4AIlstAtom: now tries moov>udta>meta>ilst first, then falls back
  to moov>meta>ilst so files from Tidal/Qobuz/Apple Music are handled
- readM4AFreeformValue: 'name' atom payload is raw UTF-8 after 4-byte
  flags, not a nested 'data' atom; fix reads it directly so ISRC/label
  freeform tags are no longer silently dropped

Refactor extractLyricsFromM4A and extractCoverFromM4A to reuse the new
helpers (findM4AIlstAtom, readM4ADataAtomPayload) instead of duplicating
the atom traversal logic. Add extractAnyCoverArtWithHint M4A case that
previously returned a hardcoded 'not yet supported' error.
2026-03-22 23:00:42 +07:00
Zarz Eleutherius
2ca6c737c0 Update README 2026-03-22 22:46:03 +07:00