fix: fixed youtube music

This commit is contained in:
Hazel 2024-01-15 12:48:36 +01:00
parent 564621b332
commit 6a8374d595
7 changed files with 216 additions and 136 deletions

View File

@ -42,9 +42,8 @@ if __name__ == "__main__":
] ]
bandcamp_test = [ bandcamp_test = [
"s: #a Ghost Bath", "s: #a Only Smile",
"0", "d: 1",
"d: 4"
] ]

View File

@ -16,7 +16,6 @@ from ..download.page_attributes import Pages
from ..pages import Page from ..pages import Page
from ..objects import Song, Album, Artist, DatabaseObject from ..objects import Song, Album, Artist, DatabaseObject
""" """
This is the implementation of the Shell This is the implementation of the Shell
@ -107,6 +106,7 @@ def get_existing_genre() -> List[str]:
return existing_genres return existing_genres
def get_genre(): def get_genre():
existing_genres = get_existing_genre() existing_genres = get_existing_genre()
for i, genre_option in enumerate(existing_genres): for i, genre_option in enumerate(existing_genres):
@ -137,7 +137,6 @@ def help_message():
print() print()
class Downloader: class Downloader:
def __init__( def __init__(
self, self,
@ -165,7 +164,6 @@ class Downloader:
print(f"Downloading to: \"{self.genre}\"") print(f"Downloading to: \"{self.genre}\"")
print() print()
def print_current_options(self): def print_current_options(self):
self.page_dict = dict() self.page_dict = dict()
@ -176,8 +174,9 @@ class Downloader:
if isinstance(option, Option): if isinstance(option, Option):
print(f"{option.index:0{self.option_digits}} {option.music_object.option_string}") print(f"{option.index:0{self.option_digits}} {option.music_object.option_string}")
else: else:
prefix = ALPHABET[page_count%len(ALPHABET)] prefix = ALPHABET[page_count % len(ALPHABET)]
print(f"({prefix}) ------------------------{option.__name__:{PAGE_NAME_FILL}<{MAX_PAGE_LEN}}------------") print(
f"({prefix}) ------------------------{option.__name__:{PAGE_NAME_FILL}<{MAX_PAGE_LEN}}------------")
self.page_dict[prefix] = option self.page_dict[prefix] = option
self.page_dict[option.__name__] = option self.page_dict[option.__name__] = option
@ -255,7 +254,7 @@ class Downloader:
latest_key: str = None latest_key: str = None
for i in range(len(query) - 1): for i in range(len(query) - 1):
current_char = query[i] current_char = query[i]
next_char = query[i+1] next_char = query[i + 1]
if skip_next: if skip_next:
skip_next = False skip_next = False
@ -285,7 +284,6 @@ class Downloader:
if latest_key is not None: if latest_key is not None:
key_text[latest_key] = new_text key_text[latest_key] = new_text
parsed_query: Query = self._process_parsed(key_text, query) parsed_query: Query = self._process_parsed(key_text, query)
self.set_current_options(self.pages.search(parsed_query)) self.set_current_options(self.pages.search(parsed_query))
@ -311,7 +309,6 @@ class Downloader:
self.print_current_options() self.print_current_options()
def download(self, download_str: str, download_all: bool = False) -> bool: def download(self, download_str: str, download_all: bool = False) -> bool:
to_download: List[DatabaseObject] = [] to_download: List[DatabaseObject] = []
@ -340,7 +337,8 @@ class Downloader:
_result_map: Dict[DatabaseObject, DownloadResult] = dict() _result_map: Dict[DatabaseObject, DownloadResult] = dict()
for database_object in to_download: for database_object in to_download:
r = self.pages.download(music_object=database_object, genre=self.genre, download_all=download_all, process_metadata_anyway=self.process_metadata_anyway) r = self.pages.download(music_object=database_object, genre=self.genre, download_all=download_all,
process_metadata_anyway=self.process_metadata_anyway)
_result_map[database_object] = r _result_map[database_object] = r
for music_object, result in _result_map.items(): for music_object, result in _result_map.items():
@ -387,6 +385,7 @@ class Downloader:
if self.process_input(input("> ")): if self.process_input(input("> ")):
return return
@cli_function @cli_function
def download( def download(
genre: str = None, genre: str = None,

View File

@ -99,7 +99,9 @@ class Pages:
if not isinstance(music_object, INDEPENDENT_DB_OBJECTS): if not isinstance(music_object, INDEPENDENT_DB_OBJECTS):
return DownloadResult(error_message=f"{type(music_object).__name__} can't be downloaded.") return DownloadResult(error_message=f"{type(music_object).__name__} can't be downloaded.")
_page_types = set() self.fetch_details(music_object)
_page_types = set(self._source_to_page)
for src in music_object.source_collection.source_pages: for src in music_object.source_collection.source_pages:
if src in self._source_to_page: if src in self._source_to_page:
_page_types.add(self._source_to_page[src]) _page_types.add(self._source_to_page[src])

View File

@ -315,7 +315,7 @@ class Collection(Generic[T]):
yield element yield element
def __merge__(self, __other: Collection, override: bool = False): def __merge__(self, __other: Collection, override: bool = False):
self.extend(__other.shallow_list, from_map=True) self.extend(__other._data, from_map=True)
def __getitem__(self, item: int): def __getitem__(self, item: int):
if item < len(self._data): if item < len(self._data):

View File

@ -1,6 +1,7 @@
from __future__ import annotations from __future__ import annotations
import random import random
from collections import defaultdict
from functools import lru_cache from functools import lru_cache
from typing import Optional, Dict, Tuple, List, Type, Generic, Any, TypeVar, Set from typing import Optional, Dict, Tuple, List, Type, Generic, Any, TypeVar, Set
@ -131,6 +132,18 @@ class OuterProxy:
return super().__setattr__(__name, __value) return super().__setattr__(__name, __value)
def _add_other_db_objects(self, object_type: Type[OuterProxy], object_list: List[OuterProxy]):
pass
def add_list_of_other_objects(self, object_list: List[OuterProxy]):
d: Dict[Type[OuterProxy], List[OuterProxy]] = defaultdict(list)
for db_object in object_list:
d[type(db_object)].append(db_object)
for key, value in d.items():
self._add_other_db_objects(key, value)
def __hash__(self): def __hash__(self):
""" """
:raise: IsDynamicException :raise: IsDynamicException

View File

@ -95,6 +95,22 @@ class Song(Base):
"feature_song_collection": self "feature_song_collection": self
} }
def _add_other_db_objects(self, object_type: Type[OuterProxy], object_list: List[OuterProxy]):
if object_type is Song:
return
if isinstance(object_list, Lyrics):
self.lyrics_collection.extend(object_list)
return
if isinstance(object_list, Artist):
self.main_artist_collection.extend(object_list)
return
if isinstance(object_list, Album):
self.album_collection.extend(object_list)
return
@property @property
def indexing_values(self) -> List[Tuple[str, object]]: def indexing_values(self) -> List[Tuple[str, object]]:
return [ return [
@ -229,6 +245,22 @@ class Album(Base):
"main_artist_collection": self.artist_collection "main_artist_collection": self.artist_collection
} }
def _add_other_db_objects(self, object_type: Type[OuterProxy], object_list: List[OuterProxy]):
if object_type is Song:
self.song_collection.extend(object_list)
return
if object_type is Artist:
self.artist_collection.extend(object_list)
return
if object_type is Album:
return
if object_type is Label:
self.label_collection.extend(object_list)
return
@property @property
def indexing_values(self) -> List[Tuple[str, object]]: def indexing_values(self) -> List[Tuple[str, object]]:
return [ return [
@ -436,6 +468,23 @@ class Artist(Base):
"current_artist_collection": self "current_artist_collection": self
} }
def _add_other_db_objects(self, object_type: Type[OuterProxy], object_list: List[OuterProxy]):
if object_type is Song:
# this doesn't really make sense
# self.feature_song_collection.extend(object_list)
return
if object_type is Artist:
return
if object_type is Album:
self.main_album_collection.extend(object_list)
return
if object_type is Label:
self.label_collection.extend(object_list)
return
@property @property
def options(self) -> List[P]: def options(self) -> List[P]:
options = [self, *self.main_album_collection.shallow_list, *self.feature_album] options = [self, *self.main_album_collection.shallow_list, *self.feature_album]
@ -618,6 +667,18 @@ class Label(Base):
*[('url', source.url) for source in self.source_collection] *[('url', source.url) for source in self.source_collection]
] ]
def _add_other_db_objects(self, object_type: Type[OuterProxy], object_list: List[OuterProxy]):
if object_type is Song:
return
if object_type is Artist:
self.current_artist_collection.extend(object_list)
return
if object_type is Album:
self.album_collection.extend(object_list)
return
@property @property
def options(self) -> List[P]: def options(self) -> List[P]:
options = [self] options = [self]

View File

@ -28,7 +28,6 @@ from ..utils.support_classes.query import Query
from ..utils.support_classes.download_result import DownloadResult from ..utils.support_classes.download_result import DownloadResult
from ..utils.string_processing import fit_to_file_system from ..utils.string_processing import fit_to_file_system
INDEPENDENT_DB_OBJECTS = Union[Label, Album, Artist, Song] INDEPENDENT_DB_OBJECTS = Union[Label, Album, Artist, Song]
INDEPENDENT_DB_TYPES = Union[Type[Song], Type[Album], Type[Artist], Type[Label]] INDEPENDENT_DB_TYPES = Union[Type[Song], Type[Album], Type[Artist], Type[Label]]
@ -133,6 +132,7 @@ def _clean_song(song: Song, collections: Dict[INDEPENDENT_DB_TYPES, Collection])
_clean_collection(song.feature_artist_collection, collections) _clean_collection(song.feature_artist_collection, collections)
_clean_collection(song.main_artist_collection, collections) _clean_collection(song.main_artist_collection, collections)
def clean_object(dirty_object: DatabaseObject) -> DatabaseObject: def clean_object(dirty_object: DatabaseObject) -> DatabaseObject:
if isinstance(dirty_object, INDEPENDENT_DB_OBJECTS): if isinstance(dirty_object, INDEPENDENT_DB_OBJECTS):
collections = { collections = {
@ -148,12 +148,14 @@ def clean_object(dirty_object: DatabaseObject) -> DatabaseObject:
_clean_music_object(dirty_object, collections) _clean_music_object(dirty_object, collections)
return dirty_object return dirty_object
def build_new_object(new_object: DatabaseObject) -> DatabaseObject: def build_new_object(new_object: DatabaseObject) -> DatabaseObject:
new_object = clean_object(new_object) new_object = clean_object(new_object)
new_object.compile(merge_into=False) new_object.compile(merge_into=False)
return new_object return new_object
def merge_together(old_object: DatabaseObject, new_object: DatabaseObject, do_compile: bool = True) -> DatabaseObject: def merge_together(old_object: DatabaseObject, new_object: DatabaseObject, do_compile: bool = True) -> DatabaseObject:
new_object = clean_object(new_object) new_object = clean_object(new_object)
@ -176,7 +178,6 @@ class Page:
# set this to true, if all song details can also be fetched by fetching album details # set this to true, if all song details can also be fetched by fetching album details
NO_ADDITIONAL_DATA_FROM_SONG = False NO_ADDITIONAL_DATA_FROM_SONG = False
def get_source_type(self, source: Source) -> Optional[Type[DatabaseObject]]: def get_source_type(self, source: Source) -> Optional[Type[DatabaseObject]]:
return None return None
@ -221,8 +222,8 @@ class Page:
def song_search(self, song: Song) -> List[Song]: def song_search(self, song: Song) -> List[Song]:
return [] return []
def fetch_details(self, music_object: DatabaseObject, stop_at_level: int = 1,
def fetch_details(self, music_object: DatabaseObject, stop_at_level: int = 1, post_process: bool = True) -> DatabaseObject: post_process: bool = True) -> DatabaseObject:
""" """
when a music object with lacking data is passed in, it returns when a music object with lacking data is passed in, it returns
the SAME object **(no copy)** with more detailed data. the SAME object **(no copy)** with more detailed data.
@ -263,7 +264,9 @@ class Page:
return music_object return music_object
def fetch_object_from_source(self, source: Source, stop_at_level: int = 2, enforce_type: Type[DatabaseObject] = None, post_process: bool = True) -> Optional[DatabaseObject]: def fetch_object_from_source(self, source: Source, stop_at_level: int = 2,
enforce_type: Type[DatabaseObject] = None, post_process: bool = True) -> Optional[
DatabaseObject]:
obj_type = self.get_source_type(source) obj_type = self.get_source_type(source)
if obj_type is None: if obj_type is None:
@ -294,7 +297,8 @@ class Page:
collection = music_object.__getattribute__(collection_str) collection = music_object.__getattribute__(collection_str)
for sub_element in collection: for sub_element in collection:
sub_element.merge(self.fetch_details(sub_element, stop_at_level=stop_at_level-1, post_process=False)) sub_element.merge(
self.fetch_details(sub_element, stop_at_level=stop_at_level - 1, post_process=False))
return music_object return music_object
@ -310,7 +314,8 @@ class Page:
def fetch_label(self, source: Source, stop_at_level: int = 1) -> Label: def fetch_label(self, source: Source, stop_at_level: int = 1) -> Label:
return Label() return Label()
def download(self, music_object: DatabaseObject, genre: str, download_all: bool = False, process_metadata_anyway: bool = False) -> DownloadResult: def download(self, music_object: DatabaseObject, genre: str, download_all: bool = False,
process_metadata_anyway: bool = False) -> DownloadResult:
naming_dict: NamingDict = NamingDict({"genre": genre}) naming_dict: NamingDict = NamingDict({"genre": genre})
def fill_naming_objects(naming_music_object: DatabaseObject): def fill_naming_objects(naming_music_object: DatabaseObject):
@ -330,8 +335,8 @@ class Page:
return self._download(music_object, naming_dict, download_all, process_metadata_anyway=process_metadata_anyway) return self._download(music_object, naming_dict, download_all, process_metadata_anyway=process_metadata_anyway)
def _download(self, music_object: DatabaseObject, naming_dict: NamingDict, download_all: bool = False,
def _download(self, music_object: DatabaseObject, naming_dict: NamingDict, download_all: bool = False, skip_details: bool = False, process_metadata_anyway: bool = False) -> DownloadResult: skip_details: bool = False, process_metadata_anyway: bool = False) -> DownloadResult:
skip_next_details = skip_details skip_next_details = skip_details
# Skips all releases, that are defined in shared.ALBUM_TYPE_BLACKLIST, if download_all is False # Skips all releases, that are defined in shared.ALBUM_TYPE_BLACKLIST, if download_all is False
@ -357,7 +362,9 @@ class Page:
sub_ordered_music_object: DatabaseObject sub_ordered_music_object: DatabaseObject
for sub_ordered_music_object in collection: for sub_ordered_music_object in collection:
download_result.merge(self._download(sub_ordered_music_object, naming_dict.copy(), download_all, skip_details=skip_next_details, process_metadata_anyway=process_metadata_anyway)) download_result.merge(self._download(sub_ordered_music_object, naming_dict.copy(), download_all,
skip_details=skip_next_details,
process_metadata_anyway=process_metadata_anyway))
return download_result return download_result
@ -378,7 +385,6 @@ class Page:
) )
) )
if song.target_collection.empty: if song.target_collection.empty:
song.target_collection.append(new_target) song.target_collection.append(new_target)
@ -416,9 +422,9 @@ class Page:
if not found_on_disc: if not found_on_disc:
r = self.download_song_to_target(source=source, target=temp_target, desc=song.title) r = self.download_song_to_target(source=source, target=temp_target, desc=song.title)
if not r.is_fatal_error: if not r.is_fatal_error:
r.merge(self._post_process_targets(song, temp_target, [] if found_on_disc else self.get_skip_intervals(song, source))) r.merge(self._post_process_targets(song, temp_target,
[] if found_on_disc else self.get_skip_intervals(song, source)))
return r return r