feat(deezer): add Deezer enrichment service + wire into match pipeline
Add a standalone Deezer Go microservice (deezer/) modeled on musicbrainz/
that takes title+artist+optional album and returns fully enriched track
metadata plus a ranked list of best matches. It owns rate limiting
(50 req / 5s) and an in-memory TTL cache, and deep-fetches the top candidate
to fill every available field (ISRC, UPC, label, genres, release date/year,
track/disc number, hi-res album art + artist picture).
When Spotify search fails or returns partial data we end up with incomplete
metadata; Deezer now fills the gaps (or builds the track from scratch):
- apps/api matchSong: ctx.deezer client + DEEZER_URL env; fallback after the
Spotify block. Adds an additive, non-breaking `matches` list to
songViewDetailed (pkl -> lexicon JSON -> generated TS). Score scaled 0-100.
- Rust scrobbler: DeezerClient + try_deezer_enrich fallback across scrobble,
scrobble_v1 and scrobble_listenbrainz.
Duration unit: Deezer returns seconds; the service x1000 to durationMs to
align with Spotify's native duration_ms (verified against the live API).
Tests mock the real Deezer API with httptest; a DEEZER_SMOKE-gated live smoke
test exercises the real endpoint (off in CI). Adds systemd unit, root npm
script, and a gated GitHub Actions job for the Go tests.
feat(deezer): add Deezer enrichment service + wire into match pipeline
Add a standalone Deezer Go microservice (deezer/) modeled on musicbrainz/
that takes title+artist+optional album and returns fully enriched track
metadata plus a ranked list of best matches. It owns rate limiting
(50 req / 5s) and an in-memory TTL cache, and deep-fetches the top candidate
to fill every available field (ISRC, UPC, label, genres, release date/year,
track/disc number, hi-res album art + artist picture).
When Spotify search fails or returns partial data we end up with incomplete
metadata; Deezer now fills the gaps (or builds the track from scratch):
- apps/api matchSong: ctx.deezer client + DEEZER_URL env; fallback after the
Spotify block. Adds an additive, non-breaking `matches` list to
songViewDetailed (pkl -> lexicon JSON -> generated TS). Score scaled 0-100.
- Rust scrobbler: DeezerClient + try_deezer_enrich fallback across scrobble,
scrobble_v1 and scrobble_listenbrainz.
Duration unit: Deezer returns seconds; the service x1000 to durationMs to
align with Spotify's native duration_ms (verified against the live API).
Tests mock the real Deezer API with httptest; a DEEZER_SMOKE-gated live smoke
test exercises the real endpoint (off in CI). Adds systemd unit, root npm
script, and a gated GitHub Actions job for the Go tests.