From 9c9ddb7a9ed278ac06e9858a9bf786c6f4fd0736 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A8=D1=83=D1=80=D1=83=D0=BF=D0=BE=D0=B2=20=D0=98=D0=BB?= =?UTF-8?q?=D1=8C=D1=8F=20=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=D0=BE=D0=B2?= =?UTF-8?q?=D0=B8=D1=87?= Date: Tue, 6 Jan 2026 02:34:04 +0300 Subject: [PATCH] nice stats and stable --- Flow.py | 2 +- main.py | 2 +- src/StatGenerator.py | 155 ++++++++++++++++++++++--------------------- src/helpers.py | 2 +- 4 files changed, 81 insertions(+), 80 deletions(-) diff --git a/Flow.py b/Flow.py index 79c1759..bcbdd16 100644 --- a/Flow.py +++ b/Flow.py @@ -53,7 +53,7 @@ class Flow: resolve_remote_tracks(client_id, client_secret, self.local_library, self.spotify_library, work_dir) def log_stats(self): - log_stats(work_dir / "remote_to_local_map.json", work_dir / "stats.json", self.local_library, self.spotify_library) + log_stats(work_dir / "remote_to_local_map.json", work_dir / "stats", self.local_library, self.spotify_library) def run(self, arg): diff --git a/main.py b/main.py index f811d33..5e395a4 100644 --- a/main.py +++ b/main.py @@ -5,7 +5,7 @@ if __name__ == '__main__': flow = Flow.Flow() # flow.fetch_spotify() - # flow.fetch_local() + flow.fetch_local() flow.parse_spotify_library() flow.parse_local_library() diff --git a/src/StatGenerator.py b/src/StatGenerator.py index f597e17..ac16622 100644 --- a/src/StatGenerator.py +++ b/src/StatGenerator.py @@ -1,4 +1,5 @@ from src.Library import * +from src.helpers import * from pathlib import Path import json @@ -52,20 +53,8 @@ class ArtistStat: return (f"{str(int(self.resolved_precent))} % - " f"{self.artist.title}") - -class StatGenerator: - def __init__(self): - self.out = { - "local_count": 0, - "resolved_count": 0, - - "popular_artists": [], - "popular_albums": [], - - "unresolved_albums": [], - "unresolved_tracks": [], - } - +class LibraryStats: + def __init__(self, mapping_path : Path, local_lib: Library, remote_lib: Library): self.max_popular_albums = 100 self.max_popular_artists = 100 @@ -79,17 +68,19 @@ class StatGenerator: self.albums_map: dict[str, AlbumStat] = {} self.artists_map: dict[str, ArtistStat] = {} - self.local_lib: Library = Library() - self.remote_lib: Library = Library() + self.local_lib: Library = local_lib + self.remote_lib: Library = remote_lib - self.mapping: dict[str, Any] = Any + self.mapping: dict[str, Any] = get_mapping(mapping_path) self.remote_id_map: dict[str, Track] = {} self.local_id_map: dict[str, Track] = {} - def find_add_local_paths(self, mapping_path: Path): - self.mapping = get_mapping(mapping_path) + self.find_add_local_paths() + self.score_artists() + self.score_albums() + def find_add_local_paths(self): self.remote_id_map = {track.id: track for track in self.remote_lib.tracks} self.local_id_map = {track.id: track for track in self.local_lib.tracks} @@ -134,75 +125,85 @@ class StatGenerator: for artist in album_stat.album.artists: self.artists_map[artist.id].albums_stats.append(album_stat) - def stat_generic(self): - self.out["resolved_count"] = len(self.mapping) - def gen_album_songs(album_stat): - tracks = [] + def gen_album(self, album_stat, condition): + tracks = [] - for track in album_stat.album.tracks: + for track in album_stat.album.tracks: + if condition.should_add_track(track): tracks.append(f"{"✔" if is_resolved(track) else "𐄂"} {track.title} -> {track.local_path}") - return { - "album" : album_stat.get_str(), - "tracks" : tracks - } + return { + "album" : album_stat.get_str(), + "tracks" : tracks + } + + def gen_artist(self, artist_stat, condition): + albums = [] + + for album_stat in artist_stat.albums_stats: + if condition.should_add_album(album_stat): + albums.append(self.gen_album(album_stat, condition)) + + return { + "artist" : artist_stat.get_str(), + "albums" : albums, + } + + def generate_stat_tree(self, condition): + artists = [] for artist_stat in self.artists: - artist_log = { - "artist": artist_stat.get_str(), - "albums": [gen_album_songs(album) for album in artist_stat.albums_stats] - } + if condition.should_add_artist(artist_stat): + artists.append(self.gen_artist(artist_stat, condition)) - if len(self.out["popular_artists"]) < self.max_popular_artists: - self.out["popular_artists"].append(artist_log) + return { + "artists" : artists + } - for album_data in self.albums: - log = album_data.get_str() + def save_tree(self, output_path, condition): + save_data(self.generate_stat_tree(condition), output_path) - if len(self.out["popular_albums"]) < self.max_popular_albums: - self.out["popular_albums"].append(log) + def save_all(self, output_path): - def stat_unresolved_albums(self): - for album_data in self.albums: - if album_data.resolved_precent > self.resolved_percentage_threshold: - continue + class Condition: + def should_add_artist(self, item): + return True - log = album_data.get_str() + def should_add_album(self, item): + return True - if len(self.out["unresolved_albums"]) < self.max_unresolved_albums: - self.out["unresolved_albums"].append(log) - - def stat_unresolved_tracks(self): - self.out["local_count"] = len(self.local_lib.tracks) - - for track in self.remote_lib.top_tracks: - if not track.local_path: - - log = f"{track.title} - {track.artists[0].title if len(track.artists) else ""} - {track.album.title}" - if len(self.out["unresolved_tracks"]) < self.max_unresolved_tracks: - self.out["unresolved_tracks"].append(log) - - def save_stats(self, output_path: Path): - with output_path.open("w", encoding="utf-8") as f: - json.dump(self.out, f, indent=2, ensure_ascii=False) - - def log_stats(self, mapping_path: Path, output_path: Path, local_lib: Library, remote_lib: Library): - self.remote_lib = remote_lib - self.local_lib = local_lib - - self.find_add_local_paths(mapping_path) - - self.score_artists() - self.score_albums() - - self.stat_generic() - self.stat_unresolved_tracks() - self.stat_unresolved_albums() - - self.save_stats(output_path) + def should_add_track(self, item): + return True -def log_stats(mapping_path: Path, output_path: Path, local_lib: Library, remote_lib: Library): - stat_gen = StatGenerator() - stat_gen.log_stats(mapping_path, output_path, local_lib, remote_lib) + self.save_tree(output_path, Condition()) + + + def save_threshold(self, output_path, threshold = 80): + + class Condition: + def should_add_artist(self, item : ArtistStat): + for album in item.albums_stats: + if self.should_add_album(album): + return True + + return False + + def should_add_album(self, item: AlbumStat): + return item.resolved_precent < threshold and len(item.album.tracks) > 3 + + def should_add_track(self, item: Track): + return not is_resolved(item) + + self.save_tree(output_path, Condition()) + + + +def log_stats(mapping_path: Path, output_dir: Path, local_lib: Library, remote_lib: Library): + set_workdir(output_dir) + + stat = LibraryStats(mapping_path, local_lib, remote_lib) + + stat.save_all("stats_all") + stat.save_threshold("stats_missing") diff --git a/src/helpers.py b/src/helpers.py index 8f81bbf..3c7db3e 100644 --- a/src/helpers.py +++ b/src/helpers.py @@ -46,7 +46,7 @@ def save_data(data, name): file_path = os.path.join(data_dir, f"{name}.json") with open(file_path, 'w') as file: - json.dump(data, file, indent=4) + json.dump(data, file, indent=4, ensure_ascii=False) print(f"Data saved to {file_path}.")