Commit Graph

91 Commits

Author SHA1 Message Date
Jonathan 1d847e2aeb docs: add staged-download + atomic-import implementation plan
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 15:07:01 +02:00
Jonathan 2614e32f55 docs: add staged-download + atomic-import design (pipeline fix)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 14:59:55 +02:00
Jonathan 9f895cfc10 fix: search-now dedupes against an active job (match worker enqueue invariant)
The worker's enqueue_due refuses to enqueue when a non-terminal job
already exists for a release. The search-now route enqueued
unconditionally, so two clicks (or a click racing the worker sweep)
could create two concurrent pipelines for the same album, both
importing and colliding on LibraryItem's @@unique([artist,album]).

Mirror the worker's guard: no-op if a job in a non-terminal state
(anything other than imported/needs_attention) already exists for the
release.
2026-07-11 14:19:31 +02:00
Jonathan 7087b2780e feat: artist discography page + wanted page 2026-07-11 14:11:53 +02:00
Jonathan f7135b9046 feat: artists page with live-search follow picker 2026-07-11 14:08:10 +02:00
Jonathan cd652c7285 feat: wanted list API — list + standalone add 2026-07-11 14:04:35 +02:00
Jonathan 6615ecf3a7 feat: releases API — monitor toggle + search-now enqueue 2026-07-11 14:00:49 +02:00
Jonathan f5ceea8fde fix: narrow artist PATCH/DELETE 404 catch to Prisma P2025 (rethrow real errors) 2026-07-11 13:58:40 +02:00
Jonathan 1e39c74b16 feat: artist item API — detail, auto-monitor toggle, unfollow 2026-07-11 13:55:36 +02:00
Jonathan 7f0ac68657 feat: artists API — follow (persist discography) + list 2026-07-11 13:51:14 +02:00
Jonathan 8e97796bad feat: MusicBrainz proxy API routes (artist search + discography browse)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 13:45:24 +02:00
Jonathan 341da71505 test: type the fetch mock so the MB test typechecks (tsc --noEmit clean) 2026-07-11 13:15:21 +02:00
Jonathan cdcda9e501 feat: read-only MusicBrainz module + monitoring test cleanup
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 13:11:11 +02:00
Jonathan eb2a2207a1 docs: add monitoring-web implementation plan (slice 2, plan 2)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 13:08:19 +02:00
Jonathan 2273bf86ee test: pin that an at-cutoff upgrade job short-circuits intake (no candidates) 2026-07-11 12:56:54 +02:00
Jonathan 4481f1d998 fix: make intake dedupe cutoff-aware so monitor upgrade path works end-to-end
The monitor's quality-upgrade path was inert: run_pipeline's intake
short-circuit treated any existing LibraryItem as "done", so a
monitor-driven upgrade job for a below-cutoff copy jumped straight to
imported without downloading. Intake now checks whether the job is
monitor-driven (Request.monitoredReleaseId IS NOT NULL) and, if so,
only short-circuits when the existing copy already meets the quality
cutoff; plain manual requests keep exact slice-1 behavior. Also drops
an unused `field` import in browser.py.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 12:52:03 +02:00
Jonathan 3934a0c45d feat: import stage upserts to replace lower-quality library items 2026-07-11 12:40:11 +02:00
Jonathan 133f6219ae feat: monitor sweep, real MB browser, and worker-loop wiring 2026-07-11 12:35:07 +02:00
Jonathan 1f23aaa0e8 feat: monitor outcome reconcile 2026-07-11 12:29:26 +02:00
Jonathan 8f72568d87 feat: monitor retry/enqueue phase 2026-07-11 12:24:39 +02:00
Jonathan 7192752d8f test: genuinely exercise discovery ON CONFLICT dedup + config defaults 2026-07-11 12:22:29 +02:00
Jonathan 9a4ce3d5b5 feat: monitor discovery phase + MonitorConfig 2026-07-11 12:16:17 +02:00
Jonathan 2ca0f7a385 feat: MbBrowser seam and FakeMbBrowser test double
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 12:12:17 +02:00
Jonathan ec85016d88 feat: monitoring data model (WatchedArtist, MonitoredRelease, Request link) 2026-07-11 12:07:09 +02:00
Jonathan 915bd96cd3 docs: add monitoring-core implementation plan (slice 2, plan 1)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 12:01:57 +02:00
Jonathan dcddcac86c docs: add library-manager design (slice 2)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 11:46:41 +02:00
Jonathan c75b9d3562 fix: force IPv4 in worker, count real Qobuz files, flatten nested output
- worker had no IPv6 route -> Qobuz CDN (dual-stack) failed ~half the tracks
  with 'Network is unreachable'; disable IPv6 in the container (IPv4 only).
- StreamripClient counted metadata tracks (always full) -> a partial download
  falsely imported; now count the actual files written (short count -> needs_attention).
- streamrip writes into a nested 'Artist - Album [..]/' folder the tagger never saw;
  flatten audio files up into the album dir so tagging/organization runs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 01:01:23 +02:00
Jonathan 00b3dddca6 feat: Qobuz auth-token (user_id + token) login as a reliable alternative to email/password
Qobuz's email/password API login returns 401 even with valid credentials
(a known Qobuz limitation, unaffected by streamrip version). Add settings
fields for a Qobuz user ID + auth token (token stored encrypted); when both
are present StreamripClient uses use_auth_token=True, else falls back to
email/password.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 00:46:17 +02:00
Jonathan caa0d542ea fix: MD5-hash Qobuz password and never log credential-bearing login errors
streamrip's email/password login expects the MD5 digest, not plaintext
(caused 'Invalid credentials'). Also wrap login() so streamrip's exception —
which embeds the email/password/app_id — can never reach the worker logs;
re-raise a clean credential-free RuntimeError instead.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 00:30:34 +02:00
Jonathan f91117092b refactor: use one album path for tagging and library record
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 00:09:42 +02:00
Jonathan 1596bab23e feat: wire the mutagen tagger into the worker loop 2026-07-10 23:59:28 +02:00
Jonathan d1fcfd544f fix: make album tagging best-effort per file
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 23:57:28 +02:00
Jonathan 32deac65a6 feat: real mutagen tagger and MusicBrainz tracklist 2026-07-10 23:52:19 +02:00
Jonathan 1c02a870f3 feat: Tagger seam and pipeline tag-stage integration
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 23:47:20 +02:00
Jonathan 2812695465 feat: organized album directory and persistent /music bind-mount
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 23:43:15 +02:00
Jonathan 6cce95cec6 docs: add tagging, import & persistent library plan (slice 1, plan 4b)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 23:41:46 +02:00
Jonathan fef8cf0681 feat: wire MusicBrainz resolver into the worker loop 2026-07-10 23:27:28 +02:00
Jonathan 85c74ba109 feat: add real MusicBrainz resolver and opt-in live test 2026-07-10 23:21:50 +02:00
Jonathan c16e9f1035 feat: MusicBrainz-aware intake and completeness check against canonical track count 2026-07-10 23:17:06 +02:00
Jonathan cf5632aa5f fix: isolate per-adapter search failures in the pipeline match loop
Wrap each adapter's search() call in the match stage in try/except so
a down source (dead slskd, Qobuz auth error, etc.) contributes no
candidates instead of crashing run_pipeline and killing the worker.
2026-07-10 23:12:33 +02:00
Jonathan 6ea930ec7a docs: add MusicBrainz intake + search-hardening plan (slice 1, plan 4a)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 23:10:10 +02:00
Jonathan f93d71f78a test: verify SoulseekAdapter through the pipeline and full ranking order 2026-07-10 22:56:23 +02:00
Jonathan 6b09047f64 feat: add Soulseek to the config-aware registry 2026-07-10 22:53:25 +02:00
Jonathan ec1fc568ba fix: scope slskd download polling to enqueued files and raise on timeout
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 22:52:03 +02:00
Jonathan 7b4b7ef1aa feat: add real slskd Soulseek client and opt-in live test 2026-07-10 22:45:36 +02:00
Jonathan b5931a7b66 feat: add SoulseekAdapter with injectable client seam 2026-07-10 22:40:53 +02:00
Jonathan 9804f3bb90 docs: add Soulseek adapter plan (slice 1, plan 3d)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 22:39:34 +02:00
Jonathan 7510e7718b test: verify real QobuzAdapter flows through the pipeline and outranks YouTube 2026-07-10 22:16:03 +02:00
Jonathan 71143dc15d feat: config-aware registry gating Qobuz on credentials; wire config into worker 2026-07-10 22:13:11 +02:00
Jonathan 085193aa82 fix: run Qobuz login inside try/finally and reuse a single streamrip config
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 22:11:25 +02:00