diff --git a/src/goof.py b/src/goof.py index 91bb68d..ec9a685 100644 --- a/src/goof.py +++ b/src/goof.py @@ -4,7 +4,8 @@ from music_kraken import ( Lyrics, Target, Source, - Album + Album, + Artist ) import music_kraken.database.new_database as db @@ -16,6 +17,18 @@ def div(): cache = music_kraken.database.new_database.Database("test.db") cache.reset() +main_artist = Artist( + name="I'm in a coffin" +) + +split_artist = Artist( + name="split" +) + +feature_artist = Artist( + name="feature" +) + album_input = Album( title="One Final Action" ) diff --git a/src/music_kraken/database/__init__.py b/src/music_kraken/database/__init__.py index 5e9f4d2..8dd8fd4 100644 --- a/src/music_kraken/database/__init__.py +++ b/src/music_kraken/database/__init__.py @@ -4,7 +4,6 @@ from . import ( ) Song = objects.Song -Artist = objects.Artist Source = objects.Source Target = objects.Target Metadata = objects.Metadata @@ -12,4 +11,5 @@ Lyrics = objects.Lyrics Album = objects.Album +Artist = objects.Artist # cache = temp_database.TempDatabase() diff --git a/src/music_kraken/database/new_database.py b/src/music_kraken/database/new_database.py index 363d9f1..1d14894 100644 --- a/src/music_kraken/database/new_database.py +++ b/src/music_kraken/database/new_database.py @@ -39,12 +39,12 @@ FROM Lyrics WHERE {where}; """ ALBUM_QUERY_UNJOINED = """ -SELECT Album.id AS album_id, title, copyright, album_status, language, year, date, country, barcode +SELECT Album.id AS album_id, title, copyright, album_status, language, year, date, country, barcode, albumsort, is_split FROM Album WHERE {where}; """ ALBUM_QUERY_JOINED = """ -SELECT a.id AS album_id, a.title, a.copyright, a.album_status, a.language, a.year, a.date, a.country, a.barcode +SELECT a.id AS album_id, a.title, a.copyright, a.album_status, a.language, a.year, a.date, a.country, a.barcode, a.albumsort, a.is_split FROM Song INNER JOIN Album a ON Song.album_id=a.id WHERE {where}; @@ -87,6 +87,9 @@ class Database: return self.connection, self.cursor def push_one(self, db_object: Song | Lyrics | Target | Artist | Source | Album): + if db_object.dynamic: + return + if type(db_object) == Song: return self.push_song(song=db_object) @@ -122,7 +125,7 @@ class Database: def push_album(self, album: Album): table = "Album" - query = f"INSERT OR REPLACE INTO {table} (id, title, copyright, album_status, language, year, date, country, barcode) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);" + query = f"INSERT OR REPLACE INTO {table} (id, title, copyright, album_status, language, year, date, country, barcode, albumsort, is_split) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);" values = ( album.id, @@ -133,7 +136,9 @@ class Database: album.year, album.date, album.country, - album.barcode + album.barcode, + album.albumsort, + album.is_split ) self.cursor.execute(query, values) self.connection.commit() @@ -220,8 +225,36 @@ class Database: self.cursor.execute(query, values) self.connection.commit() + def push_artist_song(self, artist_ref: Reference, song_ref: Reference, is_feature: bool): + table = "SongArtist" + # checking if already exists + query = f"SELECT * FROM {table} WHERE song_id=\"{song_ref.id}\" AND artist_id=\"{artist_ref.id}\"" + self.cursor.execute(query) + if len(self.cursor.fetchall()) > 0: + # join already exists + return + + query = f"INSERT OR REPLACE INTO {table} (song_id, artist_id, is_feature) VALUES (?, ?, ?);" + values = ( + song_ref.id, + artist_ref.id, + is_feature + ) + + self.cursor.execute(query, values) + self.connection.commit() + def push_artist(self, artist: Artist): - pass + table = "Artist" + query = f"INSERT OR REPLACE INTO {table} (id, name) VALUES (?, ?);" + values = ( + artist.id, + artist.name + ) + + self.cursor.execute(query, values) + self.connection.commit() + def pull_lyrics(self, song_ref: Reference = None, lyrics_ref: Reference = None) -> List[Lyrics]: """ @@ -354,7 +387,9 @@ class Database: year=album_result['year'], date=album_result['date'], country=album_result['country'], - barcode=album_result['barcode'] + barcode=album_result['barcode'], + is_split=album_result['is_split'], + albumsort=album_result['albumsort'] ) if Song not in exclude_relations: diff --git a/src/music_kraken/database/objects/__init__.py b/src/music_kraken/database/objects/__init__.py index 2c1db2a..5f46f87 100644 --- a/src/music_kraken/database/objects/__init__.py +++ b/src/music_kraken/database/objects/__init__.py @@ -3,9 +3,7 @@ from . import ( ) Song = song.Song - -Song = song.Song -Artist = None +Artist = song.Artist Source = song.Source Target = song.Target Metadata = song.Metadata diff --git a/src/music_kraken/database/objects/database_object.py b/src/music_kraken/database/objects/database_object.py index 694ab91..506d148 100644 --- a/src/music_kraken/database/objects/database_object.py +++ b/src/music_kraken/database/objects/database_object.py @@ -19,15 +19,21 @@ class Reference: class DatabaseObject: - def __init__(self, id_: str = None) -> None: + def __init__(self, id_: str = None, dynamic: bool = False) -> None: self.id_: str | None = id_ + self.dynamic = dynamic def get_id(self) -> str: """ returns the id if it is set, else it returns a randomly generated UUID https://docs.python.org/3/library/uuid.html + + if the object is dynamic, it raises an error """ + if self.dynamic: + raise ValueError("Dynamic objects have no idea, because they are not in the database") + if self.id_ is None: self.id_ = str(uuid.uuid4()) logger.info(f"id for {self.__str__()} isn't set. Setting to {self.id_}") diff --git a/src/music_kraken/database/objects/song.py b/src/music_kraken/database/objects/song.py index 8885bf7..5f2b406 100644 --- a/src/music_kraken/database/objects/song.py +++ b/src/music_kraken/database/objects/song.py @@ -1,5 +1,5 @@ import os -from typing import List +from typing import List, Tuple from mutagen.easyid3 import EasyID3 from ...utils.shared import ( @@ -258,6 +258,7 @@ class Album(DatabaseObject): country TEXT, barcode TEXT, song_id BIGINT, + is_split BOOLEAN NOT NULL DEFAULT 0 """ def __init__( @@ -271,8 +272,11 @@ class Album(DatabaseObject): date: str = None, country: str = None, barcode: str = None, + is_split: bool = False, + albumsort: int = None, + dynamic: bool = False ) -> None: - DatabaseObject.__init__(self, id_=id_) + DatabaseObject.__init__(self, id_=id_, dynamic=dynamic) self.title: str = title self.copyright: str = copyright_ self.album_status: str = album_status @@ -281,6 +285,8 @@ class Album(DatabaseObject): self.date: str = date self.country: str = country self.barcode: str = barcode + self.is_split: bool = is_split + self.albumsort: int | None = albumsort self.tracklist: List[Song] = [] @@ -294,7 +300,7 @@ class Album(DatabaseObject): self.tracklist = tracklist for i, track in enumerate(self.tracklist): - track.tracksort = i+1 + track.tracksort = i + 1 def add_song(self, song: Song): for existing_song in self.tracklist: @@ -303,3 +309,90 @@ class Album(DatabaseObject): song.tracksort = len(self.tracklist) self.tracklist.append(song) + + +""" +All objects dependent on Artist +""" + + +class ArtistSong(Song): + """ + A subclass of Song with the additional attribute is_feature, which + makes only sense when in/from the Artist class + """ + is_feature: bool = False + + +class Artist(DatabaseObject): + def __init__( + self, + id_: str = None, + name: str = None, + discography: List[Album] = [], + features: List[Song] = [] + ): + DatabaseObject.__init__(self, id_=id_) + + self.name: str | None = name + + self.songs: List[ArtistSong] = [] + self.album_refs: List[Album] = [] + self.song + + self.set_discography(discography) + self.set_features(features) + + def __str__(self): + return self.name or "" + + def add_album(self, album: Album): + self.album_refs.append(album) + + for song in album.tracklist: + song.__class__ = ArtistSong + song.is_feature = False + + self.songs.append(song) + + def set_discography(self, discography: List[Album]): + """ + :param discography: + :return: + """ + for album in discography: + self.add_album(album) + + def get_discography(self) -> List[Album]: + flat_copy_discography = self.discography.copy() + feature_release = Album( + title="features", + copyright_=self.name, + album_status="dynamically generated", + is_split=True, + albumsort=666, + dynamic=True + ) + for song in self.songs: + if song.is_feature: + feature_release.add_song(song) + + flat_copy_discography.append(feature_release) + return flat_copy_discography + + def set_features(self, feature_tracks: List[Song]): + for song in feature_tracks: + song.__class__ = ArtistSong + song.is_feature = True + + self.songs.append(song) + + def get_features(self) -> List[ArtistSong]: + feature_releases = [] + for song in self.songs: + if song.is_feature: + feature_releases.append(song) + return feature_releases + + discography = property(fget=get_discography, fset=set_discography) + features = property(fget=get_features, fset=set_features) diff --git a/src/music_kraken/static_files/new_db.sql b/src/music_kraken/static_files/new_db.sql index 981a73c..b107bbf 100644 --- a/src/music_kraken/static_files/new_db.sql +++ b/src/music_kraken/static_files/new_db.sql @@ -21,12 +21,6 @@ CREATE TABLE Source FOREIGN KEY(song_id) REFERENCES Song(id) ); -CREATE TABLE Artist -( - id INTEGER AUTO_INCREMENT PRIMARY KEY, - name TEXT -); - CREATE TABLE Album ( @@ -38,7 +32,9 @@ CREATE TABLE Album year TEXT, date TEXT, country TEXT, - barcode TEXT + barcode TEXT, + album_sort INT, + is_split BOOLEAN NOT NULL DEFAULT 0 ); CREATE TABLE Target @@ -59,18 +55,25 @@ CREATE TABLE Lyrics FOREIGN KEY(song_id) REFERENCES Song(id) ); +CREATE TABLE Artist +( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + name TEXT +); + CREATE TABLE SongArtist ( - song_id BIGINT, - artist_id INTEGER, + song_id BIGINT NOT NULL, + artist_id BIGINT NOT NULL, + is_feature BOOLEAN NOT NULL DEFAULT 0, FOREIGN KEY(song_id) REFERENCES Song(id), FOREIGN KEY(artist_id) REFERENCES Artist(id) ); CREATE TABLE AlbumArtist ( - album_id BIGINT, - artist_id INTEGER, + album_id BIGINT, + artist_id BIGINT, FOREIGN KEY(album_id) REFERENCES Album(id), FOREIGN KEY(artist_id) REFERENCES Artist(id) );