New frontend-agnostic crate qbz-external-reco: blends Last.fm + ListenBrainz recommendation signals into the Discover "External Recommendations" carousels, validating every candidate against the Qobuz catalog (ISRC direct -> MusicBrainz inc=isrcs bridge -> fuzzy text scorer, gated) before display. Four carousels (C1 similar artists / C2 similar tracks you haven't heard, C3 listened-but-not- recently, C4 deep cuts from known artists) capped at 20 with daily rotation over a larger cache-backed pool, plus a Qobuz-editorial cold-start fallback (top albums + artists) when no external source is connected. Round-robin blend across sources. Resolution cache (per-user SQLite, 30d hits / 7d misses, WAL). Supporting public read methods added to qbz-integrations: - Last.fm: get_top_artists / get_top_tracks / get_loved_tracks / get_recent_tracks / get_similar_tracks (public reads via the Cloudflare proxy). - ListenBrainz: get_cf_recommendations / get_recent_listens / get_metadata_recordings (204/404 -> empty, never error). - MusicBrainz: get_recording_isrcs (the MBID -> ISRC bridge). Engine is headless and unit-tested (matching + cache); no qbz-ui dependency. cc
26 lines
660 B
TOML
26 lines
660 B
TOML
[package]
|
|
name = "qbz-external-reco"
|
|
version.workspace = true
|
|
edition.workspace = true
|
|
license.workspace = true
|
|
description = "Frontend-agnostic external-recommendations engine (Last.fm + ListenBrainz -> Qobuz) for Discover"
|
|
|
|
[dependencies]
|
|
qbz-integrations = { path = "../qbz-integrations" }
|
|
qbz-models = { path = "../qbz-models" }
|
|
|
|
# Async
|
|
tokio = { workspace = true }
|
|
async-trait = { workspace = true }
|
|
futures-util = { workspace = true }
|
|
|
|
# Serialization
|
|
serde = { workspace = true }
|
|
serde_json = { workspace = true }
|
|
|
|
# Cache (per-user SQLite, WAL per ADR-002)
|
|
rusqlite = { version = "0.31", features = ["bundled"] }
|
|
|
|
# Logging
|
|
log = { workspace = true }
|