One device holds the audio; the rest watch the same session over an
event stream and act as remotes. Picking a device hands the audio over
at the position the previous one reported, and that device stops.
Also centre the command palette with margins instead of a translate:
animate-rise sets its own transform and dropped the offset on mobile.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Acquisition ran yt-dlp without --embed-metadata, so every download
arrived untagged. The scanner then stored the video id as the title and
"Unknown Artist" as the artist, the vetted-candidate tag check rejected
the mismatch, and all 18 acquired tracks were hidden and retired.
- Pass --embed-metadata so downloads carry real tags.
- Let a scan take fallback title/artist from the candidate, for sources
that still ship untagged files.
- Install Deno alongside yt-dlp: YouTube guards some formats with a JS
challenge yt-dlp must execute, and no other runtime is enabled.
- Dedupe candidates by artist and title. The (source, external_id) key
misses the same song reaching us under two Deezer release ids.
Also carries the in-flight discovery work this builds on: the
Recommendations page replacing Discover, the discovery source service,
and the acquisition spec tests.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
db.service.ts was 2085 lines, of which ~700 were not behaviour at all: the
migration registry, the row-shape interfaces, and the column allowlist. That
makes the file painful to review — the reviewer's note on the MIGRATIONS
array.
Three pure moves into backend/src/db/, which already owns schema.sql:
- migrations.ts (513) — the registry, plus a named Migration type
- types.ts (168) — the row shapes
- updatable-columns.ts (42) — UPDATABLE_COLUMNS + allowedFields
db.service.ts drops to 1378 lines and re-exports ../db/types.js, so existing
`import { Track, ListenerBelief } from '../services/db.service.js'` in the
routes, generators and session-director keeps working untouched.
No behaviour change, and verified as such rather than asserted: the migration
id list and the entire 502-line SQL body diff byte-identical against the
previous commit, backend typecheck is clean and 33/33 tests pass.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>