stable - generate local playlists & add default options

This commit is contained in:
Шурупов Илья Викторович 2026-01-04 01:37:47 +03:00
parent 4734befe98
commit 2824bd1a0e
4 changed files with 132 additions and 26 deletions

View file

@ -3,7 +3,7 @@ from SpotifyWebAPI import find_song, update_access_token
from rapidfuzz import fuzz
from tqdm import tqdm
import requests
def spotify_pattern_generator():
update_access_token()
@ -11,15 +11,29 @@ def spotify_pattern_generator():
local_library = get_data("local_library")
spotify_library = get_data("tracks")
def find_local_songs_on_spotify(local_lib):
search_log = []
total = len(local_library)
found_tracks_map = {}
with tqdm(total=total, desc='Searching local songs on spotify') as pbar:
for track_id, track in local_lib.items():
search_pattern = f"{track['name']} {track['artist']} {track['album']}"
found_tracks = find_song(search_pattern)
found_tracks_map[track_id] = found_tracks[0:1]
parts = [track["name"], track["artist"], track.get("album")]
search_pattern = " ".join(p for p in parts if p)
if search_pattern == "":
print(f"cannot generate search pattern for the track - {track["path"]}")
pbar.update(1)
continue
found_tracks = find_song(search_pattern)[0:1]
found_tracks_map[track_id] = found_tracks
search_log.append({"path" : track["path"], "search_pattern": search_pattern, "result" : found_tracks_map[track_id]})
pbar.update(1)
save_data(search_log, "spotify_search_log")
return found_tracks_map
spotify_found_tracks = find_local_songs_on_spotify(local_library)
@ -127,10 +141,8 @@ def fuzzy_tag_pattern_generator():
save_data(mappings, "link_pattern_tags")
def run_pattern_generators():
#fuzzy_pattern_generator()
#spotify_pattern_generator()
fuzzy_tag_pattern_generator()
def run_pattern_generators(type_id = 0):
spotify_pattern_generator()
if __name__ == "__main__":

50
LocalPLaylistGenerator.py Normal file
View file

@ -0,0 +1,50 @@
from pathlib import Path
def generate_playlists(local_library, library_path, remote_playlists, link_pattern, output_dir):
output_dir = Path(output_dir)
output_playlists = []
remote_to_local_map = {}
for local_id, links in link_pattern.items():
if len(links['items']):
remote_to_local_map[links['items'][0]] = local_id
for name, tracks in remote_playlists.items():
local_tracks = []
for track in tracks:
track = track["track"]
path = "error"
error = ""
if track is None:
error = f"error: track is none"
elif track["id"] in remote_to_local_map:
path = local_library[remote_to_local_map[track["id"]]]["path"]
else:
error = f"error: not_found - {track["name"]}"
if error != "":
local_tracks.append("# " + error)
else:
path = (output_dir / ".." ).resolve() / Path(path)
local_tracks.append(path)
pl = {
"name": name,
"tracks": local_tracks,
}
output_playlists.append(pl)
for pl in output_playlists:
output_dir.mkdir(parents=True, exist_ok=True)
playlist_path = Path(output_dir) / f"{pl['name']}.m3u"
with (playlist_path.open("w", encoding="utf-8") as f):
for track in pl["tracks"]:
f.write(str(track))
f.write("\n")

7
TODO Normal file
View file

@ -0,0 +1,7 @@
skip playlists that are not created by user
create one big playlist with the order of the spotify and dont skip missing
generate percentage like hierarchy of the missing items
generate detailed log of the missing items and search results

73
main.py
View file

@ -1,12 +1,19 @@
from DataBase import *
from LocalLibraryIndexer import update_local_library
from LinkerPatternGenerator import run_pattern_generators
from LocalPLaylistGenerator import generate_playlists
from SpotifyWebAPI import fetch_data, update_access_token
import cmd
local_library_path = "/home/auser/Music/"
rel_autogen_dir = "0autogen"
playlists = get_data('playlistTracks')
top_artists = get_data('top_artists')
top_tracks = get_data('top_tracks')
user_tracks = get_data('tracks')
link_patterns = [get_data('link_pattern_tags'), get_data('link_pattern_tags'), get_data('link_pattern_tags')]
link_pattern = get_data('link_pattern_spotify')
local_library = get_data("local_library")
user_tracks_by_id = {track['track']['id']: track['track'] for track in user_tracks}
@ -109,33 +116,20 @@ def print_link_stats(link_pattern):
print(f"'{remote_track['name']}' by '{get_artists_str(remote_track['artists'])}'")
index += 1
return
print(f"\nUnresolved tracks from the local: {len(unresolved_local)}")
index = 0
for link in unresolved_local:
local_track = local_library[link]
print(f" {index}: '{local_track['name']}' by '{local_track['artist']}'")
index += 1
class Interpreter(cmd.Cmd):
intro = "Welcome to the interactive command loop. Type help or ? to list commands.\n"
prompt = "(spotify) "
def do_link_patterns(self, arg):
def do_list_stat_vs_types(self, arg):
"""prints available linking patterns"""
print(f"0 - fuzzy tags")
print(f"1 - spotify")
print(f"2 - fuzzy")
def do_link_pattern(self, arg):
if not len(arg.split()):
print("expected name of the pattern")
return
"""prints available linking patterns"""
pattern_id = int(arg.split()[0])
print_link_stats(link_patterns[pattern_id])
def do_stat_local_library_vs_spotify(self, arg):
"""compares local library and remote with the link pattern generated by the generate_pattern_command"""
print_link_stats(link_pattern)
def do_playlists(self, arg):
"""prints user playlists"""
@ -168,6 +162,24 @@ class Interpreter(cmd.Cmd):
"""Print tracks"""
print_tracks()
def do_generate_local_playlists(self, arg):
"""generates local playlists based on the vs comparison type"""
generate_playlists(local_library, local_library_path, playlists, link_pattern, local_library_path + rel_autogen_dir)
def do_index_local_library(self, arg):
"""find local tracks and fetches the data from them for further analysis"""
global local_library
update_local_library(local_library_path)
local_library = get_data("local_library")
def do_generate_vs_stat(self, arg):
"""compares local and remote library and generates the vs stats"""
global link_pattern
run_pattern_generators()
link_pattern = get_data('link_pattern_spotify')
def do_fetch(self, arg):
"""Fetch all spotify data"""
try:
@ -184,6 +196,31 @@ class Interpreter(cmd.Cmd):
except ValueError:
print("Can not fetch the data.")
def do_generate_todo(self, arg):
"""generates todos for the pirates"""
print("sorry not implemented yet.")
def do_run_all_default(self, arg):
"""runs general flow"""
local_lib_path = "/home/auser/Music/"
print("Fetching spotify data...")
self.do_fetch([])
print("Fetching local data...")
self.do_index_local_library(f"{local_lib_path}/organized")
print("Generating vs patterns...")
self.do_generate_vs_stat("0")
print("Generating todos...")
self.do_generate_todo(f"{local_lib_path}")
print("Generating local playlists...")
self.do_generate_local_playlists(f"{local_lib_path}")
return True
def do_exit(self, arg):
"""Exit the command loop: exit"""
print("Goodbye!")