diff --git a/src/music_kraken/database/objects/parents.py b/src/music_kraken/database/objects/parents.py index da4903f..b20f620 100644 --- a/src/music_kraken/database/objects/parents.py +++ b/src/music_kraken/database/objects/parents.py @@ -19,9 +19,16 @@ class Reference: class DatabaseObject: - def __init__(self, id_: str = None, dynamic: bool = False) -> None: + empty: bool + + def __init__(self, id_: str = None, dynamic: bool = False, empty: bool = False, **kwargs) -> None: + """ + empty means it is an placeholder. + it makes the object perform the same, it is just the same + """ self.id_: str | None = id_ self.dynamic = dynamic + self.empty = empty def get_id(self) -> str: """ @@ -29,8 +36,11 @@ class DatabaseObject: it returns a randomly generated UUID https://docs.python.org/3/library/uuid.html + if the object is empty, it returns None if the object is dynamic, it raises an error """ + if self.empty: + return None if self.dynamic: raise ValueError("Dynamic objects have no idea, because they are not in the database") @@ -63,6 +73,8 @@ class DatabaseObject: id = property(fget=get_id) reference = property(fget=get_reference) + options = property(fget=get_options) + options_str = property(fget=get_option_string) class SongAttribute: diff --git a/src/music_kraken/database/objects/song.py b/src/music_kraken/database/objects/song.py index f6d00d9..379d2ac 100644 --- a/src/music_kraken/database/objects/song.py +++ b/src/music_kraken/database/objects/song.py @@ -114,10 +114,11 @@ class Song(DatabaseObject, SourceAttribute, MetadataAttribute): genre: str = None, source_list: List[Source] = None, target: Target = None, - lyrics: List[Lyrics] = None, + lyrics_list: List[Lyrics] = None, album=None, main_artist_list: list = None, - feature_artist_list: list = None + feature_artist_list: list = None, + **kwargs ) -> None: """ id: is not NECESARRILY the musicbrainz id, but is DISTINCT for every song @@ -125,39 +126,36 @@ class Song(DatabaseObject, SourceAttribute, MetadataAttribute): target: Each Song can have exactly one target which can be either full or empty lyrics: There can be multiple lyrics. Each Lyrics object can me added to multiple lyrics """ - super().__init__(id_=id_) + super().__init__(id_=id_, **kwargs) # attributes # *private* attributes self.title: str = title self.isrc: str = isrc self.length: int = length self.mb_id: str | None = mb_id - self.album_name: str | None = album_name - self.tracksort: int | None = tracksort + self.tracksort: int = tracksort or 0 self.genre: str = genre - if source_list: - self.source_list = source_list + self.source_list = source_list or [] - self.target = Target() - if target is not None: - self.target = target - self.target.add_song(self) + self.target = target or Target() + self.lyrics_list = lyrics_list or [] - self.lyrics = [] - if lyrics is not None: - self.lyrics = lyrics - - self._album = None + # initialize with either a passed in album, or an empty one, + # so it can at least properly generate dynamic attributes + self._album = album or Album(empty=True) self.album = album - self.main_artist_list = [] - if main_artist_list is not None: - self.main_artist_list = main_artist_list - - self.feature_artist_list = [] - if feature_artist_list is not None: - self.feature_artist_list = feature_artist_list + self.main_artist_collection = Collection( + data=main_artist_list or [], + map_attributes=["title"], + element_type=Artist + ) + self.feature_artist_collection = Collection( + data=feature_artist_list or [], + map_attributes=["title"], + element_type=Artist + ) def __eq__(self, other): if type(other) != type(self): @@ -165,10 +163,12 @@ class Song(DatabaseObject, SourceAttribute, MetadataAttribute): return self.id == other.id def get_artist_credits(self) -> str: - feature_str = "" - if len(self.feature_artist_list) > 0: - feature_str = " feat. " + ", ".join([artist.name for artist in self.feature_artist_list]) - return ", ".join([artist.name for artist in self.main_artist_list]) + feature_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]) + + if len(feature_artists) == 0: + return main_artists + return f"{main_artists} feat. {feature_artists}" def __str__(self) -> str: artist_credit_str = "" @@ -179,24 +179,14 @@ class Song(DatabaseObject, SourceAttribute, MetadataAttribute): return f"\"{self.title}\"{artist_credit_str}" def __repr__(self) -> str: - return self.__str__() - - def has_isrc(self) -> bool: - return self.isrc is not None - - def get_album_id(self): - if self.album is None: - return None - return self.album.id + return f"Song(\"{self.title}\")" def get_tracksort_str(self): - if self.tracksort is None: - return None - - if self.album is None: - return str(self.tracksort) - - return f"{self.tracksort}/{len(self.album.tracklist)}" + """ + if the album tracklist is empty, it sets it length to 1, this song has to be in the Album + :returns id3_tracksort: {song_position}/{album.length_of_tracklist} + """ + return f"{self.tracksort}/{len(self.album.tracklist) or 1}" def get_metadata(self) -> MetadataAttribute.Metadata: metadata = MetadataAttribute.Metadata({ @@ -208,36 +198,27 @@ class Song(DatabaseObject, SourceAttribute, MetadataAttribute): }) metadata.merge_many([s.get_song_metadata() for s in self.source_list]) - if self.album is not None: + if not self.album.empty: metadata.merge(self.album.metadata) metadata.merge_many([a.metadata for a in self.main_artist_list]) metadata.merge_many([a.metadata for a in self.feature_artist_list]) metadata.merge_many([l.metadata for l in self.lyrics]) return metadata - def set_album(self, album): - if album is None: - return - - self._album = album - if self not in self._album.tracklist: - flat_copy = copy.copy(self) - flat_copy.dynamic = True - self._album.tracklist.append(flat_copy) - def get_options(self) -> list: options = self.main_artist_list.copy() options.extend(self.feature_artist_list.copy()) - if self.album is not None: + if not self.album.empty: options.append(self.album) options.append(self) return options def get_option_string(self) -> str: - return f"Song: {self.title}; Album: {self.album.title}; Artists: {self.get_artist_credits()}" + return f"Song({self.title}) of Album({self.album.title}) from Artists({self.get_artist_credits()})" tracksort_str = property(fget=get_tracksort_str) - album = property(fget=lambda self: self._album, fset=set_album) + main_artist_list: list = property(fget=lambda self: self.main_artist_collection.copy()) + feature_artist_list: list = property(fget=lambda self: self.feature_artist_collection.copy()) """ @@ -246,20 +227,6 @@ All objects dependent on Album class Album(DatabaseObject, SourceAttribute, MetadataAttribute): - """ - -------DB-FIELDS------- - title TEXT, - copyright TEXT, - album_status TEXT, - language TEXT, - year TEXT, - date TEXT, - country TEXT, - barcode TEXT, - song_id BIGINT, - is_split BOOLEAN NOT NULL DEFAULT 0 - """ - def __init__( self, id_: str = None, @@ -274,11 +241,12 @@ class Album(DatabaseObject, SourceAttribute, MetadataAttribute): albumsort: int = None, dynamic: bool = False, source_list: List[Source] = None, - artists: list = None, + artist_list: list = None, tracklist: List[Song] = None, - album_type: str = None + album_type: str = None, + **kwargs ) -> None: - DatabaseObject.__init__(self, id_=id_, dynamic=dynamic) + DatabaseObject.__init__(self, id_=id_, dynamic=dynamic, **kwargs) """ TODO @@ -290,9 +258,7 @@ class Album(DatabaseObject, SourceAttribute, MetadataAttribute): self.album_status: str = album_status self.label = label self.language: pycountry.Languages = language - self.date: ID3Timestamp = date - if date is None: - self.date = ID3Timestamp() + self.date: ID3Timestamp = date or ID3Timestamp() self.country: str = country """ TODO @@ -301,6 +267,11 @@ class Album(DatabaseObject, SourceAttribute, MetadataAttribute): """ self.barcode: str = barcode self.is_split: bool = is_split + """ + TODO + implement a function in the Artist class, + to set albumsort with help of the release year + """ self.albumsort: int | None = albumsort @@ -310,7 +281,11 @@ class Album(DatabaseObject, SourceAttribute, MetadataAttribute): element_type=Song ) self.source_list = source_list or [] - self.artists = artists or [] + self.artists = Collection( + data=artist_list or [], + map_attributes=["name"], + element_type=Artist + ) def __str__(self) -> str: