diff --git a/src/goof.py b/src/goof.py index 1424b5f..91bb68d 100644 --- a/src/goof.py +++ b/src/goof.py @@ -9,6 +9,9 @@ from music_kraken import ( import music_kraken.database.new_database as db +def div(): + print("-"*100) + cache = music_kraken.database.new_database.Database("test.db") cache.reset() @@ -21,6 +24,7 @@ song_input = Song( title="Vein Deep in the Solution", album_name=album_input.title, length=666, + tracksort=2, target=Target(file="~/Music/genre/artist/album/song.mp3", path="~/Music/genre/artist/album"), metadata={ "album": "One Final Action" @@ -49,18 +53,28 @@ lyrics = Lyrics(text="these are some Lyrics that don't belong to any Song", lang cache.push([album_input, song_input, lyrics, additional_song]) # getting song by song ref +div() song_output_list = cache.pull_songs(song_ref=song_ref) -print(len(song_output_list), song_output_list, song_output_list[0].album) -# song_output = song_output_list[0] -# print(song_output) -# print("album id", song_output.album_ref) +print(len(song_output_list), song_output_list, song_output_list[0].album, sep=" | ") +print("tracksort", song_output_list[0].tracksort, sep=": ") # getting song by album ref +div() song_output_list = cache.pull_songs(album_ref=album_input.reference) print(len(song_output_list), song_output_list) +for song in song_output_list: + print(song, song.album) # getting album +div() album_output_list = cache.pull_albums(album_ref=album_input.reference) album_output = album_output_list[0] print(album_output) -print(album_output.tracklist) +for track in album_output.tracklist: + print(track.tracksort, track) + +# getting album by song +div() +album_output_list = cache.pull_albums(song_ref=song_ref) +print(album_output_list) +print("len of album ->", len(album_output_list[0]), album_output_list[0], sep=" | ") diff --git a/src/music_kraken/database/new_database.py b/src/music_kraken/database/new_database.py index 6eba370..363d9f1 100644 --- a/src/music_kraken/database/new_database.py +++ b/src/music_kraken/database/new_database.py @@ -22,7 +22,7 @@ logger = logging.getLogger("database") # use complicated query builder SONG_QUERY = """ SELECT -Song.id AS song_id, Song.name AS title, Song.isrc AS isrc, Song.length AS length, Song.album_id, +Song.id AS song_id, Song.name AS title, Song.isrc AS isrc, Song.length AS length, Song.album_id, Song.tracksort, Target.id AS target_id, Target.file AS file, Target.path AS path FROM Song LEFT JOIN Target ON Song.id=Target.song_id @@ -38,11 +38,17 @@ SELECT id, text, language, song_id FROM Lyrics WHERE {where}; """ -ALBUM_QUERY = """ +ALBUM_QUERY_UNJOINED = """ SELECT Album.id AS album_id, title, copyright, album_status, language, year, date, country, barcode 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 +FROM Song +INNER JOIN Album a ON Song.album_id=a.id +WHERE {where}; +""" class Database: @@ -147,9 +153,10 @@ class Database: song.title, song.isrc, song.length, - song.get_album_id() + song.get_album_id(), + song.tracksort ) - query = f"INSERT OR REPLACE INTO {table} (id, name, isrc, length, album_id) VALUES (?, ?, ?, ?, ?);" + query = f"INSERT OR REPLACE INTO {table} (id, name, isrc, length, album_id, tracksort) VALUES (?, ?, ?, ?, ?, ?);" self.cursor.execute(query, values) self.connection.commit() @@ -268,17 +275,20 @@ class Database: url=source_row['url'] ) for source_row in source_rows] - def get_song_from_row(self, song_result, exclude_relations: set = set()) -> Song: + def get_song_from_row(self, song_result, exclude_relations: set = None) -> Song: + if exclude_relations is None: + exclude_relations = set() new_exclude_relations: set = set(exclude_relations) new_exclude_relations.add(Song) song_id = song_result['song_id'] - + song_obj = Song( id_=song_id, title=song_result['title'], isrc=song_result['isrc'], length=song_result['length'], + tracksort=song_result['tracksort'], target=Target( id_=song_result['target_id'], file=song_result['file'], @@ -290,21 +300,24 @@ class Database: ) if Album not in exclude_relations and song_result['album_id'] is not None: - album_obj = self.pull_albums(album_ref=Reference(song_result['album_id']), exclude_relations=new_exclude_relations) + album_obj = self.pull_albums(album_ref=Reference(song_result['album_id']), + exclude_relations=new_exclude_relations) if len(album_obj) > 0: song_obj.album = album_obj[0] return song_obj - def pull_songs(self, song_ref: Reference = None, album_ref: Reference = None, exclude_relations: set = set()) -> List[Song]: + def pull_songs(self, song_ref: Reference = None, album_ref: Reference = None, exclude_relations: set = set()) -> \ + List[Song]: """ This function is used to get one song (including its children like Sources etc) from one song id (a reference object) + :param exclude_relations: + By default all relations are pulled by this funktion. If the class object of for + example the Artists is in the set it won't get fetched. + This is done to prevent an infinite recursion. :param song_ref: :param album_ref: - :param exclude_independent_relations: - This excludes all relations from being fetched like for example the Album of the Song. - This is necessary when adding the Song as subclass as e.g. an Album (as tracklist or whatever). :return requested_song: """ @@ -313,7 +326,7 @@ class Database: where = f"Song.id=\"{song_ref.id}\"" elif album_ref is not None: where = f"Song.album_id=\"{album_ref.id}\"" - + query = SONG_QUERY.format(where=where) self.cursor.execute(query) @@ -324,11 +337,12 @@ class Database: exclude_relations=exclude_relations ) for song_result in song_rows] - def get_album_from_row(self, album_result, exclude_relations: set = set()) -> Album: + def get_album_from_row(self, album_result, exclude_relations=None) -> Album: + if exclude_relations is None: + exclude_relations = set() new_exclude_relations: set = exclude_relations.copy() - new_exclude_relations.add(Album) - + album_id = album_result['album_id'] album_obj = Album( @@ -343,10 +357,7 @@ class Database: barcode=album_result['barcode'] ) - print(exclude_relations) - if Song not in exclude_relations: - print("yay") # getting the tracklist tracklist: List[Song] = self.pull_songs( album_ref=Reference(id_=album_id), @@ -356,18 +367,30 @@ class Database: return album_obj - def pull_albums(self, album_ref: Reference = None, exclude_relations: set = set()) -> List[Album]: + def pull_albums(self, album_ref: Reference = None, song_ref: Reference = None, exclude_relations: set = None) -> List[Album]: """ This function is used to get matching albums/releses from one song id (a reference object) + :param exclude_relations: + By default all relations are pulled by this funktion. If the class object of for + example the Artists is in the set it won't get fetched. + This is done to prevent an infinite recursion. :param album_ref: :return requested_album_list: """ + if exclude_relations is None: + exclude_relations = set() + + query = ALBUM_QUERY_UNJOINED where = "1=1" if album_ref is not None: + query = ALBUM_QUERY_UNJOINED where = f"Album.id=\"{album_ref.id}\"" + elif song_ref is not None: + query = ALBUM_QUERY_JOINED + where = f"Song.id=\"{song_ref.id}\"" - query = ALBUM_QUERY.format(where=where) + query = query.format(where=where) self.cursor.execute(query) album_rows = self.cursor.fetchall() diff --git a/src/music_kraken/database/objects/song.py b/src/music_kraken/database/objects/song.py index c3c6c45..8885bf7 100644 --- a/src/music_kraken/database/objects/song.py +++ b/src/music_kraken/database/objects/song.py @@ -145,6 +145,7 @@ class Song(DatabaseObject): artist_names: List[str] = [], isrc: str = None, length: int = None, + tracksort: int = None, sources: List[Source] = None, target: Target = None, lyrics: List[Lyrics] = None, @@ -167,6 +168,7 @@ class Song(DatabaseObject): self.isrc: str | None = isrc self.length_: int | None = length self.artist_names = artist_names + self.tracksort: int | None = tracksort self.metadata = Metadata(data=metadata) @@ -190,7 +192,7 @@ class Song(DatabaseObject): self.album_ref = album_ref self.artist_refs = artist_refs - self.album: Album = None + self._album: Album | None = None def __eq__(self, other): if type(other) != type(self): @@ -227,11 +229,20 @@ class Song(DatabaseObject): return None return self.album_ref.id - length = property(fget=get_length, fset=set_length) + def set_album(self, album): + if self.album_ref.id is not None: + if self.album_ref.id != album.id: + logger.warning(f"song already refers to different album, overriding reference.") + + self.album_ref = Reference(album.id) + self._album = album + + album = property(fget=lambda self: self._album, fset=set_album) + length: int = property(fget=get_length, fset=set_length) """ -All objects dependend on Album +All objects dependent on Album """ @@ -273,11 +284,22 @@ class Album(DatabaseObject): self.tracklist: List[Song] = [] + def __str__(self) -> str: + return f"Album: \"{self.title}\"" + + def __len__(self) -> int: + return len(self.tracklist) + def set_tracklist(self, tracklist: List[Song]): self.tracklist = tracklist + for i, track in enumerate(self.tracklist): + track.tracksort = i+1 + def add_song(self, song: Song): for existing_song in self.tracklist: if existing_song == song: return + + song.tracksort = len(self.tracklist) self.tracklist.append(song) diff --git a/src/music_kraken/static_files/new_db.sql b/src/music_kraken/static_files/new_db.sql index 89b427b..981a73c 100644 --- a/src/music_kraken/static_files/new_db.sql +++ b/src/music_kraken/static_files/new_db.sql @@ -4,6 +4,7 @@ CREATE TABLE Song name TEXT, isrc TEXT, length INT, -- length is in milliseconds (could be wrong) + tracksort INT, album_id BIGINT, FOREIGN KEY(album_id) REFERENCES Album(id) ); diff --git a/src/test.db b/src/test.db index 5c10724..b6da822 100644 Binary files a/src/test.db and b/src/test.db differ