import os import json import yaml from yt_dlp import YoutubeDL from Playlist_Updater import update import re CONFIG_FILE = "config.yaml" NOW_PLAYING_FILE = "/home/media/Music/now_playing.json" MUSIC_BASE = "/home/media/Music" with open(CONFIG_FILE, "r") as f: config = yaml.safe_load(f) sources = config.get("sources", {}) if os.path.exists(NOW_PLAYING_FILE): with open(NOW_PLAYING_FILE, "r") as f: now_playing = json.load(f) else: now_playing = {} ydl_opts = { "quiet": True, "extract_flat": True, "dump_single_json": True, "skip_download": True } def sanitize_filename(title): return title.replace("/", "-") pending_files = {} for name, data in sources.items(): url = data.get("youtube_url") id_cache_file = data.get("id_cache") if not url or not id_cache_file: continue print(f"Processing Playlist: {name}") try: with open(id_cache_file, "r") as f: content = f.read().strip() old_ids = set(json.loads(content)) if content else set() except FileNotFoundError: old_ids = set() with YoutubeDL(ydl_opts) as ydl: info = ydl.extract_info(url, download=False) new_ids = set(entry["id"] for entry in info["entries"]) to_add = new_ids - old_ids to_remove = old_ids - new_ids print(f"To download: {len(to_add)}") print(f"To remove: {len(to_remove)}") # where new music will be saved folder_path = os.path.join(MUSIC_BASE, name.capitalize()) os.makedirs(folder_path, exist_ok=True) # Define download options for yt-dlp download_opts = { "format": "bestaudio/best", "quiet": False, "postprocessors": [{ "key": "FFmpegExtractAudio", "preferredcodec": "mp3", "preferredquality": "192", }] } if to_add: with YoutubeDL({"quiet": True}) as metadata_ydl: for vid in to_add: try: info = metadata_ydl.extract_info(f"https://youtu.be/{vid}", download=False) title = info.get("title", vid) safe_title = sanitize_filename(title) filename = f"{safe_title} [{vid}]" full_output_path = os.path.join(folder_path, filename) if os.path.exists(full_output_path): print(f"File already downloaded") continue print(f"downloading... {filename}") download_opts["outtmpl"] = full_output_path with YoutubeDL(download_opts) as downloader: downloader.download([f"https://youtu.be/{vid}"]) except Exception as e: print(f"Failed to process {vid}: {e}") currently_playing = now_playing.get(name, "").strip() pending_delete = [] for file in os.listdir(folder_path): if not file.endswith(".mp3"): continue full_path = os.path.join(folder_path, file) # Try to match video ID match = re.search(r'\[([a-zA-Z0-9_-]{11})\]\.mp3$', file) if not match: continue file_id = match.group(1) # Only delete if: # - the ID is in the removal list # - the file is NOT currently playing if file_id in to_remove: if file == currently_playing: print(f"postponing deletion (still playing): {file}") pending_delete.append(file_id) pending_files.setdefault(name, []).append(file) else: print(f"Deleting: {file}") os.remove(full_path) with open(id_cache_file, "w") as f: json.dump(sorted(new_ids), f, indent=2) print(f"Saved updated ID cache: {id_cache_file}") print("\nšŸ” Regenerating .m3u playlists...") update(skip_files=pending_files) print("āœ… Playlist sync complete.")