feat: bandcamp artist artwork
This commit is contained in:
parent
d83e40ed83
commit
7d23ecac06
@ -4,6 +4,8 @@ from pathlib import Path
|
|||||||
import re
|
import re
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
from . import FetchOptions, DownloadOptions
|
from . import FetchOptions, DownloadOptions
|
||||||
from .results import SearchResults
|
from .results import SearchResults
|
||||||
from ..objects import (
|
from ..objects import (
|
||||||
@ -17,6 +19,7 @@ from ..objects import (
|
|||||||
Artist,
|
Artist,
|
||||||
Label,
|
Label,
|
||||||
)
|
)
|
||||||
|
from ..objects.artwork import ArtworkVariant
|
||||||
from ..audio import write_metadata_to_target, correct_codec
|
from ..audio import write_metadata_to_target, correct_codec
|
||||||
from ..utils import output, BColors
|
from ..utils import output, BColors
|
||||||
from ..utils.string_processing import fit_to_file_system
|
from ..utils.string_processing import fit_to_file_system
|
||||||
@ -29,9 +32,11 @@ from ..utils.support_classes.download_result import DownloadResult
|
|||||||
from ..utils.exception import MKMissingNameException
|
from ..utils.exception import MKMissingNameException
|
||||||
from ..utils.exception.download import UrlNotFoundException
|
from ..utils.exception.download import UrlNotFoundException
|
||||||
from ..utils.shared import DEBUG_PAGES
|
from ..utils.shared import DEBUG_PAGES
|
||||||
|
from ..connection import Connection
|
||||||
|
|
||||||
from ..pages import Page, EncyclopaediaMetallum, Musify, YouTube, YoutubeMusic, Bandcamp, Genius, INDEPENDENT_DB_OBJECTS
|
from ..pages import Page, EncyclopaediaMetallum, Musify, YouTube, YoutubeMusic, Bandcamp, Genius, INDEPENDENT_DB_OBJECTS
|
||||||
|
|
||||||
|
artwork_connection: Connection = Connection()
|
||||||
|
|
||||||
ALL_PAGES: Set[Type[Page]] = {
|
ALL_PAGES: Set[Type[Page]] = {
|
||||||
# EncyclopaediaMetallum,
|
# EncyclopaediaMetallum,
|
||||||
@ -162,6 +167,59 @@ class Pages:
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def download_artwork_variant_to_target(self, artwork_variant: ArtworkVariant, target: Target):
|
||||||
|
|
||||||
|
r = artwork_connection.get(
|
||||||
|
url=artwork_variant["url"],
|
||||||
|
name=artwork_variant["url"],
|
||||||
|
)
|
||||||
|
|
||||||
|
temp_target: Target = Target.temp()
|
||||||
|
with temp_target.open("wb") as f:
|
||||||
|
f.write(r.content)
|
||||||
|
|
||||||
|
converted_target: Target = Target.temp(file_extension=main_settings["image_format"])
|
||||||
|
with Image.open(temp_target.file_path) as img:
|
||||||
|
# crop the image if it isn't square in the middle with minimum data loss
|
||||||
|
width, height = img.size
|
||||||
|
if width != height:
|
||||||
|
if width > height:
|
||||||
|
img = img.crop((width // 2 - height // 2, 0, width // 2 + height // 2, height))
|
||||||
|
else:
|
||||||
|
img = img.crop((0, height // 2 - width // 2, width, height // 2 + width // 2))
|
||||||
|
|
||||||
|
# resize the image to the preferred resolution
|
||||||
|
img.thumbnail((main_settings["preferred_artwork_resolution"], main_settings["preferred_artwork_resolution"]))
|
||||||
|
|
||||||
|
# https://stackoverflow.com/a/59476938/16804841
|
||||||
|
if img.mode != 'RGB':
|
||||||
|
img = img.convert('RGB')
|
||||||
|
|
||||||
|
img.save(converted_target.file_path, main_settings["image_format"])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _fetch_artist_artwork(self, artist: Artist, naming: dict):
|
||||||
|
naming: Dict[str, List[str]] = defaultdict(list, naming)
|
||||||
|
naming["artist"].append(artist.name)
|
||||||
|
naming["label"].extend([l.title_value for l in artist.label_collection])
|
||||||
|
# removing duplicates from the naming, and process the strings
|
||||||
|
for key, value in naming.items():
|
||||||
|
# https://stackoverflow.com/a/17016257
|
||||||
|
naming[key] = list(dict.fromkeys(value))
|
||||||
|
|
||||||
|
artwork: Artwork = artist.artwork
|
||||||
|
for image_number, variant in enumerate(artwork):
|
||||||
|
naming["image_number"] = [str(image_number)]
|
||||||
|
|
||||||
|
url: str = variant["url"]
|
||||||
|
|
||||||
|
target = Target(
|
||||||
|
relative_to_music_dir=True,
|
||||||
|
file_path=Path(self._parse_path_template(main_settings["artist_artwork_path"], naming=naming))
|
||||||
|
)
|
||||||
|
self.download_artwork_variant_to_target(variant, target)
|
||||||
|
|
||||||
def download(self, data_object: DataObject, genre: str, **kwargs) -> DownloadResult:
|
def download(self, data_object: DataObject, genre: str, **kwargs) -> DownloadResult:
|
||||||
# fetch the given object
|
# fetch the given object
|
||||||
self.fetch_details(data_object)
|
self.fetch_details(data_object)
|
||||||
@ -186,6 +244,16 @@ class Pages:
|
|||||||
|
|
||||||
kwargs["fetched_upwards"] = True
|
kwargs["fetched_upwards"] = True
|
||||||
|
|
||||||
|
naming = kwargs.get("naming", {
|
||||||
|
"genre": [genre],
|
||||||
|
"audio_format": [main_settings["audio_format"]],
|
||||||
|
"image_format": [main_settings["image_format"]]
|
||||||
|
})
|
||||||
|
|
||||||
|
# download artist artwork
|
||||||
|
if isinstance(data_object, Artist):
|
||||||
|
self._fetch_artist_artwork(artist=data_object, naming=naming)
|
||||||
|
|
||||||
# download all children
|
# download all children
|
||||||
download_result: DownloadResult = DownloadResult()
|
download_result: DownloadResult = DownloadResult()
|
||||||
for c in data_object.get_child_collections():
|
for c in data_object.get_child_collections():
|
||||||
@ -203,10 +271,7 @@ class Pages:
|
|||||||
I am able to do that, because duplicate values are removed later on.
|
I am able to do that, because duplicate values are removed later on.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self._download_song(data_object, naming={
|
self._download_song(data_object, naming=naming)
|
||||||
"genre": [genre],
|
|
||||||
"audio_format": [main_settings["audio_format"]],
|
|
||||||
})
|
|
||||||
|
|
||||||
return download_result
|
return download_result
|
||||||
|
|
||||||
@ -326,3 +391,5 @@ class Pages:
|
|||||||
_actual_page = self._source_to_page[source.source_type]
|
_actual_page = self._source_to_page[source.source_type]
|
||||||
|
|
||||||
return _actual_page, self._page_instances[_actual_page].fetch_object_from_source(source=source, stop_at_level=stop_at_level)
|
return _actual_page, self._page_instances[_actual_page].fetch_object_from_source(source=source, stop_at_level=stop_at_level)
|
||||||
|
|
||||||
|
|
@ -10,7 +10,6 @@ from .metadata import Mapping as id3Mapping
|
|||||||
from .metadata import Metadata
|
from .metadata import Metadata
|
||||||
from .parents import OuterProxy as Base
|
from .parents import OuterProxy as Base
|
||||||
|
|
||||||
|
|
||||||
class ArtworkVariant(TypedDict):
|
class ArtworkVariant(TypedDict):
|
||||||
url: str
|
url: str
|
||||||
width: int
|
width: int
|
||||||
@ -76,3 +75,8 @@ class Artwork:
|
|||||||
if not isinstance(other, Artwork):
|
if not isinstance(other, Artwork):
|
||||||
return False
|
return False
|
||||||
return any(a == b for a, b in zip(self._variant_mapping.keys(), other._variant_mapping.keys()))
|
return any(a == b for a, b in zip(self._variant_mapping.keys(), other._variant_mapping.keys()))
|
||||||
|
|
||||||
|
def __iter__(self) -> Generator[ArtworkVariant, None, None]:
|
||||||
|
yield from self._variant_mapping.values()
|
||||||
|
|
||||||
|
|
@ -239,6 +239,11 @@ class Bandcamp(Page):
|
|||||||
for subsoup in html_music_grid.find_all("li"):
|
for subsoup in html_music_grid.find_all("li"):
|
||||||
artist.album_collection.append(self._parse_album(soup=subsoup, initial_source=source))
|
artist.album_collection.append(self._parse_album(soup=subsoup, initial_source=source))
|
||||||
|
|
||||||
|
# artist artwork
|
||||||
|
artist_artwork: BeautifulSoup = soup.find("img", {"class":"band-photo"})
|
||||||
|
if artist_artwork is not None:
|
||||||
|
artist.artwork.append(artist_artwork.get("data-src", artist_artwork.get("src")))
|
||||||
|
|
||||||
for i, data_blob_soup in enumerate(soup.find_all("div", {"id": ["pagedata", "collectors-data"]})):
|
for i, data_blob_soup in enumerate(soup.find_all("div", {"id": ["pagedata", "collectors-data"]})):
|
||||||
data_blob = data_blob_soup["data-blob"]
|
data_blob = data_blob_soup["data-blob"]
|
||||||
|
|
||||||
@ -316,7 +321,6 @@ class Bandcamp(Page):
|
|||||||
artwork.append(url=_artwork_url, width=350, height=350)
|
artwork.append(url=_artwork_url, width=350, height=350)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
for i, track_json in enumerate(data.get("track", {}).get("itemListElement", [])):
|
for i, track_json in enumerate(data.get("track", {}).get("itemListElement", [])):
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
dump_to_file(f"album_track_{i}.json", json.dumps(track_json), is_json=True, exit_after_dump=False)
|
dump_to_file(f"album_track_{i}.json", json.dumps(track_json), is_json=True, exit_after_dump=False)
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
|
|
||||||
from .config import Config
|
from .config import Config
|
||||||
from .config_files import (
|
from .config_files import main_config, logging_config, youtube_config
|
||||||
main_config,
|
|
||||||
logging_config,
|
|
||||||
youtube_config,
|
|
||||||
)
|
|
||||||
|
|
||||||
_sections: Tuple[Config, ...] = (
|
_sections: Tuple[Config, ...] = (
|
||||||
main_config.config,
|
main_config.config,
|
||||||
|
@ -18,7 +18,7 @@ config = Config((
|
|||||||
AudioFormatAttribute(name="audio_format", default_value="mp3", description="""Music Kraken will stream the audio into this format.
|
AudioFormatAttribute(name="audio_format", default_value="mp3", description="""Music Kraken will stream the audio into this format.
|
||||||
You can use Audio formats which support ID3.2 and ID3.1,
|
You can use Audio formats which support ID3.2 and ID3.1,
|
||||||
but you will have cleaner Metadata using ID3.2."""),
|
but you will have cleaner Metadata using ID3.2."""),
|
||||||
Attribute(name="image_format", default_value="jpg", description="This Changes the format in which images are getting downloaded")
|
Attribute(name="image_format", default_value="jpeg", description="This Changes the format in which images are getting downloaded"),
|
||||||
|
|
||||||
Attribute(name="result_history", default_value=True, description="""If enabled, you can go back to the previous results.
|
Attribute(name="result_history", default_value=True, description="""If enabled, you can go back to the previous results.
|
||||||
The consequence is a higher meory consumption, because every result is saved."""),
|
The consequence is a higher meory consumption, because every result is saved."""),
|
||||||
@ -29,7 +29,7 @@ The further you choose to be able to go back, the higher the memory usage.
|
|||||||
EmptyLine(),
|
EmptyLine(),
|
||||||
|
|
||||||
Attribute(name="preferred_artwork_resolution", default_value=1000),
|
Attribute(name="preferred_artwork_resolution", default_value=1000),
|
||||||
Attribute(name="download_artist_artworks", default_value=True, description="Changes if the artists Profile picture is being downloaded."),
|
Attribute(name="download_artist_artworks", default_value=True, description="Enables the fetching of artist galleries."),
|
||||||
|
|
||||||
EmptyLine(),
|
EmptyLine(),
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ This means for example, the Studio Albums and EP's are always in front of Single
|
|||||||
- album_type
|
- album_type
|
||||||
The folder music kraken should put the songs into."""),
|
The folder music kraken should put the songs into."""),
|
||||||
Attribute(name="download_file", default_value="{song}.{audio_format}", description="The filename of the audio file."),
|
Attribute(name="download_file", default_value="{song}.{audio_format}", description="The filename of the audio file."),
|
||||||
Attribute(name="artist_artwork_path" default_value="{genre}/{artist}/{artist}.{image_format}", description="The Path to download artist images to."),
|
Attribute(name="artist_artwork_path", default_value="{genre}/{artist}/{artist}_{image_number}.{image_format}", description="The Path to download artist images to."),
|
||||||
SelectAttribute(name="album_type_blacklist", default_value=[
|
SelectAttribute(name="album_type_blacklist", default_value=[
|
||||||
"Compilation Album",
|
"Compilation Album",
|
||||||
"Live Album",
|
"Live Album",
|
||||||
|
Loading…
Reference in New Issue
Block a user