df779c9e6d
* fix: Sync issues - AudioDB warmig + automatic sync skips * progress ui/ux + discovery and album fixes * artist fixes * several request level fixes and improvements * handle request fails + artist refresh + resilience fixes * fix format * fix stop sync fail + last.fn mbid issues + failures/validation reworks
138 lines
6.8 KiB
Python
138 lines
6.8 KiB
Python
"""Artist metadata + image pre-caching phase."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import logging
|
|
from typing import Any
|
|
|
|
from repositories.protocols import LidarrRepositoryProtocol, CoverArtRepositoryProtocol
|
|
from repositories.coverart_disk_cache import get_cache_filename
|
|
from services.cache_status_service import CacheStatusService
|
|
from infrastructure.cache.cache_keys import ARTIST_INFO_PREFIX
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class ArtistPhase:
|
|
def __init__(
|
|
self,
|
|
lidarr_repo: LidarrRepositoryProtocol,
|
|
cover_repo: CoverArtRepositoryProtocol,
|
|
preferences_service: Any,
|
|
genre_index: Any,
|
|
sync_state_store: Any,
|
|
):
|
|
self._lidarr_repo = lidarr_repo
|
|
self._cover_repo = cover_repo
|
|
self._preferences_service = preferences_service
|
|
self._genre_index = genre_index
|
|
self._sync_state_store = sync_state_store
|
|
|
|
async def precache_artist_images(
|
|
self,
|
|
artists: list[dict],
|
|
status_service: CacheStatusService,
|
|
library_artist_mbids: set[str] = None,
|
|
library_album_mbids: dict[str, Any] = None,
|
|
offset: int = 0,
|
|
generation: int = 0,
|
|
) -> None:
|
|
logger.info(f"Pre-caching metadata+images for {len(artists)} artists")
|
|
from core.dependencies import get_artist_service
|
|
from infrastructure.validators import is_unknown_mbid
|
|
artist_service = get_artist_service()
|
|
|
|
seen_mbids: set[str] = set()
|
|
unique_artists: list[dict] = []
|
|
for a in artists:
|
|
mbid = a.get('mbid')
|
|
if not mbid or is_unknown_mbid(mbid):
|
|
unique_artists.append(a)
|
|
elif mbid.lower() not in seen_mbids:
|
|
seen_mbids.add(mbid.lower())
|
|
unique_artists.append(a)
|
|
if len(unique_artists) < len(artists):
|
|
logger.info("Deduplicated %d artists to %d unique", len(artists), len(unique_artists))
|
|
artists = unique_artists
|
|
|
|
async def cache_artist(artist: dict, index: int) -> str:
|
|
mbid = artist.get('mbid')
|
|
try:
|
|
artist_name = artist.get('name', 'Unknown')
|
|
if is_unknown_mbid(mbid):
|
|
await status_service.update_progress(index + 1, artist_name, processed_artists=offset + index + 1, generation=generation)
|
|
return mbid
|
|
artist_cache_key = f"{ARTIST_INFO_PREFIX}{mbid}"
|
|
cached_artist = await artist_service._cache.get(artist_cache_key)
|
|
if not cached_artist:
|
|
try:
|
|
await artist_service.get_artist_info(mbid, library_artist_mbids, library_album_mbids)
|
|
except Exception: # noqa: BLE001
|
|
logger.debug(f"Failed to cache artist metadata for {artist_name}")
|
|
else:
|
|
logger.debug(f"Artist metadata for {artist_name} already cached, skipping fetch")
|
|
cache_filename_250 = get_cache_filename(f"artist_{mbid}_250", "img")
|
|
file_path_250 = self._cover_repo.cache_dir / f"{cache_filename_250}.bin"
|
|
cache_filename_500 = get_cache_filename(f"artist_{mbid}_500", "img")
|
|
file_path_500 = self._cover_repo.cache_dir / f"{cache_filename_500}.bin"
|
|
if file_path_250.exists() and file_path_500.exists():
|
|
logger.debug(f"Artist images for {artist_name} already cached, skipping")
|
|
await status_service.update_progress(index + 1, artist_name, processed_artists=offset + index + 1, generation=generation)
|
|
return mbid
|
|
await status_service.update_progress(index + 1, f"Fetching images for {artist_name}", processed_artists=offset + index + 1, generation=generation)
|
|
if not file_path_250.exists():
|
|
await self._cover_repo.get_artist_image(mbid, size=250)
|
|
if not file_path_500.exists():
|
|
await self._cover_repo.get_artist_image(mbid, size=500)
|
|
await status_service.update_progress(index + 1, artist_name, processed_artists=offset + index + 1, generation=generation)
|
|
return mbid
|
|
except Exception as e: # noqa: BLE001
|
|
logger.warning(f"Failed to cache artist {artist.get('name')} (mbid: {mbid}): {e}", exc_info=True)
|
|
await status_service.update_progress(index + 1, f"Failed: {artist.get('name', 'Unknown')}", processed_artists=offset + index + 1, generation=generation)
|
|
return mbid
|
|
|
|
advanced_settings = self._preferences_service.get_advanced_settings()
|
|
batch_size = advanced_settings.batch_artist_images
|
|
for i in range(0, len(artists), batch_size):
|
|
if status_service.is_cancelled():
|
|
logger.info("Artist pre-caching cancelled by user")
|
|
break
|
|
batch = artists[i:i + batch_size]
|
|
tasks = [cache_artist(artist, i + idx) for idx, artist in enumerate(batch)]
|
|
results = await asyncio.gather(*tasks, return_exceptions=True)
|
|
processed_mbids = []
|
|
for idx, result in enumerate(results):
|
|
if isinstance(result, Exception):
|
|
artist_name = batch[idx].get('name', 'Unknown')
|
|
logger.error(f"Batch error caching artist {artist_name}: {result}")
|
|
processed_mbids.append(batch[idx].get('mbid'))
|
|
elif result:
|
|
processed_mbids.append(result)
|
|
if processed_mbids:
|
|
await self._sync_state_store.mark_items_processed_batch('artist', processed_mbids)
|
|
await status_service.persist_progress(generation=generation)
|
|
await asyncio.sleep(advanced_settings.delay_artist)
|
|
await status_service.persist_progress(force=True, generation=generation)
|
|
logger.info("Artist metadata+image pre-caching complete")
|
|
await self._cache_artist_genres(artists)
|
|
|
|
async def _cache_artist_genres(self, artists: list[dict]) -> None:
|
|
from core.dependencies import get_artist_service
|
|
artist_service = get_artist_service()
|
|
artist_genres: dict[str, list[str]] = {}
|
|
logger.info(f"Extracting genre tags for {len(artists)} library artists")
|
|
for artist in artists:
|
|
mbid = artist.get('mbid')
|
|
if not mbid:
|
|
continue
|
|
cache_key = f"{ARTIST_INFO_PREFIX}{mbid}"
|
|
cached_info = await artist_service._cache.get(cache_key)
|
|
if cached_info and hasattr(cached_info, 'tags') and cached_info.tags:
|
|
artist_genres[mbid] = cached_info.tags[:10]
|
|
if artist_genres:
|
|
await self._genre_index.save_artist_genres(artist_genres)
|
|
logger.info(f"Cached genres for {len(artist_genres)} artists")
|
|
else:
|
|
logger.info("No artist genres found to cache")
|