Commit Graph
469 Commits
Author SHA1 Message Date
zarzet 4fa0ab860b chore(deps): upgrade Flutter Go and Android dependencies 2026-08-27 10:42:14 +07:00
zarzet 1a7c20111a feat(library): add lyrics filtering and metadata actions 2026-08-27 01:44:06 +07:00
zarzet 5e8608cef9 fix(extensions): accept artist tracks alias 2026-08-26 23:08:53 +07:00
zarzet 747dea886c fix(runtime): bound resources and quarantine stalled extensions 2026-08-26 21:09:05 +07:00
zarzet 0e3fca9967 perf(downloads): stream concurrent native queue work 2026-08-26 18:37:56 +07:00
zarzet f8bd1b5931 feat(extensions): add resilient transfer runtime 2026-08-26 18:35:44 +07:00
zarzet 8f7b9ba47d perf(metadata): coalesce Deezer enrichment requests 2026-08-26 18:31:53 +07:00
zarzet 5f8e0da148 fix(metadata): remove forced download attribution 2026-08-26 18:31:39 +07:00
zarzet 89d39c4074 fix(metadata): show readable SAF file locations 2026-08-21 01:02:02 +07:00
zarzet 8e3dbf3c23 test(audio): verify tagged PCM playback compatibility 2026-08-19 14:33:03 +07:00
zarzet ea63291db8 refactor(metadata): move cover resolution to extensions 2026-08-19 13:12:29 +07:00
zarzet bdb10a2ae6 chore(deps): update Flutter and Go dependencies 2026-08-19 11:59:28 +07:00
zarzet 4fef287a7b feat(metadata): add download attribution to comments 2026-08-18 21:17:36 +07:00
zarzet fd12ad2dbe fix(metadata): preserve source release fields across providers 2026-08-18 20:57:11 +07:00
zarzet 958e0db4e8 fix(download): accept matching tracks across releases 2026-08-18 20:50:39 +07:00
zarzet a7423b7f89 feat(metadata): edit release tags and render UPC barcodes 2026-08-18 19:29:12 +07:00
zarzet 290923e88f feat(tagging): write release identity tags 2026-08-18 19:04:26 +07:00
zarzet 48883d51d4 feat(extensions): preserve provider metadata 2026-08-18 12:28:35 +07:00
zarzet 7fc27e8314 perf(library): stream scans and optimize queue queries 2026-08-18 11:03:05 +07:00
zarzet 2e115308b7 feat(filename): add provider traceability tags 2026-08-17 20:51:27 +07:00
zarzet 8333069b95 fix(lyrics): use normalized Genius response (#517) 2026-08-11 17:54:15 +07:00
Abelardo Ramirez 48dbe3679d fix(download): treat a short clean EOF as resumable, not complete
Follow-up to #491, which was fixed in 4.8.5 by making mid-download
resume opt-in (safe default: fail and delete the staged file, since
switching networks can route a stable URL to a different CDN object).

That fix only changes what happens on a *real* read error. It doesn't
help if a transport surfaces a mid-transfer connection drop as a plain
io.EOF instead of io.ErrUnexpectedEOF - fileDownload's copyBody loop
still breaks out and promotes the file on any clean EOF, regardless of
whether written bytes actually reached Content-Length. This app's
uTLS-based client (used for TLS-fingerprint spoofing) is exactly the
kind of custom transport where that guarantee isn't necessarily upheld.

Now a clean EOF short of a known Content-Length is routed through the
same resume-or-fail path as a real read error, so it respects the same
opt-in `resume` option: fails and cleans up the staged file by default,
or resumes via Range/If-Range when the caller explicitly requested it
and the server provided a validator.

extension_runtime_file_download_integrity_test.go adds three tests:
- fails by default even when a validator is present (matches the
  opt-in policy from 4.8.5)
- resumes correctly when `resume: true` is passed
- fails and cleans up when there's no validator at all

All three fail on the pre-fix code and pass with it.

Note: fileDownloadChunked's unknown-total-size path (used for
YouTube's CDN) has an analogous but harder-to-fix ambiguity - without
a known length there's no way to distinguish a legitimately short
final chunk from a truncated one - left as a follow-up.
2026-08-11 17:29:48 +07:00
Abelardo Ramirez bed95efb29 fix(playlists): thread the source provider id through to recent playlists
Fixes #368.

Reopening a Spotify playlist from "recent access" showed no tracks,
while the first view (right after pasting the URL) worked fine.

Root cause traced across two layers:

1. Go: ExtURLHandleResult (the parsed shape of an extension's handleUrl()
   return value) never captured a top-level `id` for the handled
   resource. Track/album/artist results carry their own id inside their
   nested metadata, but a plain playlist result has no such object, so
   its id was silently dropped everywhere from the goja parser through
   to the JSON the Dart side receives.

2. Dart: TrackState had nowhere to put that id even if it existed
   (only playlistName), so recording a "recent" playlist access stored
   the playlist's *name* as if it were its id. Reopening it later fed
   that name into PlaylistScreen's provider-guessing logic
   (legacyProviderIdFromResourceId, which only recognizes legacy
   "provider:id" prefixes), which naturally failed and fell through to
   a hardcoded Deezer metadata fetch using a Spotify playlist's name as
   the resource id - guaranteed to return nothing.

Fix, matching the "generic API, not per-provider checks" architecture
in CONTRIBUTING.md:

- go_backend/extension_provider_wrapper.go: add ExtURLHandleResult.ID.
- go_backend/extension_goja_convert.go: parse it from the handler's
  return value.
- go_backend/exports_extensions.go: surface it in the JSON response.
- lib/providers/track_provider.dart: add TrackState.playlistId,
  populated from the response's `id` field for playlist results.
- lib/screens/home_tab.dart: record the real playlist id (falling back
  to the name only if a provider never supplies one) and pass both the
  id and the already-known provider id forward to PlaylistScreen.
- lib/screens/playlist_screen.dart: PlaylistScreen gains a
  metadataProviderId param that takes priority over guessing from the
  id's shape.
- lib/screens/home_tab_recent.dart: pass the recent-access entry's
  stored providerId through when reopening a playlist.
- lib/utils/provider_resource_ids.dart: extract the
  known-id-vs-guessed-id preference into a small, directly testable
  resolvePreferredMetadataProviderId helper.

Note: this fixes the common case (an extension already reported its
own id as the source provider for the URL it handled). If a provider
never supplies an id for its handleUrl() playlist result, playlistId
stays null and behavior is unchanged from before this fix - no
regression, just not a complete fix for that narrower case, since
that would require changes in extension-side JS code outside this
repo.

Tests:
- go_backend/extension_goja_convert_url_handle_test.go: the new ID
  field round-trips from a handler's return value, and stays empty
  when the handler doesn't supply one.
- test/provider_resource_ids_test.dart: resolvePreferredMetadataProviderId
  prefers a known provider id, falls back to the legacy-prefix guess,
  treats a blank known id as unknown, and returns null for the
  unprefixed-id-with-no-known-provider case from #368 itself.

Verification: go build/vet/test and flutter analyze/test all green.
2026-08-11 17:29:41 +07:00
zarzet bec9e33c94 feat(metadata): review batch tag enrichment #511 2026-08-11 16:40:06 +07:00
zarzet 6ad993e27d fix(lyrics): respect extension provider selection #511 2026-08-11 15:46:33 +07:00
zarzet d5bfc1e365 fix(metadata): prompt verification during autofill #511 2026-08-11 15:38:39 +07:00
zarzet 7972fd64a4 chore(deps): update Flutter and Go dependencies 2026-08-01 18:46:11 +07:00
zarzet b3bbe93c1e fix(metadata): preserve WAV and AIFF tags 2026-08-01 15:33:20 +07:00
zarzet dc58353c07 fix(lyrics): reject empty embedded payloads 2026-08-01 15:02:03 +07:00
zarzet 66928e259d fix(session): enforce provider retry modes 2026-07-30 13:31:54 +07:00
zarzet 113ad7ed9b fix(session): block challenged session generations 2026-07-30 13:09:42 +07:00
zarzet 177a176218 fix(session): honor canonical gateway error contracts 2026-07-30 12:59:48 +07:00
zarzet 169a811162 fix(session): coordinate verification across parallel downloads 2026-07-30 11:39:37 +07:00
zarzet e7fbaf7b75 fix(download): suffix only colliding quality variants 2026-07-29 02:43:35 +07:00
zarzet 452d9a76cb fix(metadata): verify maximum cover candidates 2026-07-29 02:42:46 +07:00
zarzet 8dcf7fa998 fix(storage): recover from unwritable download folders 2026-07-29 02:03:09 +07:00
zarzet fb538e9517 feat(metadata): choose extension for online autofill 2026-07-28 15:18:10 +07:00
zarzet 385afe290a fix(download): reject mismatched provider tracks 2026-07-28 14:15:44 +07:00
zarzet b4f239c692 fix(download): honor explicitly selected provider 2026-07-28 14:11:14 +07:00
zarzet 26570792a9 fix(library): persist lossless bitrate metadata 2026-07-28 00:50:20 +07:00
zarzet e8a3fc58c7 feat(metadata): show average bitrate for lossless files in the track screen 2026-07-27 01:20:38 +07:00
zarzet bd2bd85c9e refactor(go): split per-format scanners and incremental scan out of library_scan.go 2026-07-27 00:21:04 +07:00
zarzet 1b861f9ae8 refactor(go): split result/overlay helpers and output builders out of extension_fallback.go 2026-07-27 00:20:04 +07:00
zarzet 8a95f684d2 refactor(go): split deezer models, cache upkeep, and browse endpoints out of deezer.go 2026-07-27 00:18:45 +07:00
zarzet 358154c39e refactor(go): split lyrics provider config, LRC codec, and search matching out of lyrics.go 2026-07-27 00:16:18 +07:00
zarzet ea172ba17b refactor(go): split package validation and VM lifecycle out of extension_manager.go 2026-07-26 23:05:30 +07:00
zarzet 9dc44ecd88 refactor(go): split Ogg parsing and cover-art extraction out of audio_metadata.go 2026-07-26 22:39:58 +07:00
zarzet 2dbf8f6f9b refactor(go): split M4A/MP4 tagging out of metadata.go 2026-07-26 22:38:08 +07:00
zarzet 1cda88cafc perf(metadata): cache MusicBrainz ISRC lookups in the finalize path 2026-07-26 22:15:04 +07:00
zarzet 6fcfb67af2 perf(go): cut redundant signed-session writes, progress-callback churn, tombstone growth 2026-07-26 22:14:58 +07:00