stable - generate local playlists & add default options
This commit is contained in:
parent
11e3b06824
commit
892568d668
4 changed files with 132 additions and 26 deletions
|
|
@ -3,7 +3,7 @@ from SpotifyWebAPI import find_song, update_access_token
|
||||||
|
|
||||||
from rapidfuzz import fuzz
|
from rapidfuzz import fuzz
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
|
import requests
|
||||||
|
|
||||||
def spotify_pattern_generator():
|
def spotify_pattern_generator():
|
||||||
update_access_token()
|
update_access_token()
|
||||||
|
|
@ -11,15 +11,29 @@ def spotify_pattern_generator():
|
||||||
local_library = get_data("local_library")
|
local_library = get_data("local_library")
|
||||||
spotify_library = get_data("tracks")
|
spotify_library = get_data("tracks")
|
||||||
|
|
||||||
|
|
||||||
def find_local_songs_on_spotify(local_lib):
|
def find_local_songs_on_spotify(local_lib):
|
||||||
|
search_log = []
|
||||||
|
|
||||||
total = len(local_library)
|
total = len(local_library)
|
||||||
found_tracks_map = {}
|
found_tracks_map = {}
|
||||||
with tqdm(total=total, desc='Searching local songs on spotify') as pbar:
|
with tqdm(total=total, desc='Searching local songs on spotify') as pbar:
|
||||||
for track_id, track in local_lib.items():
|
for track_id, track in local_lib.items():
|
||||||
search_pattern = f"{track['name']} {track['artist']} {track['album']}"
|
parts = [track["name"], track["artist"], track.get("album")]
|
||||||
found_tracks = find_song(search_pattern)
|
search_pattern = " ".join(p for p in parts if p)
|
||||||
found_tracks_map[track_id] = found_tracks[0:1]
|
|
||||||
|
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)
|
pbar.update(1)
|
||||||
|
|
||||||
|
save_data(search_log, "spotify_search_log")
|
||||||
|
|
||||||
return found_tracks_map
|
return found_tracks_map
|
||||||
|
|
||||||
spotify_found_tracks = find_local_songs_on_spotify(local_library)
|
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")
|
save_data(mappings, "link_pattern_tags")
|
||||||
|
|
||||||
|
|
||||||
def run_pattern_generators():
|
def run_pattern_generators(type_id = 0):
|
||||||
#fuzzy_pattern_generator()
|
spotify_pattern_generator()
|
||||||
#spotify_pattern_generator()
|
|
||||||
fuzzy_tag_pattern_generator()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
||||||
50
LocalPLaylistGenerator.py
Normal file
50
LocalPLaylistGenerator.py
Normal 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
7
TODO
Normal 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
73
main.py
|
|
@ -1,12 +1,19 @@
|
||||||
from DataBase import *
|
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
|
from SpotifyWebAPI import fetch_data, update_access_token
|
||||||
import cmd
|
import cmd
|
||||||
|
|
||||||
|
local_library_path = "/home/auser/Music/"
|
||||||
|
rel_autogen_dir = "0autogen"
|
||||||
|
|
||||||
playlists = get_data('playlistTracks')
|
playlists = get_data('playlistTracks')
|
||||||
top_artists = get_data('top_artists')
|
top_artists = get_data('top_artists')
|
||||||
top_tracks = get_data('top_tracks')
|
top_tracks = get_data('top_tracks')
|
||||||
user_tracks = get_data('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")
|
local_library = get_data("local_library")
|
||||||
user_tracks_by_id = {track['track']['id']: track['track'] for track in user_tracks}
|
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'])}'")
|
print(f"'{remote_track['name']}' by '{get_artists_str(remote_track['artists'])}'")
|
||||||
index += 1
|
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):
|
class Interpreter(cmd.Cmd):
|
||||||
intro = "Welcome to the interactive command loop. Type help or ? to list commands.\n"
|
intro = "Welcome to the interactive command loop. Type help or ? to list commands.\n"
|
||||||
prompt = "(spotify) "
|
prompt = "(spotify) "
|
||||||
|
|
||||||
def do_link_patterns(self, arg):
|
def do_list_stat_vs_types(self, arg):
|
||||||
"""prints available linking patterns"""
|
"""prints available linking patterns"""
|
||||||
print(f"0 - fuzzy tags")
|
print(f"0 - fuzzy tags")
|
||||||
print(f"1 - spotify")
|
print(f"1 - spotify")
|
||||||
print(f"2 - fuzzy")
|
print(f"2 - fuzzy")
|
||||||
|
|
||||||
def do_link_pattern(self, arg):
|
def do_stat_local_library_vs_spotify(self, arg):
|
||||||
if not len(arg.split()):
|
"""compares local library and remote with the link pattern generated by the generate_pattern_command"""
|
||||||
print("expected name of the pattern")
|
print_link_stats(link_pattern)
|
||||||
return
|
|
||||||
|
|
||||||
"""prints available linking patterns"""
|
|
||||||
pattern_id = int(arg.split()[0])
|
|
||||||
print_link_stats(link_patterns[pattern_id])
|
|
||||||
|
|
||||||
def do_playlists(self, arg):
|
def do_playlists(self, arg):
|
||||||
"""prints user playlists"""
|
"""prints user playlists"""
|
||||||
|
|
@ -168,6 +162,24 @@ class Interpreter(cmd.Cmd):
|
||||||
"""Print tracks"""
|
"""Print tracks"""
|
||||||
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):
|
def do_fetch(self, arg):
|
||||||
"""Fetch all spotify data"""
|
"""Fetch all spotify data"""
|
||||||
try:
|
try:
|
||||||
|
|
@ -184,6 +196,31 @@ class Interpreter(cmd.Cmd):
|
||||||
except ValueError:
|
except ValueError:
|
||||||
print("Can not fetch the data.")
|
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):
|
def do_exit(self, arg):
|
||||||
"""Exit the command loop: exit"""
|
"""Exit the command loop: exit"""
|
||||||
print("Goodbye!")
|
print("Goodbye!")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue