Commit graph

5 commits

Author SHA1 Message Date
BoulderBadgeDad
d430dd8b71 #903: real source wiring for the export MBID waterfall
Phase 4. core/exports/export_sources.py supplies the real I/O behind each waterfall source
and assembles resolve_fn: cache -> DB (tracks.musicbrainz_recording_id via text match) ->
file tag (UFID/musicbrainz_trackid) -> live MusicBrainz match_recording. Every source is
fail-safe (any error -> None -> fall through, export never breaks). A fresh non-cache hit is
written back to the persistent cache so the same song is free next export. Sources are
injectable; build_resolve_fn wiring (cache short-circuit + write-back) is unit-tested. 4 tests.
2026-06-22 20:35:20 -07:00
BoulderBadgeDad
42ff13d517 #903: persistent recording-MBID cache + export orchestrator
Phase 3. Additive backbone for the export job:
- mb_recording_cache table (IF NOT EXISTS) + core/exports/recording_mbid_cache.py: persistent
  (artist,title)->recording_mbid cache, mirrors album_mbid_cache (lazy DB, error-degrades to
  miss). The MusicBrainz tail is ~1 req/s, so a resolved MBID is remembered once and reused
  across every export/playlist.
- core/exports/playlist_export.py: resolve_playlist_tracks(tracks, resolve_fn) — walks tracks,
  dedups repeated songs within a run (resolve once), builds the ordered pseudo-playlist, tallies
  live stats (resolved/unmatched/deduped/by_source). Pure (I/O injected via resolve_fn + progress
  callback), so dedup + accounting are unit-tested with no DB/network. 5 tests.

No wiring into runtime yet; nothing existing touched except the additive table.
2026-06-22 20:31:35 -07:00
BoulderBadgeDad
fb9d88ea6a #903: playlist export to ListenBrainz — pure cores (JSPF builder + MBID resolver)
Phase 1 of exporting mirrored playlists to ListenBrainz. Two pure, fully-tested seams,
zero runtime wiring yet (additive, no regression):

- core/exports/jspf_export.py: build_jspf(title, tracks) -> ({"playlist": {...}}, summary).
  LB's POST /1/playlist/create requires every track to carry a string identifier
  'https://musicbrainz.org/recording/<mbid>' (text-only tracks are rejected), so tracks
  without a valid recording-MBID UUID are dropped and counted in the coverage summary.
- core/exports/mbid_resolver.py: resolve_recording_mbid(artist, title, sources) — the
  cheapest-first waterfall (cache -> DB -> file tag -> MusicBrainz) as a pure function over
  injected (label, fn) sources. Short-circuits expensive lookups, treats a raising source
  as a miss (one flaky MB call can't fail the export), reports the resolving source label.

API spec confirmed against LB docs: POST /1/playlist/create, 'Authorization: Token <t>',
{"playlist": {"title", "track": [{"identifier": "<mb recording url>", title, creator, album}]}}.

13 tests.
2026-06-22 20:25:44 -07:00
BoulderBadgeDad
a789fb71c0 Library export: export the whole library roster too (corruption's request)
Extends the watchlist export to the full library. The exporter is now general
(core/exports/artist_export.py, renamed from watchlist_export) — adds tidal/qobuz
links and an extra_fields passthrough, so the library export also carries
lastfm/genius URLs + soul_id, and an optional "library counts" toggle adds owned
album/track counts per artist.

- GET /api/library/artists/export?format=&links=&contents= — pulls every artists
  row, normalizes onto the canonical *_artist_id keys, optionally GROUP-BY counts
  for album/track totals.
- The export modal is now openArtistExportModal(scope): "Export Library" button in
  the library header + the existing "Export" on the watchlist bar (a thin wrapper).
  Library mode shows the extra "library counts" toggle.

Tests (11): builder across formats + the new tidal/qobuz links + extra_fields
columns; watchlist + library endpoint wiring. 64 integrity green; ruff clean.
2026-06-11 22:59:21 -07:00
BoulderBadgeDad
f8652c106b Watchlist: export the roster to JSON / CSV / text (corruption's request)
An "Export" button on the watchlist filter bar opens a modal (same aesthetic as the
artist DB-record inspector) to export your whole watchlist roster — each artist's
name + source IDs (spotify / musicbrainz / deezer / discogs / itunes / amazon),
with an optional "external links" toggle that adds the discography URLs built from
those IDs. Live preview, copy, and download in the chosen format.

- core/exports/watchlist_export.py: pure builder (json/csv/txt + links, present-IDs
  only, deterministic columns) — the single source of truth, fully unit-tested.
- GET /api/watchlist/export?format=&links= shapes the roster + returns it (with
  X-Export-Count / X-Export-Ext headers for the modal).
- Frontend reuses the DB-record helpers (_jsonSyntaxHighlight / _arecCopy).

Tests (8): builder across json/csv/txt, links on/off, present-ids-only, empty +
bad-format fallback, mime/ext, and endpoint wiring. ruff clean; 64 integrity green.

Scoped to the watchlist for v1; library-wide export + a "library contents"
(owned albums/tracks) option are natural follow-ups.
2026-06-11 22:48:58 -07:00