nice stats and stable

This commit is contained in:
Шурупов Илья Викторович 2026-01-06 02:34:04 +03:00
parent 11fb2816e3
commit 9c9ddb7a9e
4 changed files with 81 additions and 80 deletions

View file

@ -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):

View file

@ -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()

View file

@ -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")

View file

@ -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}.")