Avoid repeated artwork miss fetches and refresh churn
Lowers CPU usage: - Only call refresh() when real artwork is found - Treating NoArtwork as a valid cached result (art is not None check). - Adding in-flight dedupe (pending_tracks) so the same track doesn't spawn parallel fetches.
This commit is contained in:
parent
9b910cd991
commit
fa82f45ef9
1 changed files with 25 additions and 10 deletions
|
|
@ -30,19 +30,28 @@ class MpdArtworkCache:
|
||||||
mpd: MpdStateHandler
|
mpd: MpdStateHandler
|
||||||
album_cache: Cache[Artwork]
|
album_cache: Cache[Artwork]
|
||||||
track_cache: Cache[Artwork]
|
track_cache: Cache[Artwork]
|
||||||
|
pending_tracks: set[str]
|
||||||
|
|
||||||
def __init__(self, mpd: MpdStateHandler, cache_url: URL = MEMORY):
|
def __init__(self, mpd: MpdStateHandler, cache_url: URL = MEMORY):
|
||||||
self.mpd = mpd
|
self.mpd = mpd
|
||||||
self.album_cache = make_cache(ArtworkSchema, cache_url, "album")
|
self.album_cache = make_cache(ArtworkSchema, cache_url, "album")
|
||||||
self.track_cache = make_cache(ArtworkSchema, cache_url, "track")
|
self.track_cache = make_cache(ArtworkSchema, cache_url, "track")
|
||||||
|
self.pending_tracks = set()
|
||||||
|
|
||||||
async def get_cached_artwork(self, song: CurrentSongResponse) -> bytes | None:
|
async def get_cached_artwork(self, song: CurrentSongResponse) -> bytes | None:
|
||||||
art = await self.track_cache.get(calc_track_key(song))
|
track_key = calc_track_key(song)
|
||||||
if art:
|
art = await self.track_cache.get(track_key)
|
||||||
return art.data
|
if art is not None:
|
||||||
|
# NoArtwork is a valid cached value too: returning None here avoids
|
||||||
|
# repeatedly re-querying MPD for files that have no embedded art.
|
||||||
|
if art:
|
||||||
|
return art.data
|
||||||
|
return None
|
||||||
|
|
||||||
# If we don't have track artwork cached, go find some.
|
# If we don't have track artwork cached, go find some.
|
||||||
run_background_task(self.cache_artwork(song))
|
if track_key not in self.pending_tracks:
|
||||||
|
self.pending_tracks.add(track_key)
|
||||||
|
run_background_task(self.cache_artwork(song))
|
||||||
|
|
||||||
# Even if we don't have cached track art, we can try looking for cached album art.
|
# Even if we don't have cached track art, we can try looking for cached album art.
|
||||||
art = await self.album_cache.get(calc_album_key(song))
|
art = await self.album_cache.get(calc_album_key(song))
|
||||||
|
|
@ -52,10 +61,16 @@ class MpdArtworkCache:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def cache_artwork(self, song: CurrentSongResponse) -> None:
|
async def cache_artwork(self, song: CurrentSongResponse) -> None:
|
||||||
art = to_artwork(await self.mpd.get_art(song["file"]))
|
track_key = calc_track_key(song)
|
||||||
try:
|
try:
|
||||||
await self.album_cache.add(calc_album_key(song), art, ttl=CACHE_TTL)
|
art = to_artwork(await self.mpd.get_art(song["file"]))
|
||||||
except ValueError:
|
try:
|
||||||
pass
|
await self.album_cache.add(calc_album_key(song), art, ttl=CACHE_TTL)
|
||||||
await self.track_cache.set(calc_track_key(song), art, ttl=CACHE_TTL)
|
except ValueError:
|
||||||
await self.mpd.refresh()
|
pass
|
||||||
|
await self.track_cache.set(track_key, art, ttl=CACHE_TTL)
|
||||||
|
# Refresh receivers only when we discovered actual artwork.
|
||||||
|
if art:
|
||||||
|
await self.mpd.refresh()
|
||||||
|
finally:
|
||||||
|
self.pending_tracks.discard(track_key)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue