diff --git a/beetsplug/bandcamp/__init__.py b/beetsplug/bandcamp/__init__.py index dd32c36..ee451ba 100644 --- a/beetsplug/bandcamp/__init__.py +++ b/beetsplug/bandcamp/__init__.py @@ -21,10 +21,11 @@ import json import logging import re -from contextlib import contextmanager +from contextlib import contextmanager, suppress from functools import partial from operator import itemgetter -from typing import TYPE_CHECKING, Any, Iterable, Iterator, Literal, Optional +from pathlib import Path +from typing import TYPE_CHECKING, Any, Iterable, Iterator, Literal from beets import IncludeLazyConfig, config, plugins @@ -223,6 +224,18 @@ def candidates( self, items: list[Item], artist: str, album: str, *_: Any, **__: Any ) -> Iterable[AlbumInfo]: """Return a sequence of album candidates matching given artist and album.""" + url = items[0].comments + parent_dir = Path(items[0].path.decode()).parent + with suppress(StopIteration): + playlist_info_path = next(parent_dir.glob("Playlist_*")) + playlist_info = json.loads(playlist_info_path.read_text()) + + playlist_info["tracks"] = [] + for track_info_path in set(parent_dir.glob("*.info.json")) - { + playlist_info_path + }: + playlist_info["tracks"].append(json.loads(track_info_path.read_text())) + item = items[0] label = "" if items and album == item.album and artist == item.albumartist: @@ -324,8 +337,8 @@ def _get_soundcloud_data(self, url: str) -> AlbumInfo | TrackInfo | None: sc_data_key = "sound" method = get_soundcloud_track - self._info("Fetching data from soundcloud url {} as {}", url, _type) - data = re.search(r"\[\{[^<]+[^;<)]", self._get(url)) + self._info("Fetching data from soundcloud url {}", url) + data = re.search(r"\[.*hydratable.*\]", self._get(url)) if not data: return None diff --git a/beetsplug/bandcamp/helpers.py b/beetsplug/bandcamp/helpers.py index 6e48f9f..52c24e3 100644 --- a/beetsplug/bandcamp/helpers.py +++ b/beetsplug/bandcamp/helpers.py @@ -290,10 +290,14 @@ def within_another_genre(genre: str) -> bool: return (g for g in unique_genres if not within_another_genre(g)) @staticmethod - def unpack_props(obj: JSONDict) -> JSONDict: + def unpack_props(obj: Any) -> Any: """Add all 'additionalProperty'-ies to the parent dictionary.""" - for prop in obj.get("additionalProperty") or []: - obj[prop["name"]] = prop["value"] + if isinstance(obj, dict): + for prop in obj.pop("additionalProperty", []): + obj[prop["name"]] = prop["value"] + return {k: Helpers.unpack_props(v) for k, v in obj.items()} + if isinstance(obj, list): + return [Helpers.unpack_props(item) for item in obj] return obj @staticmethod diff --git a/beetsplug/bandcamp/soundcloud.py b/beetsplug/bandcamp/soundcloud.py index e04adc9..97b2f74 100644 --- a/beetsplug/bandcamp/soundcloud.py +++ b/beetsplug/bandcamp/soundcloud.py @@ -3,10 +3,9 @@ import re from datetime import datetime from functools import cached_property -from typing import Any, ClassVar, Final, Literal +from typing import TYPE_CHECKING, Any, ClassVar, Final, Literal from unicodedata import normalize -from beets import IncludeLazyConfig from beets.autotag.hooks import AlbumInfo, TrackInfo from pycountry import countries, subdivisions from pydantic import BaseModel, Field, OnErrorOmit @@ -15,6 +14,9 @@ from .metaguru import COUNTRY_OVERRIDES, DIGI_MEDIA from .track import Track +if TYPE_CHECKING: + from beets import IncludeLazyConfig + JSONDict = dict[str, Any] SINGLE_TRACK_MAX_LENGTH = 2000 @@ -43,7 +45,7 @@ class ParsedTrack(BaseModel): @cached_property def data(self) -> JSONDict: - return {k: v for k, v in self.dict().items() if k != "live" and v} + return {k: v for k, v in self.dict().items() if k != "live"} def parse_title(source: str, title: str, artist: str) -> ParsedTrack: @@ -56,9 +58,8 @@ def parse_title(source: str, title: str, artist: str) -> ParsedTrack: label_pat = r"(?P \[(?P