diff --git a/README.md b/README.md index e4c1446..5f8c11f 100644 --- a/README.md +++ b/README.md @@ -209,10 +209,10 @@ Label }o--o{ Artist : LabelSong ``` -Looks way more managebal, doesn't it? +Looks way more manageable, doesn't it? The reason every relation here is a `n:m` *(many to many)* relation is not, that it makes sense in the aspekt of modeling reality, but to be able to put data from many Sources in the same Data Model. -Every Service models Data a bit different, and projecting a one to many relationship to a many to many relationship without data loss is easy. The other way around it is basically impossible +Every Service models Data a bit different, and projecting a one-to-many relationship to a many to many relationship without data loss is easy. The other way around it is basically impossible ## Data Objects @@ -222,60 +222,68 @@ Every Service models Data a bit different, and projecting a one to many relation ```python # importing the libraries I build on +from music_kraken import objects + import pycountry -# importing the custom dataclasses -from music_kraken import ( - Song, - Lyrics, - Target, - Source, - Album, - Artist, - # the custom date class - ID3Timestamp, - - # the enums (I will elaborate on later) - SourcePages, - SourceTypes -) - - -song_object = Song( +song = objects.Song( genre="HS Core", title="Vein Deep in the Solution", length=666, isrc="US-S1Z-99-00001", tracksort=2, - target=Target(file="song.mp3", path="~/Music"), - lyrics=[ - Lyrics(text="these are some depressive lyrics", language="en"), - Lyrics(text="test", language="en") + target=[ + objects.Target(file="song.mp3", path="example") ], - sources=[ - Source(SourcePages.YOUTUBE, "https://youtu.be/dfnsdajlhkjhsd"), - Source(SourcePages.MUSIFY, "https://ln.topdf.de/Music-Kraken/") + lyrics_list=[ + objects.Lyrics(text="these are some depressive lyrics", language="en"), + objects.Lyrics(text="Dies sind depressive Lyrics", language="de") + ], + source_list=[ + objects.Source(objects.SourcePages.YOUTUBE, "https://youtu.be/dfnsdajlhkjhsd"), + objects.Source(objects.SourcePages.MUSIFY, "https://ln.topdf.de/Music-Kraken/") + ], + album_list=[ + objects.Album( + title="One Final Action", + date=objects.ID3Timestamp(year=1986, month=3, day=1), + language=pycountry.languages.get(alpha_2="en"), + label_list=[ + objects.Label(name="an album label") + ], + source_list=[ + objects.Source(objects.SourcePages.ENCYCLOPAEDIA_METALLUM, "https://www.metal-archives.com/albums/I%27m_in_a_Coffin/One_Final_Action/207614") + ] + ), ], - album=Album( - title="One Final Action", - date=ID3Timestamp(year=1986, month=3, day=1), - language=pycountry.languages.get(alpha_2="en"), - label="cum productions", - sources=[ - Source(SourcePages.ENCYCLOPAEDIA_METALLUM, "https://www.metal-archives.com/albums/I%27m_in_a_Coffin/One_Final_Action/207614") - ] - ), main_artist_list=[ - Artist( + objects.Artist( name="I'm in a coffin", - sources=[ - Source(SourcePages.ENCYCLOPAEDIA_METALLUM, "https://www.metal-archives.com/bands/I%27m_in_a_Coffin/127727") + source_list=[ + objects.Source( + objects.SourcePages.ENCYCLOPAEDIA_METALLUM, + "https://www.metal-archives.com/bands/I%27m_in_a_Coffin/127727" + ) + ] + ), + objects.Artist(name="some_split_artist") + ], + feature_artist_list=[ + objects.Artist( + name="Ruffiction", + label_list=[ + objects.Label(name="Ruffiction Productions") ] ) ], - feature_artist_list=[Artist(name="Rick Astley")], ) + +print(song.option_string) +for album in song.album_collection: + print(album.option_string) +for artist in song.main_artist_collection: + print(artist.option_string) ``` diff --git a/src/create_custom_objects.py b/src/create_custom_objects.py index 8886dca..3a9c323 100644 --- a/src/create_custom_objects.py +++ b/src/create_custom_objects.py @@ -1,25 +1,6 @@ from music_kraken import objects import pycountry -import logging - -logging.disable() - - -def div(msg: str = ""): - print("-" * 50 + msg + "-" * 50) - - -def print_song(song_: objects.Song): - print(str(song_.metadata)) - print("----album--") - print(song_.album) - print("----src----") - print("song:") - print(song_.source_list) - print("album:") - print(song_.album.source_list) - print("\n") song = objects.Song( @@ -41,13 +22,15 @@ song = objects.Song( ], album_list=[ objects.Album( - title="One Final Action", - date=objects.ID3Timestamp(year=1986, month=3, day=1), - language=pycountry.languages.get(alpha_2="en"), - label="cum productions", - source_list=[ - objects.Source(objects.SourcePages.ENCYCLOPAEDIA_METALLUM, "https://www.metal-archives.com/albums/I%27m_in_a_Coffin/One_Final_Action/207614") - ] + title="One Final Action", + date=objects.ID3Timestamp(year=1986, month=3, day=1), + language=pycountry.languages.get(alpha_2="en"), + label_list=[ + objects.Label(name="an album label") + ], + source_list=[ + objects.Source(objects.SourcePages.ENCYCLOPAEDIA_METALLUM, "https://www.metal-archives.com/albums/I%27m_in_a_Coffin/One_Final_Action/207614") + ] ), ], main_artist_list=[ @@ -62,25 +45,18 @@ song = objects.Song( ), objects.Artist(name="some_split_artist") ], - feature_artist_list=[objects.Artist(name="Ruffiction")], + feature_artist_list=[ + objects.Artist( + name="Ruffiction", + label_list=[ + objects.Label(name="Ruffiction Productions") + ] + ) + ], ) -print(song) - -exit() - -div() -song_ref = song.reference - - -# getting song by song ref -song_list = cache.pull_songs(song_ref=song_ref) -song_from_db = song_list[0] - -print_song(song_from_db) - -# try writing metadata -write_metadata(song) - -# getting song by album ref -div() \ No newline at end of file +print(song.option_string) +for album in song.album_collection: + print(album.option_string) +for artist in song.main_artist_collection: + print(artist.option_string) diff --git a/src/music_kraken/objects/parents.py b/src/music_kraken/objects/parents.py index f7963d9..a6fd9bd 100644 --- a/src/music_kraken/objects/parents.py +++ b/src/music_kraken/objects/parents.py @@ -19,7 +19,7 @@ class DatabaseObject: https://docs.python.org/3/library/uuid.html """ _id = str(uuid.uuid4()) - LOGGER.info(f"id for {type(self).__name__} isn't set. Setting to {_id}") + LOGGER.debug(f"id for {type(self).__name__} isn't set. Setting to {_id}") # The id can only be None, if the object is dynamic (self.dynamic = True) self.id: Optional[str] = _id @@ -74,6 +74,14 @@ class DatabaseObject: def metadata(self) -> Metadata: return Metadata() + @property + def option_list(self) -> List[Type['DatabaseObject']]: + return [self] + + @property + def option_string(self) -> str: + return self.__repr__() + class MainObject(DatabaseObject): """ @@ -91,12 +99,3 @@ class MainObject(DatabaseObject): DatabaseObject.__init__(self, _id=_id, dynamic=dynamic, **kwargs) self.additional_arguments: dict = kwargs - - def get_options(self) -> list: - return [] - - def get_option_string(self) -> str: - return "" - - options = property(fget=get_options) - options_str = property(fget=get_option_string) diff --git a/src/music_kraken/objects/song.py b/src/music_kraken/objects/song.py index 60b700d..abd6d44 100644 --- a/src/music_kraken/objects/song.py +++ b/src/music_kraken/objects/song.py @@ -11,7 +11,7 @@ from ..utils.shared import ( MUSIC_DIR, DATABASE_LOGGER as LOGGER ) -from ..utils.string_processing import unify +from ..utils.string_processing import unify from .parents import ( DatabaseObject, MainObject @@ -33,6 +33,7 @@ All Objects dependent """ CountryTyping = type(list(pycountry.countries)[0]) +OPTION_STRING_DELIMITER = " | " class Song(MainObject): @@ -41,7 +42,9 @@ class Song(MainObject): tracksort, genre, source_list, target, lyrics_list, album, main_artist_list, and feature_artist_list. """ - COLLECTION_ATTRIBUTES = ("lyrics_collection", "album_collection", "main_artist_collection", "feature_artist_collection", "source_collection") + COLLECTION_ATTRIBUTES = ( + "lyrics_collection", "album_collection", "main_artist_collection", "feature_artist_collection", + "source_collection") SIMPLE_ATTRIBUTES = ("title", "unified_title", "isrc", "length", "tracksort", "genre") def __init__( @@ -73,15 +76,10 @@ class Song(MainObject): self.genre: str = genre self.source_collection: SourceCollection = SourceCollection(source_list) - self.target_collection: Collection = Collection(data=target_list, element_type=Target) - self.lyrics_collection: Collection = Collection(data=lyrics_list, element_type=Lyrics) - self.album_collection: Collection = Collection(data=album_list, element_type=Album) - self.main_artist_collection = Collection(data=main_artist_list, element_type=Artist) - self.feature_artist_collection = Collection(data=feature_artist_list, element_type=Artist) @property @@ -92,7 +90,7 @@ class Song(MainObject): ('isrc', self.isrc.strip()), *[('url', source.url) for source in self.source_collection] ] - + @property def metadata(self) -> Metadata: metadata = Metadata({ @@ -111,11 +109,6 @@ class Song(MainObject): return metadata - def __eq__(self, other): - if type(other) != type(self): - return False - return self.id == other.id - def get_artist_credits(self) -> str: main_artists = ", ".join([artist.name for artist in self.main_artist_collection]) feature_artists = ", ".join([artist.name for artist in self.feature_artist_collection]) @@ -135,6 +128,13 @@ class Song(MainObject): def __repr__(self) -> str: return f"Song(\"{self.title}\")" + @property + def option_string(self) -> str: + return f"{self.__repr__()} " \ + f"from Album({OPTION_STRING_DELIMITER.join(album.title for album in self.album_collection)}) " \ + f"by Artist({OPTION_STRING_DELIMITER.join(artist.name for artist in self.main_artist_collection)}) " \ + f"feat. Artist({OPTION_STRING_DELIMITER.join(artist.name for artist in self.feature_artist_collection)})" + @property def tracksort_str(self) -> str: """ @@ -143,21 +143,19 @@ class Song(MainObject): """ return f"{self.tracksort}/{len(self.album.tracklist) or 1}" - def get_options(self) -> list: + @property + def option_list(self) -> list: """ Return a list of related objects including the song object, album object, main artist objects, and feature artist objects. :return: a list of objects that are related to the Song object """ - options = self.main_artist_list.copy() + options = self.main_artist_collection.shallow_list options.extend(self.feature_artist_collection) options.extend(self.album_collection) options.append(self) return options - def get_option_string(self) -> str: - return f"Song({self.title}) of Album({self.album.title}) from Artists({self.get_artist_credits()})" - """ All objects dependent on Album @@ -210,14 +208,10 @@ class Album(MainObject): self.albumsort: Optional[int] = albumsort self.source_collection: SourceCollection = SourceCollection(source_list) - self.song_collection: Collection = Collection(data=song_list, element_type=Song) - self.artist_collection: Collection = Collection(data=artist_list, element_type=Artist) - self.label_collection: Collection = Collection(data=label_list, element_type=Label) - @property def indexing_values(self) -> List[Tuple[str, object]]: return [ @@ -226,7 +220,7 @@ class Album(MainObject): ('barcode', self.barcode), *[('url', source.url) for source in self.source_collection] ] - + @property def metadata(self) -> Metadata: return Metadata({ @@ -240,6 +234,12 @@ class Album(MainObject): def __repr__(self): return f"Album(\"{self.title}\")" + @property + def option_string(self) -> str: + return f"{self.__repr__()} " \ + f"by Artist({OPTION_STRING_DELIMITER.join([artist.name for artist in self.artist_collection])}) " \ + f"under Label({OPTION_STRING_DELIMITER.join([label.name for label in self.label_collection])})" + def update_tracksort(self): """ This updates the tracksort attributes, of the songs in @@ -272,8 +272,6 @@ class Album(MainObject): continue song.tracksort = i + 1 - - @property def copyright(self) -> str: if self.date is None: @@ -300,16 +298,16 @@ class Album(MainObject): """ return len(self.artist_collection) > 1 - def get_options(self) -> list: - options = self.artist_collection.copy() + @property + def option_list(self) -> list: + options = self.artist_collection.shallow_list options.append(self) options.extend(self.song_collection) return options - def get_option_string(self) -> str: - return f"Album: {self.title}; Artists {', '.join([i.name for i in self.artist_collection])}" - + + """ All objects dependent on Artist @@ -319,7 +317,7 @@ All objects dependent on Artist class Artist(MainObject): COLLECTION_ATTRIBUTES = ("feature_song_collection", "main_album_collection", "label_collection") SIMPLE_ATTRIBUTES = ("name", "name", "country", "formed_in", "notes", "lyrical_themes", "general_genre") - + def __init__( self, _id: str = None, @@ -359,13 +357,10 @@ class Artist(MainObject): """ self.lyrical_themes: List[str] = lyrical_themes or [] self.general_genre = general_genre - + self.source_collection: SourceCollection = SourceCollection(source_list) - self.feature_song_collection: Collection = Collection(data=feature_song_list, element_type=Song) - self.main_album_collection: Collection = Collection(data=main_album_list, element_type=Album) - self.label_collection: Collection = Collection(data=label_list, element_type=Label) @property @@ -375,7 +370,7 @@ class Artist(MainObject): ('name', self.unified_name), *[('url', source.url) for source in self.source_collection] ] - + @property def metadata(self) -> Metadata: metadata = Metadata({ @@ -395,6 +390,11 @@ class Artist(MainObject): def __repr__(self): return f"Artist(\"{self.name}\")" + @property + def option_string(self) -> str: + return f"{self.__repr__()} " \ + f"under Label({OPTION_STRING_DELIMITER.join([label.name for label in self.label_collection])})" + @property def country_string(self): return self.country.alpha_3 @@ -428,15 +428,13 @@ class Artist(MainObject): song_list=self.feature_song_collection.copy() ) - def get_options(self) -> list: + @property + def option_list(self) -> list: options = [self] options.extend(self.main_album_collection) options.extend(self.feature_song_collection) return options - def get_option_string(self) -> str: - return f"Artist: {self.name}" - def get_all_songs(self) -> List[Song]: """ returns a list of all Songs. @@ -464,7 +462,7 @@ Label class Label(MainObject): COLLECTION_ATTRIBUTES = ("album_collection", "current_artist_collection") SIMPLE_ATTRIBUTES = ("name",) - + def __init__( self, _id: str = None, @@ -480,7 +478,7 @@ class Label(MainObject): self.name: str = name self.unified_name: str = unified_name or unify(self.name) - + self.source_collection: SourceCollection = SourceCollection(source_list) self.album_collection: Collection = Collection(data=album_list, element_type=Album) self.current_artist_collection: Collection = Collection(data=current_artist_list, element_type=Artist)