docs: update README for Last.fm, redesign, progress/retry, and pages

Bring the README current with everything shipped since it was written:
Last.fm as a second discovery source + the /lastfm browse page, the
/library grid, The Floor's three-phase progress + retry, and the
Pressing Plant design system. Add a "Running it" quick-start. Fix a
pre-existing inaccuracy: slskd is an external daemon reached via
config, not a compose service (stack is db + web + worker).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Jonathan
2026-07-13 23:53:30 +02:00
parent 9561e6e196
commit 8922af757c
+54 -17
View File
@@ -4,8 +4,8 @@ Self-hosted, multi-source music acquisition and library tool — a replacement f
Lidarr + NZBget. Lyra discovers, ranks, downloads, tags, and organizes music across Lidarr + NZBget. Lyra discovers, ranks, downloads, tags, and organizes music across
sources of differing quality: **Qobuz** (lossless/hi-res), **Soulseek** (P2P), and sources of differing quality: **Qobuz** (lossless/hi-res), **Soulseek** (P2P), and
**YouTube** (universal lossy fallback), with recommendation-driven discovery via **YouTube** (universal lossy fallback), with recommendation-driven discovery via
**ListenBrainz** (Spotify's recommendation/related-artists APIs were retired for new **ListenBrainz** and **Last.fm** (Spotify's recommendation/related-artists APIs were
apps, so discovery is MetaBrainz-native and MBID-clean — no Spotify). retired for new apps, so discovery is MetaBrainz-native and MBID-clean — no Spotify).
## Status ## Status
@@ -22,8 +22,10 @@ Built in three slices:
existing on-disk albums as have + monitored + followed, and a per-artist existing on-disk albums as have + monitored + followed, and a per-artist
studio-only / show-all release-type filter. studio-only / show-all release-type filter.
3. **Discovery** — ✅ **done**. Recommendation-driven finding of new music via 3. **Discovery** — ✅ **done**. Recommendation-driven finding of new music via
**ListenBrainz** similar-artists (MBID-native), behind a pluggable **ListenBrainz** similar-artists and **Last.fm** `artist.getSimilar` (both
`SimilaritySource` interface. A background sweep (and a "Discover now" button) MBID-native), behind a pluggable `SimilaritySource` interface — Last.fm registers
as a second source automatically once its API key is set, and cross-source scores
are summed per candidate. A background sweep (and a "Discover now" button)
aggregates artists similar to the ones you follow into a manual review feed of aggregates artists similar to the ones you follow into a manual review feed of
suggested **artists** and **albums** at `/discover`; **Follow** and **Want** suggested **artists** and **albums** at `/discover`; **Follow** and **Want**
reuse the slice-2 follow / wanted flows, **Dismiss** is permanent. Plus a reuse the slice-2 follow / wanted flows, **Dismiss** is permanent. Plus a
@@ -33,27 +35,62 @@ Built in three slices:
Follow or Want before committing. No Spotify (its recommendation APIs are dead Follow or Want before committing. No Spotify (its recommendation APIs are dead
for new apps). for new apps).
Alongside the three slices:
- **Last.fm browse** (`/lastfm`) — your Last.fm top artists and albums (by period),
with a "hide items already in my library" toggle; each artist opens a modal of your
most-played songs and albums, and every album is one click to **Want**.
- **Library** (`/library`) — a cover-art grid of everything on disk, filterable, each
album opening a tracklist modal.
- **The Floor** (home) — a live queue of in-flight acquisitions with three-phase
progress (**Searching → Downloading → Finishing**), a live download percentage, a
per-job "pressing ticket" (source · format · tracks · elapsed), and a **Retry**
button on anything that needs attention.
The whole UI runs a hand-rolled "Pressing Plant" design system (warm-paper theme, self-hosted
Fraunces serif for prose vs. system mono for machine data, light/dark aware).
**Operational notes:** the monitor and the discovery sweep are both **off by **Operational notes:** the monitor and the discovery sweep are both **off by
default** — set `monitor.enabled=true` (+ optional `monitor.*` tuning) in the default** — set `monitor.enabled=true` (or Settings → Monitor, + optional `monitor.*`
`Config` table to activate auto-grab/upgrade, and `discover.enabled=true` (or tuning) to activate auto-grab/upgrade, and `discover.enabled=true` (or
Settings → Discovery, + optional `discover.*` tuning) to activate the scheduled Settings → Discovery, + optional `discover.*` tuning) to activate the scheduled
discovery sweep. The `/discover` "Discover now" button and seed-search work with the discovery sweep. The `/discover` "Discover now" button and seed-search work with the
sweep off. Credentials are entered in Settings and persist across sweep off. Credentials (Qobuz, Soulseek, Last.fm) are entered in Settings and persist
`docker compose up -d --build` rebuilds (don't use `down -v`). Tests run against a across `docker compose up -d --build` rebuilds (don't use `down -v`; secrets are
separate `lyra_test` database (a guard blocks the live DB). Deferred hardening + the encrypted at rest with `LYRA_SECRET_KEY`). Tests run against a separate `lyra_test`
full roadmap live in the implementation-plan "Notes" sections under database (a guard blocks the live DB). Deferred hardening + the full roadmap live in
the implementation-plan "Notes" sections under
[`docs/superpowers/plans/`](docs/superpowers/plans/). [`docs/superpowers/plans/`](docs/superpowers/plans/).
## Architecture ## Architecture
Three Docker containers on a home server: The `docker compose` stack is three services on a home server:
- **Next.js app** — UI + API (search, request, queue, progress, settings). - **db** — Postgres, the shared source of truth.
- **Python worker** — all source integration (streamrip, yt-dlp, slskd client, - **web** — the Next.js app: UI + API (search, request, queue, progress, settings).
mutagen; MusicBrainz + ListenBrainz for metadata and discovery), running the - **worker** — the Python worker: all source integration (streamrip, yt-dlp, slskd
six-stage acquisition pipeline plus the background monitor and discovery sweeps. client, mutagen; MusicBrainz + ListenBrainz + Last.fm for metadata and discovery),
- **slskd** — off-the-shelf headless Soulseek daemon. running the six-stage acquisition pipeline plus the background monitor and discovery
sweeps.
**Postgres** is the shared source of truth; a shared `/music` volume is the library. Soulseek support talks to an **external slskd** daemon (an off-the-shelf headless
Soulseek client) — the worker reaches it via the `slskd.url` + `slskd.api_key`
credentials set in Settings, so run slskd separately rather than as part of this stack.
A shared `/music` volume is the library, with a separate `/staging` volume for
in-progress downloads (point `STAGING_DIR` at a fast local disk when `MUSIC_DIR` is a
network share).
See [`docs/superpowers/specs/`](docs/superpowers/specs/) for the full design. See [`docs/superpowers/specs/`](docs/superpowers/specs/) for the full design.
## Running it
```sh
cp .env.example .env # then edit: set LYRA_SECRET_KEY (openssl rand -base64 32) and MUSIC_DIR
docker compose up -d --build # web on http://localhost:8770 ; NEVER `down -v` (wipes the Postgres volume)
```
The web entrypoint runs `prisma migrate deploy` on every rebuild, so schema changes
reach the live DB automatically. Enter Qobuz / Soulseek / Last.fm credentials in
**Settings** (they persist across rebuilds), then follow an artist or request an album
to feed The Floor.