Commit Graph

56 Commits

Author SHA1 Message Date
Jonathan e1d96aa603 feat(web): Pressing Plant design tokens + self-hosted Fraunces
globals.css defines the manila/spruce palette as CSS custom properties for
both themes (prefers-color-scheme + explicit data-theme override), paper
grain, base reset, focus-visible, reduced-motion. Fraunces vendored as woff2
and wired via next/font/local with a Palatino/Georgia fallback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 14:09:30 +02:00
Jonathan 33a1ed9cc4 fix(artists-api): create artist + discography atomically
Wrap the WatchedArtist.create + MonitoredRelease.createMany in a
$transaction so a mid-write failure can't leave a followed artist with
no releases (a state the 409 duplicate-follow guard would then wedge).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 10:42:42 +02:00
Jonathan a07e791368 feat(artists): link live-search hits to the artist preview page
Each MB search result on /artists now links its name to
/discover/artist/[mbid] (the read-only disco + tracklist + own-state
preview), so you can preview before following there too — same wiring
the discovery feed already uses.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 10:42:29 +02:00
Jonathan 3f77bb1f4c fix(preview): guard want() on res.ok; docs: preview in README + artists-search TODO
- preview-client want() no longer flips the Monitored badge when the POST fails
  (deferred item #1 from the discovery-preview whole-branch review).
- README: discovery section notes the artist preview page.
- Plan Notes + project-state: TODO to reuse the preview page from the /artists
  live search (wire artists-client.tsx hits to /discover/artist/[mbid]).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 02:03:08 +02:00
Jonathan 3928662809 feat(preview): link discovery feed + Find-similar to the artist preview
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 01:49:40 +02:00
Jonathan e1033dab81 feat(preview): /discover/artist/[mbid] preview page (disco + tracks + own-state)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 01:46:13 +02:00
Jonathan 9f2b115721 feat(preview): POST /api/discover/want (want a release-group by metadata)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 01:42:49 +02:00
Jonathan 46164c9eab feat(preview): GET /api/discover/preview/[mbid] annotated discography
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 01:39:35 +02:00
Jonathan f3cb306a19 feat(preview): GET /api/mb/release-groups/[rgMbid]/tracks
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 01:36:28 +02:00
Jonathan e9759b8ef2 feat(preview): MB getArtistName + browseReleaseGroupTracks
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 01:33:44 +02:00
Jonathan d2c581d8e7 fix(discovery): use a valid ListenBrainz similar-artists algorithm
The placeholder algorithm string (contribution_5_threshold_15_limit_50) is not a
permitted enum member of the LB Labs endpoint — every lookup 400'd, making discovery
inert. Verified against the live API 2026-07-12: the days_7500/contribution_3/
threshold_10/limit_100 variant returns rows shaped {artist_mbid,name,comment,score},
which the existing defensive parser already handles. Fixed in both the worker adapter
and the web seed-search client (kept in sync).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 00:54:33 +02:00
Jonathan b9ebd18c2c feat(discovery): Discovery settings section (enable + tuning knobs)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 00:47:18 +02:00
Jonathan 3de7461245 feat(discovery): /discover feed page + seed search UI + nav link
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 00:44:11 +02:00
Jonathan 10262f053a feat(discovery): GET/PATCH /api/discover/config
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 00:41:13 +02:00
Jonathan c7aeab5176 feat(discovery): POST /api/discover/[id] follow/want/dismiss actions
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 00:38:06 +02:00
Jonathan bf868829d8 feat(discovery): GET /api/discover/search interactive seed search
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 00:34:28 +02:00
Jonathan be59862dfc feat(discovery): POST/GET /api/discover/run trigger + status
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 00:32:01 +02:00
Jonathan f750b815d2 feat(discovery): GET /api/discover feed
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 00:29:28 +02:00
Jonathan 2dfc9ab277 feat(discovery): web ListenBrainz similar-artists client
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 00:26:29 +02:00
Jonathan 0ddc9db611 feat(discovery): DiscoverySuggestion schema + WatchedArtist.lastDiscoveredAt
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 23:58:45 +02:00
Jonathan d65230ddfb feat: artist page toggle to show all release types (default studio-only) 2026-07-11 18:07:38 +02:00
Jonathan 560fdaa4b0 feat: filter artist discography + counts to core releases unless showAllTypes 2026-07-11 18:05:46 +02:00
Jonathan f2dbef047c feat: WatchedArtist.showAllTypes toggle (default false)
Adds the per-artist release-type filter toggle: off (default) restricts
a discography view to core studio releases, on shows everything
(live, compilations, etc.).
2026-07-11 18:03:29 +02:00
Jonathan 28fc1ee1a8 fix: artists search marks already-followed artists instead of a Follow button 2026-07-11 17:50:53 +02:00
Jonathan 2fb825140f feat: auto-refresh the scan button until the worker finishes 2026-07-11 17:40:27 +02:00
Jonathan 612cd02b15 feat: Settings 'Scan library' button + last-result status 2026-07-11 17:14:37 +02:00
Jonathan 7cb99361a5 feat: /api/scan — request a library scan + read its result
Type the POST request param (_request: Request) to match the test's
call signature and keep tsc --noEmit clean, per Next.js route handler
convention already used elsewhere in the codebase.
2026-07-11 17:11:42 +02:00
Jonathan af67d01aa2 feat: LibraryItem.requestId nullable (scanned haves need no request) + test cleanup
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 16:56:59 +02:00
Jonathan f59f972c1b fix: guard test cleanup to a dedicated lyra_test DB (never wipe live data)
Test suites were running DELETE FROM.../deleteMany() against whatever
DATABASE_URL pointed to, which was the live app database, wiping user
credentials and library. Add a guard in both the worker conftest and
web test setup that raises before any cleanup unless the target DB
name ends in '_test'. Default DATABASE_URL to lyra_test when unset in
the worker, and make `npm test` target lyra_test explicitly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 16:16:12 +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 ec85016d88 feat: monitoring data model (WatchedArtist, MonitoredRelease, Request link) 2026-07-11 12:07:09 +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 69a8835c86 feat: add settings page for credentials 2026-07-10 19:58:46 +02:00
Jonathan 1b11fa37bd feat: add config API with encrypted secret storage 2026-07-10 19:54:27 +02:00
Jonathan 91ea32c4b8 feat: add web AES-256-GCM secret crypto and LYRA_SECRET_KEY plumbing 2026-07-10 19:50:16 +02:00
Jonathan 45cf9e8527 feat: add Config schema 2026-07-10 19:46:17 +02:00
Jonathan 60f3cd0df9 feat: add Candidate and LibraryItem schema
Adds Candidate (per-job download candidates found by the pipeline)
and LibraryItem (per-request imported album, deduped on
artist+album) tables. LibraryItem.requestId is @unique since
Request.libraryItem is a one-to-one back-relation (required for
Prisma validation, matching the existing Job.requestId pattern).
2026-07-10 18:40:41 +02:00
Jonathan 7532e20d1e fix: worker waits for schema before claiming; npm ci for reproducible builds
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 17:32:20 +02:00
Jonathan 52a0999f9b chore: add restart policies and document prisma copy step
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 17:22:36 +02:00
Jonathan 3e44de093e feat: add worker loop and production Dockerfiles 2026-07-10 17:16:24 +02:00