diff --git a/src/db.rs b/src/db.rs index 0efefcb..ec30120 100644 --- a/src/db.rs +++ b/src/db.rs @@ -869,6 +869,31 @@ pub fn albums_without_cover(conn: &Connection) -> Result> { rows.collect() } +/// Like [`albums_without_cover`] but restricted to albums where at least one +/// scrobble came from MPD (`source = 'MPD'`). +/// +/// There is no point asking MPD for a cover art of an album that was scrobbled +/// entirely via an MPRIS player (e.g. Qobuz, Spotify) — MPD almost certainly +/// does not have that file on disk. This query avoids those wasted round-trips. +pub fn albums_without_cover_from_mpd(conn: &Connection) -> Result> { + let mut stmt = conn.prepare( + "SELECT DISTINCT s.artist, s.album + FROM scrobbles s + LEFT JOIN album_cache c ON s.artist = c.artist AND s.album = c.album + WHERE s.album != '' + AND s.source = 'MPD' + AND (c.id IS NULL OR c.cover_url IS NULL) + ORDER BY s.artist, s.album", + )?; + let rows = stmt.query_map([], |row| { + Ok(UncachedAlbum { + artist: row.get(0)?, + album: row.get(1)?, + }) + })?; + rows.collect() +} + /// Cached album metadata returned by [`album_cache_meta`]. #[derive(Debug, Clone)] pub struct AlbumCacheMeta { diff --git a/src/mpd.rs b/src/mpd.rs index fc0639d..b9540a5 100644 --- a/src/mpd.rs +++ b/src/mpd.rs @@ -942,9 +942,10 @@ fn try_extract_cover( /// one; if it finds nothing, the local cover is preserved (see /// `db::upsert_album_cache` for the `COALESCE` logic). pub fn run_mpd_cover_enrich(config: &MpdConfig, conn: &Connection) { - // Fetch covers for every scrobbled album that still has no cover_url. - // Used by the standalone `enrich` command where the scope is the whole DB. - let albums = match db::albums_without_cover(conn) { + // Only consider albums where at least one scrobble came from MPD — albums + // scrobbled exclusively via MPRIS players (Qobuz, Spotify, etc.) are + // unlikely to be present in MPD's music database. + let albums = match db::albums_without_cover_from_mpd(conn) { Ok(a) => a, Err(e) => { eprintln!("[error] Failed to query albums without cover: {}", e); @@ -971,10 +972,10 @@ pub fn run_mpd_cover_enrich_targeted( conn: &Connection, needed: &std::collections::HashSet<(String, String)>, ) { - // Start from albums that genuinely have no cover yet, then narrow to the - // set the caller cares about. This avoids redundant MPD round-trips for - // albums that already have art. - let albums = match db::albums_without_cover(conn) { + // Start from albums that have no cover AND were scrobbled via MPD — albums + // from MPRIS-only players are excluded because MPD won't have those files. + // Then narrow further to the set the caller cares about. + let albums = match db::albums_without_cover_from_mpd(conn) { Ok(a) => a, Err(e) => { eprintln!("[error] Failed to query albums without cover: {}", e);