diff --git a/src/music_kraken/database/data_models.py b/src/music_kraken/database/data_models.py index 9621600..6b029a2 100644 --- a/src/music_kraken/database/data_models.py +++ b/src/music_kraken/database/data_models.py @@ -39,39 +39,15 @@ class BaseModel(Model): self._meta.database = database return self - class ObjectModel(BaseModel): + id: str = CharField(primary_key=True) + +class MainModel(BaseModel): additional_arguments: str = CharField(null=True) notes: str = CharField(null=True) -class Album(ObjectModel): - """A class representing an album in the music database.""" - - title: str = CharField(null=True) - album_status: str = CharField(null=True) - album_type: str = CharField(null=True) - language: str = CharField(null=True) - date: str = CharField(null=True) - date_format: str = CharField(null=True) - - barcode: str = CharField(null=True) - is_split: bool = BooleanField(default=False) - - albumsort: int = IntegerField(null=True) - - -class Artist(BaseModel): - """A class representing an artist in the music database.""" - - name: str = CharField(null=True) - country: str = CharField(null=True) - formed_in_date: str = CharField(null=True) - formed_in_date_format: str = CharField(null=True) - general_genre: str = CharField(null=True) - - -class Song(BaseModel): +class Song(MainModel): """A class representing a song in the music database.""" title: str = CharField(null=True) @@ -79,9 +55,36 @@ class Song(BaseModel): length: int = IntegerField(null=True) tracksort: int = IntegerField(null=True) genre: str = CharField(null=True) + + +class Album(MainModel): + """A class representing an album in the music database.""" + + title: str = CharField(null=True) + album_status: str = CharField(null=True) + album_type: str = CharField(null=True) + language: str = CharField(null=True) + date_string: str = CharField(null=True) + date_format: str = CharField(null=True) + barcode: str = CharField(null=True) + albumsort: int = IntegerField(null=True) -class Target(BaseModel): +class Artist(MainModel): + """A class representing an artist in the music database.""" + + name: str = CharField(null=True) + country: str = CharField(null=True) + formed_in_date: str = CharField(null=True) + formed_in_format: str = CharField(null=True) + general_genre: str = CharField(null=True) + + +class Label(MainModel): + name: str = CharField(null=True) + + +class Target(ObjectModel): """A class representing a target of a song in the music database.""" file: str = CharField() @@ -89,7 +92,7 @@ class Target(BaseModel): song = ForeignKeyField(Song, backref='targets') -class Lyrics(BaseModel): +class Lyrics(ObjectModel): """A class representing lyrics of a song in the music database.""" text: str = TextField() @@ -97,33 +100,6 @@ class Lyrics(BaseModel): song = ForeignKeyField(Song, backref='lyrics') -class SongTarget(BaseModel): - song: ForeignKeyField = ForeignKeyField(Song, backref='song_target') - artist: ForeignKeyField = ForeignKeyField(Target, backref='song_target') - is_feature: bool = BooleanField(default=False) - - -class SongArtist(BaseModel): - """A class representing the relationship between a song and an artist.""" - - song: ForeignKeyField = ForeignKeyField(Song, backref='song_artists') - artist: ForeignKeyField = ForeignKeyField(Artist, backref='song_artists') - is_feature: bool = BooleanField(default=False) - - -class AlbumArtist(BaseModel): - """A class representing the relationship between an album and an artist.""" - - album: ForeignKeyField = ForeignKeyField(Album, backref='album_artists') - artist: ForeignKeyField = ForeignKeyField(Artist, backref='album_artists') - - -class AlbumSong(BaseModel): - """A class representing the relationship between an album and an song.""" - album: ForeignKeyField = ForeignKeyField(Album, backref='album_artists') - song: ForeignKeyField = ForeignKeyField(Song, backref='album_artists') - - class Source(BaseModel): """A class representing a source of a song in the music database.""" ContentTypes = Union[Song, Album, Artist, Lyrics] @@ -140,12 +116,15 @@ class Source(BaseModel): """Get the content associated with the source as an object.""" if self.content_type == 'Song': return Song.get(Song.id == self.content_id) - elif self.content_type == 'Album': + if self.content_type == 'Album': return Album.get(Album.id == self.content_id) - elif self.content_type == 'Artist': + if self.content_type == 'Artist': return Artist.get(Artist.id == self.content_id) - else: - return None + if self.content_type == 'Label': + return Label.get(Label.id == self.content_id) + if self.content_type == 'Lyrics': + return Lyrics.get(Lyrics.id == self.content_id) + @content_object.setter def content_object(self, value: Union[Song, Album, Artist]) -> None: @@ -154,13 +133,44 @@ class Source(BaseModel): self.content_id = value.id +class SongArtist(BaseModel): + """A class representing the relationship between a song and an artist.""" + + song: ForeignKeyField = ForeignKeyField(Song, backref='song_artists') + artist: ForeignKeyField = ForeignKeyField(Artist, backref='song_artists') + is_feature: bool = BooleanField(default=False) + + +class ArtistAlbum(BaseModel): + """A class representing the relationship between an album and an artist.""" + + album: ForeignKeyField = ForeignKeyField(Album, backref='album_artists') + artist: ForeignKeyField = ForeignKeyField(Artist, backref='album_artists') + + +class AlbumSong(BaseModel): + """A class representing the relationship between an album and an song.""" + album: ForeignKeyField = ForeignKeyField(Album, backref='album_artists') + song: ForeignKeyField = ForeignKeyField(Song, backref='album_artists') + + +class LabelAlbum(BaseModel): + label: ForeignKeyField = ForeignKeyField(Label, backref='label_album') + album: ForeignKeyField = ForeignKeyField(Album, backref='label_album') + + +class LabelArtist(BaseModel): + label: ForeignKeyField = ForeignKeyField(Label, backref='label_artist') + artist: ForeignKeyField = ForeignKeyField(Artist, backref='label_artists') + + ALL_MODELS = [ Song, Album, Artist, Source, Lyrics, - AlbumArtist, + ArtistAlbum, Target, SongArtist ] diff --git a/src/music_kraken/database/write.py b/src/music_kraken/database/write.py index 2b98373..73e78d8 100644 --- a/src/music_kraken/database/write.py +++ b/src/music_kraken/database/write.py @@ -1,5 +1,6 @@ from typing import Union, Optional, Dict, DefaultDict, Type, List from collections import defaultdict +import json import traceback from peewee import ( SqliteDatabase, @@ -42,6 +43,7 @@ class WritingSession: self.added_song_ids: Dict[str] = dict() self.added_album_ids: Dict[str] = dict() self.added_artist_ids: Dict[str] = dict() + self.added_label_ids: Dict[str] = dict() self.db_objects: DefaultDict[data_models.BaseModel, List[data_models.BaseModel]] = defaultdict(list) @@ -130,7 +132,7 @@ class WritingSession: db_song: data_models.Song = data_models.Song( id=song.id, - name=song.title, + title=song.title, isrc=song.isrc, length=song.length, tracksort=song.tracksort, @@ -187,17 +189,15 @@ class WritingSession: return self.added_album_ids[album.id] db_album = data_models.Album( + id = album.id, title = album.title, - label = album.label, - album_status = album.album_status, - album_type = album.album_type, + album_status = album.album_status.value, + album_type = album.album_type.value, language = album.iso_639_2_language, - date = album.date.timestamp, + date_string = album.date.timestamp, date_format = album.date.timeformat, - country = album.country, barcode = album.barcode, - albumsort = album.albumsort, - is_split = album.is_split + albumsort = album.albumsort ).use(self.database) self.db_objects[data_models.Album].append(db_album) @@ -216,13 +216,21 @@ class WritingSession: self.db_objects[data_models.AlbumSong].append(db_song_album) for artist in album.artist_collection: - db_album_artist = data_models.AlbumArtist( + db_album_artist = data_models.ArtistAlbum( album = album, artist = self.add_artist(artist) ) self.db_objects[data_models.Artist].append(db_album_artist) + for label in album.label_collection: + self.db_objects[data_models.LabelAlbum].append( + data_models.LabelAlbum( + label = self.add_label(label=label), + album = db_album + ) + ) + return db_album def add_artist(self, artist: objects.Artist) -> Optional[data_models.Artist]: @@ -240,7 +248,10 @@ class WritingSession: db_artist = data_models.Artist( id = artist.id, name = artist.name, - notes = artist.notes.json + country = artist.country_string, + formed_in_date = artist.formed_in.timestamp, + formed_in_format = artist.formed_in.timestamp, + general_genre = artist.general_genre ) self.db_objects[data_models.Artist].append(db_artist) @@ -250,12 +261,12 @@ class WritingSession: self.add_source(source, db_artist) for album in artist.main_albums: - db_album_artist = data_models.AlbumArtist( + db_album_artist = data_models.ArtistAlbum( artist = artist, album = self.add_album(album) ) - self.db_objects[data_models.AlbumArtist].append(db_album_artist) + self.db_objects[data_models.ArtistAlbum].append(db_album_artist) for song in artist.feature_songs: db_artist_song = data_models.SongArtist( @@ -266,8 +277,47 @@ class WritingSession: self.db_objects[data_models.SongArtist].append(db_artist_song) + for label in artist.label_collection: + self.db_objects[data_models.LabelArtist].append( + data_models.LabelArtist( + artist = db_artist, + label = self.add_label(label=label) + ) + ) + return db_artist + def add_label(self, label: objects.Label) -> Optional[data_models.Label]: + if label.dynamic: + return + if label.id in self.added_label_ids: + return self.added_label_ids[label.id] + + db_label = data_models.Label( + id = label.id, + name = label.name, + additional_arguments = json.dumps(label.additional_arguments) + ) + + self.db_objects[data_models.Label] + self.add_label[label.id] = db_label + + for album in label.album_collection: + self.db_objects[data_models.LabelAlbum].append( + data_models.LabelAlbum( + album = self.add_album(album=album), + label = db_label + ) + ) + + for artist in label.current_artist_collection: + self.db_objects[data_models.LabelArtist].append( + artist = self.add_artist(artist=artist), + label = db_label + ) + + return db_label + def commit(self, reset: bool = True): """ Commit changes to the database diff --git a/src/music_kraken/objects/__init__.py b/src/music_kraken/objects/__init__.py index ae540b7..2f923e6 100644 --- a/src/music_kraken/objects/__init__.py +++ b/src/music_kraken/objects/__init__.py @@ -21,6 +21,7 @@ Artist = song.Artist Source = source.Source Target = song.Target Lyrics = song.Lyrics +Label = song.Label AlbumType = album.AlbumType AlbumStatus = album.AlbumStatus diff --git a/src/music_kraken/objects/song.py b/src/music_kraken/objects/song.py index 6ee8f55..9a98f70 100644 --- a/src/music_kraken/objects/song.py +++ b/src/music_kraken/objects/song.py @@ -206,7 +206,6 @@ class Album(MainObject, SourceAttribute, MetadataAttribute): maybe look at how mutagen does it with easy_id3 """ self.barcode: str = barcode - self.is_split: bool = is_split """ TODO implement a function in the Artist class, @@ -277,6 +276,10 @@ class Album(MainObject, SourceAttribute, MetadataAttribute): id3Mapping.DATE: [self.date.timestamp] }) + @property + def is_split(self) -> bool: + return len(self.artist_collection) > 1 + def get_copyright(self) -> str: if self.date is None: return "" @@ -285,7 +288,8 @@ class Album(MainObject, SourceAttribute, MetadataAttribute): return f"{self.date.year} {self.label_collection[0].name}" - def get_iso_639_2_lang(self) -> Optional[str]: + @property + def iso_639_2_lang(self) -> Optional[str]: if self.language is None: return None @@ -307,8 +311,6 @@ class Album(MainObject, SourceAttribute, MetadataAttribute): tracklist: List[Song] = property(fget=lambda self: self.song_collection.copy()) copyright = property(fget=get_copyright) - iso_639_2_language = property(fget=get_iso_639_2_lang) - """ All objects dependent on Artist @@ -384,6 +386,10 @@ class Artist(MainObject, SourceAttribute, MetadataAttribute): def __repr__(self): return f"Artist(\"{self.name}\")" + @property + def country_string(self): + return self.country.alpha_3 + def update_albumsort(self): """ This updates the albumsort attributes, of the albums in