diff --git a/music_kraken/objects/__init__.py b/music_kraken/objects/__init__.py index 7c7515d..2a85abf 100644 --- a/music_kraken/objects/__init__.py +++ b/music_kraken/objects/__init__.py @@ -1,27 +1,16 @@ from typing_extensions import TypeVar + +from .artwork import ArtworkCollection +from .collection import Collection +from .contact import Contact +from .country import Country +from .formatted_text import FormattedText +from .metadata import ID3Timestamp +from .metadata import Mapping as ID3Mapping +from .metadata import Metadata from .option import Options - -from .metadata import Metadata, Mapping as ID3Mapping, ID3Timestamp - +from .parents import OuterProxy +from .song import Album, Artist, Label, Lyrics, Song, Target from .source import Source, SourceType -from .song import ( - Song, - Album, - Artist, - Target, - Lyrics, - Label -) - -from .formatted_text import FormattedText -from .collection import Collection - -from .country import Country -from .contact import Contact - -from .parents import OuterProxy - -from .artwork import Artwork - DatabaseObject = OuterProxy diff --git a/music_kraken/objects/artwork.py b/music_kraken/objects/artwork.py index 6b5c096..eb5fa51 100644 --- a/music_kraken/objects/artwork.py +++ b/music_kraken/objects/artwork.py @@ -10,6 +10,7 @@ from .metadata import Mapping as id3Mapping from .metadata import Metadata from .parents import OuterProxy as Base + class ArtworkVariant(TypedDict): url: str width: int @@ -17,10 +18,10 @@ class ArtworkVariant(TypedDict): deviation: float -class Artwork: - def __init__(self, *variants: List[ArtworkVariant], parent_artworks: Set[Artwork] = None, crop_images: bool = True) -> None: +class ArtworkCollection: + def __init__(self, *variants: List[ArtworkVariant], parent_artworks: Set[ArtworkCollection] = None, crop_images: bool = True) -> None: self.crop_images: bool = crop_images - self.parent_artworks: Set[Artwork] = parent_artworks or set() + self.parent_artworks: Set[ArtworkCollection] = parent_artworks or set() self._variant_mapping: Dict[str, ArtworkVariant] = {} @@ -58,7 +59,7 @@ class Artwork: def get_variant_name(self, variant: ArtworkVariant) -> str: return f"artwork_{variant['width']}x{variant['height']}_{hash_url(variant['url']).replace('/', '_')}" - def __merge__(self, other: Artwork, **kwargs) -> None: + def __merge__(self, other: ArtworkCollection, **kwargs) -> None: self.parent_artworks.update(other.parent_artworks) for key, value in other._variant_mapping.items(): @@ -68,11 +69,11 @@ class Artwork: def __hash__(self) -> int: return id(self) - def __eq__(self, other: Artwork) -> bool: + def __eq__(self, other: ArtworkCollection) -> bool: if hash(self) == hash(other): return True - if not isinstance(other, Artwork): + if not isinstance(other, ArtworkCollection): return False return any(a == b for a, b in zip(self._variant_mapping.keys(), other._variant_mapping.keys())) diff --git a/music_kraken/objects/song.py b/music_kraken/objects/song.py index 48b52c4..567fdd5 100644 --- a/music_kraken/objects/song.py +++ b/music_kraken/objects/song.py @@ -1,35 +1,32 @@ from __future__ import annotations +import copy import random from collections import defaultdict -from typing import List, Optional, Dict, Tuple, Type, Union -import copy +from typing import Dict, List, Optional, Tuple, Type, Union import pycountry -from ..utils.enums.album import AlbumType, AlbumStatus -from .collection import Collection, AppendHookArguments -from .formatted_text import FormattedText -from .lyrics import Lyrics -from .contact import Contact -from .artwork import Artwork -from .metadata import ( - Mapping as id3Mapping, - ID3Timestamp, - Metadata -) -from .option import Options -from .parents import OuterProxy, P -from .source import Source, SourceCollection -from .target import Target -from .country import Language, Country +from ..utils.config import main_settings +from ..utils.enums.album import AlbumStatus, AlbumType +from ..utils.enums.colors import BColors from ..utils.shared import DEBUG_PRINT_ID from ..utils.string_processing import unify - +from .artwork import ArtworkCollection +from .collection import AppendHookArguments, Collection +from .contact import Contact +from .country import Country, Language +from .formatted_text import FormattedText +from .lyrics import Lyrics +from .metadata import ID3Timestamp +from .metadata import Mapping as id3Mapping +from .metadata import Metadata +from .option import Options +from .parents import OuterProxy from .parents import OuterProxy as Base - -from ..utils.config import main_settings -from ..utils.enums.colors import BColors +from .parents import P +from .source import Source, SourceCollection +from .target import Target """ All Objects dependent @@ -89,7 +86,7 @@ class Song(Base): genre: str note: FormattedText tracksort: int - artwork: Artwork + artwork: ArtworkCollection source_collection: SourceCollection target_collection: Collection[Target] @@ -105,7 +102,7 @@ class Song(Base): "source_collection": SourceCollection, "target_collection": Collection, "lyrics_collection": Collection, - "artwork": Artwork, + "artwork": ArtworkCollection, "album_collection": Collection, "artist_collection": Collection, @@ -133,7 +130,7 @@ class Song(Base): feature_artist_list: List[Artist] = None, album_list: List[Album] = None, tracksort: int = 0, - artwork: Optional[Artwork] = None, + artwork: Optional[ArtworkCollection] = None, **kwargs ) -> None: real_kwargs = copy.copy(locals()) @@ -258,7 +255,7 @@ class Album(Base): albumsort: int notes: FormattedText - artwork: Artwork + artwork: ArtworkCollection source_collection: SourceCollection song_collection: Collection[Song] @@ -278,7 +275,7 @@ class Album(Base): "date": ID3Timestamp, "notes": FormattedText, - "artwork": lambda: Artwork(crop_images=False), + "artwork": lambda: ArtworkCollection(crop_images=False), "source_collection": SourceCollection, "song_collection": Collection, @@ -301,7 +298,7 @@ class Album(Base): barcode: str = None, albumsort: int = None, notes: FormattedText = None, - artwork: Artwork = None, + artwork: ArtworkCollection = None, source_list: List[Source] = None, artist_list: List[Artist] = None, song_list: List[Song] = None, @@ -498,7 +495,7 @@ class Artist(Base): general_genre: str unformatted_location: str - artwork: Artwork + artwork: ArtworkCollection source_collection: SourceCollection contact_collection: Collection[Contact] @@ -516,7 +513,7 @@ class Artist(Base): "lyrical_themes": list, "general_genre": lambda: "", - "artwork": Artwork, + "artwork": ArtworkCollection, "source_collection": SourceCollection, "album_collection": Collection, @@ -536,7 +533,7 @@ class Artist(Base): notes: FormattedText = None, lyrical_themes: List[str] = None, general_genre: str = None, - artwork: Artwork = None, + artwork: ArtworkCollection = None, unformatted_location: str = None, source_list: List[Source] = None, contact_list: List[Contact] = None, diff --git a/music_kraken/pages/bandcamp.py b/music_kraken/pages/bandcamp.py index 2125c6e..0941f5e 100644 --- a/music_kraken/pages/bandcamp.py +++ b/music_kraken/pages/bandcamp.py @@ -1,33 +1,22 @@ -from typing import List, Optional, Type -from urllib.parse import urlparse, urlunparse import json from enum import Enum -from bs4 import BeautifulSoup -import pycountry +from typing import List, Optional, Type +from urllib.parse import urlparse, urlunparse + +import pycountry +from bs4 import BeautifulSoup -from ..objects import Source, DatabaseObject -from .abstract import Page -from ..objects import ( - Artist, - Source, - SourceType, - Song, - Album, - Label, - Target, - Contact, - ID3Timestamp, - Lyrics, - FormattedText, - Artwork, -) from ..connection import Connection +from ..objects import (Album, Artist, ArtworkCollection, Contact, + DatabaseObject, FormattedText, ID3Timestamp, Label, + Lyrics, Song, Source, SourceType, Target) from ..utils import dump_to_file -from ..utils.enums import SourceType, ALL_SOURCE_TYPES -from ..utils.support_classes.download_result import DownloadResult -from ..utils.string_processing import clean_song_title -from ..utils.config import main_settings, logging_settings +from ..utils.config import logging_settings, main_settings +from ..utils.enums import ALL_SOURCE_TYPES, SourceType from ..utils.shared import DEBUG +from ..utils.string_processing import clean_song_title +from ..utils.support_classes.download_result import DownloadResult +from .abstract import Page if DEBUG: from ..utils import dump_to_file @@ -258,7 +247,7 @@ class Bandcamp(Page): artist.source_collection.append(source) return artist - def _parse_track_element(self, track: dict, artwork: Artwork) -> Optional[Song]: + def _parse_track_element(self, track: dict, artwork: ArtworkCollection) -> Optional[Song]: lyrics_list: List[Lyrics] = [] _lyrics: Optional[str] = track.get("item", {}).get("recordingOf", {}).get("lyrics", {}).get("text") @@ -308,7 +297,7 @@ class Bandcamp(Page): )] ) - artwork: Artwork = Artwork() + artwork: ArtworkCollection = ArtworkCollection() def _get_artwork_url(_data: dict) -> Optional[str]: if "image" in _data: diff --git a/music_kraken/pages/genius.py b/music_kraken/pages/genius.py index 5afa556..df7bafc 100644 --- a/music_kraken/pages/genius.py +++ b/music_kraken/pages/genius.py @@ -1,33 +1,22 @@ -from typing import List, Optional, Type -from urllib.parse import urlparse, urlunparse, urlencode import json from enum import Enum -from bs4 import BeautifulSoup -import pycountry +from typing import List, Optional, Type +from urllib.parse import urlencode, urlparse, urlunparse + +import pycountry +from bs4 import BeautifulSoup -from ..objects import Source, DatabaseObject -from .abstract import Page -from ..objects import ( - Artist, - Source, - SourceType, - Song, - Album, - Label, - Target, - Contact, - ID3Timestamp, - Lyrics, - FormattedText, - Artwork, -) from ..connection import Connection +from ..objects import (Album, Artist, ArtworkCollection, Contact, + DatabaseObject, FormattedText, ID3Timestamp, Label, + Lyrics, Song, Source, SourceType, Target) from ..utils import dump_to_file, traverse_json_path -from ..utils.enums import SourceType, ALL_SOURCE_TYPES -from ..utils.support_classes.download_result import DownloadResult -from ..utils.string_processing import clean_song_title -from ..utils.config import main_settings, logging_settings +from ..utils.config import logging_settings, main_settings +from ..utils.enums import ALL_SOURCE_TYPES, SourceType from ..utils.shared import DEBUG +from ..utils.string_processing import clean_song_title +from ..utils.support_classes.download_result import DownloadResult +from .abstract import Page if DEBUG: from ..utils import dump_to_file @@ -56,7 +45,7 @@ class Genius(Page): return Song - def add_to_artwork(self, artwork: Artwork, url: str): + def add_to_artwork(self, artwork: ArtworkCollection, url: str): if url is None: return @@ -83,7 +72,7 @@ class Genius(Page): return None object_type = data.get("_type") - artwork = Artwork() + artwork = ArtworkCollection() self.add_to_artwork(artwork, data.get("header_image_url")) self.add_to_artwork(artwork, data.get("image_url")) diff --git a/music_kraken/pages/musify.py b/music_kraken/pages/musify.py index c8e51d1..d636bce 100644 --- a/music_kraken/pages/musify.py +++ b/music_kraken/pages/musify.py @@ -1,34 +1,24 @@ from collections import defaultdict from dataclasses import dataclass from enum import Enum -from typing import List, Optional, Type, Union, Generator, Dict, Any +from typing import Any, Dict, Generator, List, Optional, Type, Union from urllib.parse import urlparse import pycountry from bs4 import BeautifulSoup from ..connection import Connection -from .abstract import Page -from ..utils.enums import SourceType, ALL_SOURCE_TYPES -from ..utils.enums.album import AlbumType, AlbumStatus -from ..objects import ( - Artist, - Source, - Song, - Album, - ID3Timestamp, - FormattedText, - Label, - Target, - DatabaseObject, - Lyrics, - Artwork -) +from ..objects import (Album, Artist, ArtworkCollection, DatabaseObject, + FormattedText, ID3Timestamp, Label, Lyrics, Song, + Source, Target) +from ..utils import shared, string_processing from ..utils.config import logging_settings, main_settings -from ..utils import string_processing, shared +from ..utils.enums import ALL_SOURCE_TYPES, SourceType +from ..utils.enums.album import AlbumStatus, AlbumType from ..utils.string_processing import clean_song_title -from ..utils.support_classes.query import Query from ..utils.support_classes.download_result import DownloadResult +from ..utils.support_classes.query import Query +from .abstract import Page """ https://musify.club/artist/ghost-bath-280348?_pjax=#bodyContent @@ -486,7 +476,7 @@ class Musify(Page): track_name = list_points[4].text.strip() # album artwork - artwork: Artwork = Artwork() + artwork: ArtworkCollection = ArtworkCollection() album_image_element_list: List[BeautifulSoup] = soup.find_all("img", {"class": "album-img"}) for album_image_element in album_image_element_list: artwork.append(url=album_image_element.get("data-src", album_image_element.get("src"))) @@ -755,7 +745,7 @@ class Musify(Page): self.LOGGER.debug(f"Raw datetime doesn't match time format %Y-%m-%d: {raw_datetime}") # album artwork - album_artwork: Artwork = Artwork() + album_artwork: ArtworkCollection = ArtworkCollection() album_artwork_list: List[BeautifulSoup] = soup.find_all("img", {"class":"artist-img"}) for album_artwork in album_artwork_list: album_artwork.append(url=album_artwork.get("data-src", album_artwork.get("src"))) @@ -924,7 +914,7 @@ class Musify(Page): notes.html = note_soup.decode_contents() # get artist profile artwork - main_artist_artwork: Artwork = Artwork() + main_artist_artwork: ArtworkCollection = ArtworkCollection() artist_image_element_list: List[BeautifulSoup] = soup.find_all("img", {"class":"artist-img"}) for artist_image_element in artist_image_element_list: main_artist_artwork.append(url=artist_image_element.get("data-src", artist_image_element.get("src"))) diff --git a/music_kraken/pages/youtube_music/youtube_music.py b/music_kraken/pages/youtube_music/youtube_music.py index 08e2207..9780977 100644 --- a/music_kraken/pages/youtube_music/youtube_music.py +++ b/music_kraken/pages/youtube_music/youtube_music.py @@ -1,46 +1,33 @@ -from __future__ import unicode_literals, annotations +from __future__ import annotations, unicode_literals -from typing import Dict, List, Optional, Set, Type -from urllib.parse import urlparse, urlunparse, quote, parse_qs, urlencode +import json import logging import random -import json -from dataclasses import dataclass import re -from functools import lru_cache from collections import defaultdict +from dataclasses import dataclass +from functools import lru_cache +from typing import Dict, List, Optional, Set, Type +from urllib.parse import parse_qs, quote, urlencode, urlparse, urlunparse import youtube_dl from youtube_dl.extractor.youtube import YoutubeIE from youtube_dl.utils import DownloadError +from ...connection import Connection +from ...objects import Album, Artist, ArtworkCollection +from ...objects import DatabaseObject as DataObject +from ...objects import (FormattedText, ID3Timestamp, Label, Lyrics, Song, + Source, Target) +from ...utils import dump_to_file, get_current_millis, traverse_json_path +from ...utils.config import logging_settings, main_settings, youtube_settings +from ...utils.enums import ALL_SOURCE_TYPES, SourceType +from ...utils.enums.album import AlbumType from ...utils.exception.config import SettingValueError -from ...utils.config import main_settings, youtube_settings, logging_settings from ...utils.shared import DEBUG, DEBUG_YOUTUBE_INITIALIZING from ...utils.string_processing import clean_song_title -from ...utils import get_current_millis, traverse_json_path - -from ...utils import dump_to_file - -from ..abstract import Page -from ...objects import ( - DatabaseObject as DataObject, - Source, - FormattedText, - ID3Timestamp, - Artwork, - Artist, - Song, - Album, - Label, - Target, - Lyrics, -) -from ...connection import Connection -from ...utils.enums import SourceType, ALL_SOURCE_TYPES -from ...utils.enums.album import AlbumType from ...utils.support_classes.download_result import DownloadResult - +from ..abstract import Page from ._list_render import parse_renderer from ._music_object_render import parse_run_element from .super_youtube import SuperYouTube @@ -646,7 +633,7 @@ class YoutubeMusic(SuperYouTube): note=ydl_res.get("descriptions"), album_list=album_list, length=int(ydl_res.get("duration", 0)) * 1000, - artwork=Artwork(*ydl_res.get("thumbnails", [])), + artwork=ArtworkCollection(*ydl_res.get("thumbnails", [])), artist_list=artist_list, source_list=[Source( self.SOURCE_TYPE,