diff --git a/Flow.py b/Flow.py index 0031017..9cd7a04 100644 --- a/Flow.py +++ b/Flow.py @@ -23,6 +23,7 @@ from src.StatGenerator import log_stats class Flow: spotify_library : Library = None local_library : Library = None + itunes_library : Library = None @staticmethod def fetch_spotify(self): @@ -107,6 +108,7 @@ class Flow: def load_libraries(self): self.spotify_library = LibraryLoader().load(work_dir / "spotify_library.json") + self.itunes_library = LibraryLoader().load(work_dir / "itunes_library.json") self.local_library = LibraryLoader().load(work_dir / "local_library.json") def save_libraries(self): diff --git a/Gui.py b/Gui.py index 85fd6e3..3c0ecc8 100644 --- a/Gui.py +++ b/Gui.py @@ -6,34 +6,36 @@ import pandas as pd import Flow -from src.Library import Library, Artist, Album, Track, Playlist +from src.Library import * + +def entity_to_row(entity: Entity): + return { + "id": entity.id, + "mbid": entity.mbid, + "Title": entity.title, + "PLay Count": entity.play_count, + "Resolved": int(entity.resolved_percentage * 100), + "AutoScore": entity.auto_score, + "Added": entity.date_added.strftime("%Y-%m-%d") if entity.date_added else None, + "Played": entity.date_last_play.strftime("%Y-%m-%d") if entity.date_last_play else None, + "Released": entity.date_released.strftime("%Y-%m-%d") if entity.date_released else None, + "LocalId": str(entity.local_id) if entity.local_id else None, + "Path": str(entity.local_path) if entity.local_path else None, + } def track_to_row(track: Track): - return { - "Title": track.title, + return entity_to_row(track) | { "Album": track.album.title if track.album else "", "Artists": ", ".join(a.title if a.title else "no_arist_name" for a in track.artists), "Lyrics": track.has_lyrics, - "AutoScore": track.auto_score, - "Added": track.added_at.strftime("%Y-%m-%d") if track.added_at else "", - "LocalId": str(track.local_id) if track.local_id else "", - "Path": str(track.local_path) if track.local_path else "", } def artist_to_row(artist: Artist): - return { - "Title": artist.title, - "AutoScore": artist.auto_score, - "Resolved": int(artist.resolved_percentage * 100), - "LocalId": str(artist.local_id) if artist.local_id else "", + return entity_to_row(artist) | { } def album_to_row(album: Album): - return { - "Title": album.title, - "AutoScore": album.auto_score, - "Resolved": int(album.resolved_percentage * 100), - "LocalId": str(album.local_id) if album.local_id else "", + return entity_to_row(album) | { } @@ -200,7 +202,7 @@ def run(): # flow.log_stats() gui = LibraryView() - gui.add_libraries([flow.spotify_library, flow.local_library]) + gui.add_libraries([flow.spotify_library, flow.itunes_library, flow.local_library]) gui.render() run() \ No newline at end of file diff --git a/src/Library.py b/src/Library.py index 7bcefac..246d581 100644 --- a/src/Library.py +++ b/src/Library.py @@ -4,77 +4,58 @@ from datetime import datetime from pathlib import Path from typing import List, Optional, Any + class Entity: def __init__(self): self.id: str = None - self.mbid : str = None + self.mbid: str = None self.title: str = None + self.duration_ms: int = None self.fav: bool = False self.rating: int = 0 - self.play_count : int = 0 - self.date_added : datetime = None - self.date_released : datetime = None - self.date_last_play : datetime = None + self.play_count: int = 0 + self.date_added: datetime = None + self.date_released: datetime = None + self.date_last_play: datetime = None self.auto_score: float = 0 self.local_id: str = None - self.resolved_percentage : float = 0 + self.local_path: Path = None + self.resolved_percentage: float = 0 -class Artist: + +class Artist(Entity): def __init__(self): - self.id: str = None - self.title: str = None + super().__init__() self.tracks: List[Track] = [] self.albums: List[Album] = [] - self.local_id: str = None - self.auto_score: float = 0 - self.fav: bool = False - self.rating: int = 0 - self.resolved_percentage : float = 0 -class Track: +class Track(Entity): def __init__(self): - self.id: str = None - self.title: str = None + super().__init__() + self.artists: List[Artist] = [] self.album: Album = None - self.duration_ms: int = None - self.added_at: datetime = None - self.local_path: Path = None + self.has_lyrics: bool = False - self.local_id: str = None - self.auto_score: float = 0 - self.fav: bool = False - self.rating: int = 0 -class Album: +class Album(Entity): def __init__(self): - self.id: str = None - self.title: str = None - self.release_date: datetime = None + super().__init__() self.tracks: List[Track] = [] self.artists: List[Artist] = [] - self.local_id: str = None - self.auto_score: float = 0 - self.fav: bool = False - self.rating: int = 0 - self.resolved_percentage : float = 0 -class Playlist: +class Playlist(Entity): def __init__(self): - self.title: str = None + super().__init__() self.tracks: List[Track] = [] self.created_at: datetime = None self.description: str = None - self.auto_score: float = 0 - self.fav: bool = False - self.rating: int = 0 - self.resolved_percentage : float = 0 class Library: @@ -120,14 +101,8 @@ class Library: artist.albums.append(album) def auto_score(self): - self.score_tracks() - self.score_artists() self.score_albums() - - def score_tracks(self): - for idx, track in enumerate(self.top_tracks): - score = len(self.top_tracks) - idx - track.auto_score = score + self.score_artists() def score_artists(self): for track in self.tracks: @@ -150,4 +125,4 @@ class Library: album.resolved_percentage = album_count_resolved / len(album.tracks) artist_count_resolved += album_count_resolved artist_count += len(album.tracks) - artist.resolved_percentage = artist_count_resolved / artist_count \ No newline at end of file + artist.resolved_percentage = artist_count_resolved / artist_count diff --git a/src/LibrarySerializer.py b/src/LibrarySerializer.py index f331824..278776a 100644 --- a/src/LibrarySerializer.py +++ b/src/LibrarySerializer.py @@ -26,60 +26,51 @@ class LibrarySaver: def __init__(self, library: Library): self.library = library - @staticmethod - def _artist_to_dict(artist: Artist) -> dict[str, Any]: + def _entity_to_dict(self, entity: Entity) -> dict[str, Any]: return { - "id": artist.id, - "title": artist.title, - "tracks": [track.id for track in artist.tracks], - "albums": [album.id for album in artist.albums], - "local_id": artist.local_id, - "auto_score": artist.auto_score, - "fav" : artist.fav, - "rating" : artist.rating, + "id": entity.id, + "mbid": entity.mbid, + + "title": entity.title, + "duration": entity.duration_ms, + + "fav" : entity.fav, + "rating" : entity.rating, + "play_count": entity.play_count, + "date_added": entity.date_added, + "date_released": entity.date_released, + "date_last_play": entity.date_last_play, + "auto_score": entity.auto_score, + + "local_id": entity.local_id, + "local_path": entity.local_path, + "resolved_percentage": entity.resolved_percentage, } - @staticmethod - def _track_to_dict(track: Track) -> dict[str, Any]: - return { - "id": track.id, - "title": track.title, + def _artist_to_dict(self, artist: Artist) -> dict[str, Any]: + return self._entity_to_dict(artist) | { + "tracks": [track.id for track in artist.tracks], + "albums": [album.id for album in artist.albums], + } + + def _track_to_dict(self, track: Track) -> dict[str, Any]: + return self._entity_to_dict(track) | { "artists": [artist.id for artist in track.artists], "album": track.album.id, "duration_ms": track.duration_ms, - "added_at": _dt_to_str(track.added_at), - "local_path": track.local_path, "has_lyrics": track.has_lyrics, - "local_id": track.local_id, - "auto_score": track.auto_score, - "fav" : track.fav, - "rating" : track.rating, } - @staticmethod - def _playlist_to_dict(playlist: Playlist) -> dict[str, Any]: - return { - "title": playlist.title, + def _playlist_to_dict(self, playlist: Playlist) -> dict[str, Any]: + return self._entity_to_dict(playlist) | { "tracks": [track.id for track in playlist.tracks], - "created_at": _dt_to_str(playlist.created_at), "description": playlist.description, - "auto_score": playlist.auto_score, - "fav" : playlist.fav, - "rating" : playlist.rating, } - @staticmethod - def _album_to_dict(album: Album) -> dict[str, Any]: - return { - "id": album.id, - "title": album.title, - "release_date": album.release_date, + def _album_to_dict(self ,album: Album) -> dict[str, Any]: + return self._entity_to_dict(album) | { "tracks": [track.id for track in album.tracks], "artists": [artist.id for artist in album.artists], - "local_id": album.local_id, - "auto_score": album.auto_score, - "fav" : album.fav, - "rating" : album.rating, } def save(self, path: Path) -> None: @@ -100,6 +91,7 @@ class LibrarySaver: json.dump(data, f, indent=2, ensure_ascii=False) + class LibraryLoader: def __init__(self): self.track_map: dict[str, Track] = {} @@ -127,48 +119,54 @@ class LibraryLoader: self.track_map[id] = item return item + def load_entity(self, entity : Entity, data: dict[str, Any]): + entity.id = data.get("id") + entity.mbid = data.get("mbid") + + entity.title = data.get("title") + entity.duration_ms = data.get("duration_ms") + + entity.fav = data.get("fav") + entity.rating = data.get("rating") + entity.play_count = data.get("play_count") + entity.date_added = _str_to_dt(data.get("date_added")) + entity.date_released = _str_to_dt(data.get("date_released")) + entity.date_last_play = _str_to_dt(data.get("date_last_play")) + entity.auto_score = data.get("auto_score") + + entity.local_id = data.get("local_id") + entity.local_path = data.get("local_path") + entity.resolved_percentage = data.get("resolved_percentage") + + def load_track(self, data: dict[str, Any]) -> Track: track = self.get_track(data.get("id")) - track.id = data.get("id") - track.title = data.get("title") + self.load_entity(track, data) + track.duration_ms = data.get("duration_ms") - track.added_at = _str_to_dt(data.get("added_at")) - track.local_path = data.get("local_path") track.artists = [self.get_artist(artist_id) for artist_id in data.get("artists")] track.album = self.get_album(data.get("album")) track.has_lyrics = data.get("has_lyrics") - track.local_id = data.get("local_id") - track.auto_score = data.get("auto_score") - track.fav = data.get("fav") - track.rating = data.get("rating") self.track_map[track.id] = track return track def load_artist(self, data: dict[str, Any]): artist = self.get_artist(data.get("id")) - artist.id = data.get("id") - artist.title = data.get("title") + self.load_entity(artist, data) + artist.tracks = [self.get_track(track_id) for track_id in data.get("tracks")] artist.albums = [self.get_album(album_id) for album_id in data.get("albums")] if data.get("albums") else [] - artist.local_id = data.get("local_id") - artist.auto_score = data.get("auto_score") - artist.fav = data.get("fav") - artist.rating = data.get("rating") self.artist_map[artist.id] = artist return artist def load_album(self, data: dict[str, Any]): album = self.get_album(data.get("id")) - album.id = data.get("id") + self.load_entity(album, data) + album.tracks = [self.get_track(track_id) for track_id in data.get("tracks")] - album.title = data.get("title") album.artists = [self.get_artist(artist_id) for artist_id in data.get("artists")] - album.local_id = data.get("local_id") - album.auto_score = data.get("auto_score") - album.fav = data.get("fav") - album.rating = data.get("rating") self.album_map[album.id] = album return album diff --git a/src/backends/itunes/ParseItunesXml.py b/src/backends/itunes/ParseItunesXml.py index 0af465f..71eb2d7 100755 --- a/src/backends/itunes/ParseItunesXml.py +++ b/src/backends/itunes/ParseItunesXml.py @@ -167,6 +167,7 @@ class ParseItunesXml: track.title = data.get("Name") track.artists = self.parse_artists([data.get("Artist")]) track.album = self.parse_album(data.get("Album")) + track.play_count = int(data.get("Play Count")) if not track.album or not len(track.artists) or not track.title: raise "error parsing" @@ -183,10 +184,9 @@ class ParseItunesXml: self.library.name = "Itunes" + for track in self.library.tracks: + track.auto_score = track.play_count + return self.library - def parse_track_item(self, id, track: dict[str, Any]) -> Track: - - return track - diff --git a/src/backends/spotify/ParseSpotify.py b/src/backends/spotify/ParseSpotify.py index 29c24e7..cfca39d 100644 --- a/src/backends/spotify/ParseSpotify.py +++ b/src/backends/spotify/ParseSpotify.py @@ -33,6 +33,8 @@ class Parser: self.library.name = "Spotify" self.library.auto_score() + self.score_tracks() + return self.library @@ -122,3 +124,8 @@ class Parser: self.library.artists.append(artist) return artist + + def score_tracks(self): + for idx, track in enumerate(self.library.top_tracks): + score = len(self.library.top_tracks) - idx + track.auto_score = score \ No newline at end of file