import os import sys import json import yt_dlp g_OUTPUT_FOLDER = "cd_audio" def generate_ytdl_opts(outtmpl: str, metadata_artist: str = "", metadata_title: str = ""): return { "format": "bestaudio/best", "noplaylist": True, "outtmpl": outtmpl, "postprocessors": [ { "key": "FFmpegExtractAudio", "preferredcodec": "wav", } ], "postprocessor_args": [ "-ar", "44100", "-ac", "2", "-metadata", f"artist={metadata_artist}", "-metadata", f"title={metadata_title}", ], "ignoreerrors": True, } def download_playlist_for_cd(playlist_url: str): ytdl_opts = generate_ytdl_opts( os.path.join(g_OUTPUT_FOLDER, "%(playlist_index)s - %(title)s.%(ext)s") ) print(f"Downloading audio optimized for CD burning: {playlist_url}") with yt_dlp.YoutubeDL(ytdl_opts) as ytdl: ytdl.download([playlist_url]) print("\nDownload complete! Import these .wav files directly into your CD burner.") def download_single_video(url, song_num, artist, title): ytdl_opts = generate_ytdl_opts( os.path.join(g_OUTPUT_FOLDER, f"{song_num} - {artist} - {title}.%(ext)s"), artist, title ) with yt_dlp.YoutubeDL(ytdl_opts) as ytdl: ytdl.download([url]) def parse_file(filename): class Track: def __init__(self, track: int, title: str, url: str): self.track = track self.title = title self.url = url class Album: def __init__(self, artist: str, album: str): self.artist = artist self.album = album self.tracks = [] def add_track(self, track: Track): self.tracks.append(track) with open(filename, "r") as file: data = json.load(file) albums = [] for album in data: try: new_album = Album( album["artist"], album["album"]) for video in album["tracks"]: new_album.add_track(Track( video["track"], video["title"], video["url"] )) except KeyError as e: print(e) continue albums.append(new_album) return albums def print_help(): print("-h print help and exit") print("-f select a file (required)") print("-a download all") print("-d download a specific album") def parse_args(argv): class Ret: file = None downloads = None ret = Ret() cache_arg_count = len(argv) arg_idx = 1 while arg_idx < cache_arg_count: if argv[arg_idx] == "-h": print_help() exit(1) elif argv[arg_idx] == "-f": if arg_idx + 1 >= cache_arg_count: raise ValueError("-f - is missing the filename") ret.file = argv[arg_idx + 1] arg_idx += 2 continue elif argv[arg_idx] == "-a": if ret.downloads != None: raise ValueError("Cannot use -a twice, or in combination with -d") ret.downloads = "" arg_idx += 1 continue elif argv[arg_idx] == "-d": if ret.downloads != None: raise ValueError("Cannot use -d twice, or in combination with -a") if arg_idx + 1 >= cache_arg_count: raise ValueError("-d - is missing the album name") ret.downloads = argv[arg_idx + 1] arg_idx += 2 continue else: print_help() exit(2) return ret if __name__ == "__main__": config = parse_args(sys.argv) if not os.path.exists(g_OUTPUT_FOLDER): os.makedirs(g_OUTPUT_FOLDER) data = parse_file(config.file) for album in data: if not (config.downloads == "" or config.downloads == album.album): continue print(f"Band: {album.artist}") print(f"Album: {album.album}") for video in album.tracks: print(f"\tTrack {video.track}: {video.title}") download_single_video( video.url, video.track, f"{album.artist} - {album.album}", video.title )