diff --git a/README.md b/README.md index 45d3e02..8d078d7 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,9 @@ ## What is mmdl ❓ -MMDL is a cli app which allows you to quickly and efficiently download one or multiple songs from YouTube. +**TLDR**: MMDL is a cli app which allows you to quickly and efficiently download one or multiple songs from YouTube. + +I wanted a tool to quickly and efficiently download songs/audios from YouTube based on the name of the song. I also wanted the songs to be tagged with metadata. This is why I created this simple to use tool. ### Why diff --git a/images/mmdl_go.png b/images/mmdl_go.png new file mode 100644 index 0000000..f9258a4 Binary files /dev/null and b/images/mmdl_go.png differ diff --git a/images/mmdl_help.png b/images/mmdl_help.png new file mode 100644 index 0000000..98ebabf Binary files /dev/null and b/images/mmdl_help.png differ diff --git a/mmdl.egg-info/PKG-INFO b/mmdl.egg-info/PKG-INFO index aa5f0a8..1e1d37a 100644 --- a/mmdl.egg-info/PKG-INFO +++ b/mmdl.egg-info/PKG-INFO @@ -24,7 +24,9 @@ License-File: LICENSE ## What is mmdl ❓ -MMDL is a cli app which allows you to quickly and efficiently download one or multiple songs from YouTube. +**TLDR**: MMDL is a cli app which allows you to quickly and efficiently download one or multiple songs from YouTube. + +I wanted a tool to quickly and efficiently download songs/audios from YouTube based on the name of the song. I also wanted the songs to be tagged with metadata. This is why I created this simple to use tool. ### Why diff --git a/mmdl.egg-info/SOURCES.txt b/mmdl.egg-info/SOURCES.txt index e4e9aec..d7d3e0a 100644 --- a/mmdl.egg-info/SOURCES.txt +++ b/mmdl.egg-info/SOURCES.txt @@ -6,7 +6,6 @@ mmdl/__init__.py mmdl/ask.py mmdl/cli.py mmdl/globals.py -mmdl/mdl.py mmdl/utils.py mmdl/yt_utils.py mmdl.egg-info/PKG-INFO diff --git a/mmdl/cli.py b/mmdl/cli.py index 22ce7e9..64f3e0d 100644 --- a/mmdl/cli.py +++ b/mmdl/cli.py @@ -17,6 +17,7 @@ from mmdl import MusicDownloader from .ask import asker from rich.console import Console +from .utils import * import questionary console = Console() from click_help_colors import HelpColorsGroup, HelpColorsCommand @@ -50,23 +51,15 @@ def go(verbose, debug): # Go Command > Easy way to download songs. Uses inputs/inquirer. (Simply run 'mmdl go') """ + print_logo() if debug: console.log("Verbose: ", verbose) console.log("Debug: ", debug) - console.print("""\b[red] - _ _ - | | | - _ __ ___ _ __ ___ __| | | - | '_ ` _ \| '_ ` _ \ / _` | | - | | | | | | | | | | | (_| | | [/]by techboy-coder[red] - |_| |_| |_|_| |_| |_|\__,_|_| [/]find me on https://github.com/techboy-coder - - [green]mmdl [Mega Music Downloader] - A tool to easily download music.[/green] - """) + song_names=asker() - console.print("\n[cyan]> [/] Total number of songs: %s. \n" % (len(song_names))) + num_of_songs_printer(len(song_names)) # List, verbose, debug MusicDownloader(song_names, verbose, debug).download_songs() @@ -114,19 +107,11 @@ def file(file, seperator, verbose, debug): console.log("Verbose: ", verbose) console.log("Debug: ", debug) - console.print("""\b[red] - _ _ - | | | - _ __ ___ _ __ ___ __| | | - | '_ ` _ \| '_ ` _ \ / _` | | - | | | | | | | | | | | (_| | | [/]by techboy-coder[red] - |_| |_| |_|_| |_| |_|\__,_|_| [/]find me on https://github.com/techboy-coder - - [green]mmdl [Mega Music Downloader] - A tool to easily download music.[/green] - """) - console.print("\n[cyan]> [/] Total number of songs: %s. \n" % (len(songs))) - if not questionary.confirm("Do you want to continue").ask(): - quit() + print_logo() + + num_of_songs_printer(len(songs)) + + wanna_continue() # file, verbose, debug MusicDownloader(songs, verbose, debug).download_songs() return @@ -174,17 +159,8 @@ def list(songs, verbose, debug, ask): console.log("Verbose: ", verbose) console.log("Debug: ", debug) - console.print("""\b[red] - _ _ - | | | - _ __ ___ _ __ ___ __| | | - | '_ ` _ \| '_ ` _ \ / _` | | - | | | | | | | | | | | (_| | | [/]by techboy-coder[red] - |_| |_| |_|_| |_| |_|\__,_|_| [/]find me on https://github.com/techboy-coder - - [green]mmdl [Mega Music Downloader] - A tool to easily download music.[/green] - """) - console.print("\n[cyan]> [/] Total number of songs: %s. \n" % (len(songs_list))) + print_logo() + num_of_songs_printer(len(songs_list)) # file, verbose, debug MusicDownloader(songs_list, verbose, debug).download_songs() return @@ -209,9 +185,8 @@ def ytmusic(file, verbose, debug, ask): """ if not ask and not file: ask = True - if ask: - console.print("[cyan][>][/] We'll be manually asking you for the file location.") - console.print(""" + + guide = """ [bold red]YT-Music[/bold red]. - Go to your YTMusic liked songs playlist (https://music.youtube.com/playlist?list=LM) - Make sure you are logged in @@ -219,56 +194,39 @@ def ytmusic(file, verbose, debug, ask): - Keep scrolling down until you reach the end of your playlist (Songs will stop loading) - Copy the all the html markup - Create a text file and paste the html into it. - """) - if not questionary.confirm("Only continue if you have done the task.").ask(): - quit() + """ + + if ask: + console.print("[cyan][>][/] We'll be manually asking you for the file location.") + console.print(guide) + wanna_continue("Only continue if you have done the task.") + file = questionary.path("Where is that file located?").ask() with open(file, "r", encoding="utf-8") as f: data = f.read() - h = fromstring(data) - sel = CSSSelector("yt-formatted-string.title.style-scope.ytmusic-responsive-list-item-renderer.complex-string > a.yt-simple-endpoint.style-scope.yt-formatted-string") - songs_list=[e.text for e in sel(h)] - if not songs_list[0]: - console.log("[red][-] Hmm. No songs could be parsed from html. Did you select the correct HTML? If you see a error please make a bug report. Thanks!") - quit() + if not ask: if not file: console.log("[red][-] You need to enter the file location or add the -a flag.") quit() data = file.read() - h = fromstring(data) - sel = CSSSelector("yt-formatted-string.title.style-scope.ytmusic-responsive-list-item-renderer.complex-string > a.yt-simple-endpoint.style-scope.yt-formatted-string") - songs_list=[e.text for e in sel(h)] - if not songs_list[0]: - console.log("[red][-] Hmm. No songs could be parsed from html. Did you select the correct HTML? If you see a error please make a bug report. Thanks!") - console.print(""" -[bold red]YT-Music[/bold red]. -- Go to your YTMusic liked songs playlist (https://music.youtube.com/playlist?list=LM) -- Make sure you are logged in -- Press Ctrl/Cmd + Shift + i and open the dev tools -- Keep scrolling down until you reach the end of your playlist (Songs will stop loading) -- Copy the all the html markup -- Create a text file and paste the html into it. - """) + + + h = fromstring(data) + sel = CSSSelector("yt-formatted-string.title.style-scope.ytmusic-responsive-list-item-renderer.complex-string > a.yt-simple-endpoint.style-scope.yt-formatted-string") + songs_list=[e.text for e in sel(h)] + if not songs_list[0]: + console.log("[red][-] Hmm. No songs could be parsed from html. Did you select the correct HTML? If you see a error please make a bug report. Thanks!") + console.print(guide) if debug: console.log("Songs: ", str(songs_list)) console.log("Verbose: ", verbose) console.log("Debug: ", debug) - console.print("""\b[red] - _ _ - | | | - _ __ ___ _ __ ___ __| | | - | '_ ` _ \| '_ ` _ \ / _` | | - | | | | | | | | | | | (_| | | [/]by techboy-coder[red] - |_| |_| |_|_| |_| |_|\__,_|_| [/]find me on https://github.com/techboy-coder - - [green]mmdl [Mega Music Downloader] - A tool to easily download music.[/green] - """) - console.print("\n[cyan]> [/] Total number of songs: %s. \n" % (len(songs_list))) - if not questionary.confirm("Do you want to continue").ask(): - quit() + print_logo() + num_of_songs_printer(len(songs_list)) + wanna_continue() # file, verbose, debug MusicDownloader(songs_list, verbose, debug).download_songs() return @@ -300,16 +258,7 @@ def single(song, verbose, debug): console.log("Verbose: ", verbose) console.log("Debug: ", debug) - console.print("""\b[red] - _ _ - | | | - _ __ ___ _ __ ___ __| | | - | '_ ` _ \| '_ ` _ \ / _` | | - | | | | | | | | | | | (_| | | [/]by techboy-coder[red] - |_| |_| |_|_| |_| |_|\__,_|_| [/]find me on https://github.com/techboy-coder - - [green]mmdl [Mega Music Downloader] - A tool to easily download music.[/green] - """) + print_logo() songs = [song] console.print("\n[cyan]> [/] Song: %s. \n" % (song)) # file, verbose, debug diff --git a/mmdl/mdl.py b/mmdl/mdl.py index 3586800..531abbf 100644 --- a/mmdl/mdl.py +++ b/mmdl/mdl.py @@ -36,81 +36,6 @@ ##Dev Dep import shutil -class DisplayablePath(object): - display_filename_prefix_middle = '├──' - display_filename_prefix_last = '└──' - display_parent_prefix_middle = ' ' - display_parent_prefix_last = '│ ' - - def __init__(self, path, parent_path, is_last): - self.path = Path(str(path)) - self.parent = parent_path - self.is_last = is_last - if self.parent: - self.depth = self.parent.depth + 1 - else: - self.depth = 0 - - @property - def displayname(self): - if self.path.is_dir(): - return self.path.name + '/' - return self.path.name - - @classmethod - def make_tree(cls, root, parent=None, is_last=False, criteria=None): - root = Path(str(root)) - criteria = criteria or cls._default_criteria - - displayable_root = cls(root, parent, is_last) - yield displayable_root - - children = sorted(list(path - for path in root.iterdir() - if criteria(path)), - key=lambda s: str(s).lower()) - count = 1 - for path in children: - is_last = count == len(children) - if path.is_dir(): - yield from cls.make_tree(path, - parent=displayable_root, - is_last=is_last, - criteria=criteria) - else: - yield cls(path, displayable_root, is_last) - count += 1 - - @classmethod - def _default_criteria(cls, path): - return True - - @property - def displayname(self): - if self.path.is_dir(): - return self.path.name + '/' - return self.path.name - - def displayable(self): - if self.parent is None: - return self.displayname - - _filename_prefix = (self.display_filename_prefix_last - if self.is_last - else self.display_filename_prefix_middle) - - parts = ['{!s} {!s}'.format(_filename_prefix, - self.displayname)] - - parent = self.parent - while parent and parent.parent is not None: - parts.append(self.display_parent_prefix_middle - if parent.is_last - else self.display_parent_prefix_last) - parent = parent.parent - - return ''.join(reversed(parts)) - class MusicDownloader(): def __init__(self, song_names: list[str], verbose: int, debug: bool): """Music Downloader Class. This is the main class :/ @@ -171,6 +96,7 @@ def download_songs(self): # sourcery no-metrics ] temp = [] cwd = Utils.get_cwd() + for f in filenames: # print(f["filename"]) does_exist = executor.submit(Utils.check_file_exists, f["filename"], f) diff --git a/mmdl/utils.py b/mmdl/utils.py index 5b29b05..e8426ec 100644 --- a/mmdl/utils.py +++ b/mmdl/utils.py @@ -2,6 +2,9 @@ from pathlib import Path from rich.logging import RichHandler import concurrent.futures +from rich.console import Console +import questionary +console = Console() # log=logging @@ -58,4 +61,36 @@ def check_file_exists(filepath: str, rest): # print("lkj") # print(path, filepath) # print(Path(path+"/"+filepath).is_file()) - return [Path(filepath).is_file(), rest] \ No newline at end of file + # if any(File.endswith(".mp3") or File.endswith(".mp3") for File in os.listdir(".")): + # return True + # return False + exists=Path(os.path.splitext(filepath)[0]+".mp3").is_file() or Path(os.path.splitext(filepath)[0]+".m4a").is_file() or Path(os.path.splitext(filepath)[0]+".opus").is_file() + return [exists, rest] + + +def print_logo(): + console.print("""\b[red] + _ _ + | | | + _ __ ___ _ __ ___ __| | | + | '_ ` _ \| '_ ` _ \ / _` | | + | | | | | | | | | | | (_| | | [/]by techboy-coder[red] + |_| |_| |_|_| |_| |_|\__,_|_| [/]find me on https://github.com/techboy-coder + + [green]mmdl [Mega Music Downloader] - A tool to easily download music.[/green] + """) + +def num_of_songs_printer(num): + console.print("\n[cyan]> [/] Total number of songs: %s. \n" % (num)) + +def wanna_continue(msg="Do you want to continue"): + if not questionary.confirm(msg).ask(): + quit() + +def check_file(filename): + possible_extensions = ('', '.txt', '.dat') + for e in possible_extensions: + if os.path.isfile(filename + e): + return filename + e + else: + return None \ No newline at end of file diff --git a/mmdl/yt_utils.py b/mmdl/yt_utils.py index 6662d60..ac63da0 100644 --- a/mmdl/yt_utils.py +++ b/mmdl/yt_utils.py @@ -23,7 +23,7 @@ def __init__(self, mmdl: bool): { 'key': 'FFmpegExtractAudio', 'preferredcodec': 'mp3', - 'preferredquality': '192', + # 'preferredquality': '192', } ], 'outtmpl': str(self.output_dir)+'/%(title)s.%(ext)s', @@ -40,15 +40,11 @@ def download(self, url: str, do_download: bool, rest: None): with youtube_dl.YoutubeDL(self.ytdl_opts) as ydl: info = ydl.extract_info(url, download=True) filename = ydl.prepare_filename(info) - filename = filename.replace("webm","mp3") - filename = filename.replace("m4a", "mp3") return {"filename": filename, "url": url, "rest":rest} else: with youtube_dl.YoutubeDL(self.ytdl_opts) as ydl: info = ydl.extract_info(url, download=False) filename = ydl.prepare_filename(info) - filename = filename.replace("webm","mp3") - filename = filename.replace("m4a", "mp3") return {"filename": filename, "url": url, "rest":rest} def logger(self):