Files
tidal-dl/tidal_dl_ng/gui/track_extras.py

75 lines
2.7 KiB
Python

"""Track extras mixin for MainWindow.
Handles track extras caching and retrieval.
"""
import contextlib
from collections.abc import Callable
from PySide6 import QtCore
from tidal_dl_ng.helper.tidal import extract_contributor_names, fetch_raw_track_and_album, parse_track_and_album_extras
from tidal_dl_ng.worker import Worker
class TrackExtrasMixin:
"""Mixin containing track extras management methods."""
def get_track_extras(self, track_id: str, callback: Callable[[str, dict | None], None]) -> dict | None:
"""Return cached extras for a track or start async fetch."""
cached = self.track_extras_cache.get(track_id)
if cached is not None:
return cached
if track_id in self._pending_extras_workers:
return None
if callback:
self._track_extras_callbacks[track_id] = callback
def worker() -> None:
extras = None
try:
track_json, album_json = fetch_raw_track_and_album(self.tidal.session, track_id)
extras = parse_track_and_album_extras(track_json, album_json)
extras = self._decorate_extras(extras)
self.track_extras_cache.set(track_id, extras)
except Exception:
extras = None
finally:
self._pending_extras_workers.pop(track_id, None)
self.s_track_extras_ready.emit(track_id, extras)
self.s_invoke_callback.emit(track_id, extras)
worker_obj = Worker(worker)
self._pending_extras_workers[track_id] = worker_obj
self.threadpool.start(worker_obj)
return None
@QtCore.Slot(str, object)
def _on_invoke_callback(self, track_id: str, extras: dict | None) -> None:
"""Invoke the stored callback for a track in the main thread."""
callback = self._track_extras_callbacks.pop(track_id, None)
if callback:
with contextlib.suppress(Exception):
callback(track_id, extras)
def _decorate_extras(self, extras: dict | None) -> dict:
"""Add formatted string fields to extras dict."""
if not extras:
return {}
result = dict(extras)
result["genres_text"] = ", ".join(result.get("genres", []))
for role, key in [
("producer", "producers_text"),
("composer", "composers_text"),
("lyricist", "lyricists_text"),
]:
result[key] = extract_contributor_names(result.get("contributors_by_role"), role)
return result
def preload_covers_for_playlist(self, items: list) -> None:
"""Preload cover pixmaps for a list of tracks in background."""
if self.cover_manager:
self.cover_manager.preload_covers_for_playlist(items)