From 8282820b2fd5dec5f84dc8662f0773d0ec59ec4f Mon Sep 17 00:00:00 2001 From: Ebraheem Alhetari Date: Thu, 28 Mar 2024 06:34:11 +0300 Subject: [PATCH] Display the video size next to the resolution --- .gitignore | 1 + CHANGELOG.md | 34 +++++++++++++++++++++++ pyutube/downloader.py | 63 +++++++++++++++++++++++++++++++++++++------ pyutube/utils.py | 14 +++++++--- setup.py | 2 +- 5 files changed, 102 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index ec19162..3dfe3d2 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ reinstall.sh *.mp4 # Byte-compiled / optimized / DLL files __pycache__/ +.pytest_cache/ *.py[cod] *$py.class diff --git a/CHANGELOG.md b/CHANGELOG.md index 8301673..ccc0295 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,33 @@ # Pyutube Changelog +## 1.1.7 + +- Added: Display the video size next to the resolution. + For example: + +``` + ✅ There is internet connection + + [?] Choose the file type you want to download: + Audio + + > Video + > Cancel the download + + Title: Write an Incredible Resume: 5 Golden Rules! + + [?] Choose the resolution you want to download: + 144p ~= 10.91 MB + 240p ~= 15.17 MB + 360p ~= 21.62 MB + 480p ~= 38.37 MB + > 720p ~= 70.31 MB + 1080p ~= 128.81 MB + Cancel the download +``` + +> **Note:** The video size is approximate, that's mean it's not exact 100%. + ## 1.1.6 - Added: Ability to show the tool version with the `-v` or `--version` option. @@ -36,6 +64,10 @@ pyutube https://youtube.com/watch?v=cMPnY7EuZvo ``` +```` + +``` + - Fix: Enhanced Output Representation when using `--help` ## 1.1.2 @@ -57,3 +89,5 @@ ## 1.0.0 - Initial release +``` +```` diff --git a/pyutube/downloader.py b/pyutube/downloader.py index 5eeb368..9993c92 100644 --- a/pyutube/downloader.py +++ b/pyutube/downloader.py @@ -68,14 +68,21 @@ def get_available_resolutions(self, video: YouTube) -> set: audio_stream = streams.filter( only_audio=True).order_by('mime_type').first() - resolutions = { - stream.resolution for stream in available_streams if stream.resolution - } + resolutions_with_sizes = self.get_video_resolutions_sizes( + available_streams, audio_stream + ) + + resolutions_with_sizes = sorted( + resolutions_with_sizes, key=lambda x: int( + x[0][:-1]) if x[0][:-1].isdigit() else float('inf') + ) - # sort resolutions - resolutions = sorted(resolutions, key=lambda x: int(x.split("p")[0])) + # Separate resolutions and sizes without using two loops + resolutions, sizes = zip(*resolutions_with_sizes) + resolutions = list(resolutions) + sizes = list(sizes) - return resolutions, available_streams, audio_stream + return resolutions, sizes, available_streams, audio_stream @yaspin(text=colored("Downloading the video...", "green"), color="green", spinner=Spinners.dots13) def get_video_streams(self, quality: str, streams: YouTube.streams) -> YouTube: @@ -144,9 +151,9 @@ def get_selected_stream(self, video): Returns: YouTube: The selected video stream. """ - resolutions, streams, video_audio = self.get_available_resolutions( + resolutions, sizes, streams, video_audio = self.get_available_resolutions( video) - self.quality = ask_resolution(resolutions) + self.quality = ask_resolution(resolutions, sizes) return [] if self.quality.startswith("cancel") else streams, video_audio @@ -222,6 +229,46 @@ def merging(self, video_name: str, audio_name: str, video_id: str): else: print("Merged video file not found in the output directory.") + @staticmethod + def get_video_resolutions_sizes(available_streams: list[YouTube], audio_stream: YouTube) -> list: + """ + Get the available video resolutions. + + Args: + available_streams: The available video streams. + audio_stream: The audio stream. + + Returns: + list: The available video resolutions. + """ + if not available_streams: + return [] + + # Calculate the total audio file size in bytes + audio_filesize_bytes = audio_stream.filesize_approx + + # Convert the audio file size to KB + audio_filesize_kb = audio_filesize_bytes / 1000 + + resolutions_with_sizes = [] + for stream in available_streams: + if stream.resolution: + # Calculate the total video file size including audio in bytes + video_filesize_bytes = stream.filesize_approx + \ + (2 * audio_filesize_bytes) + # Convert the video file size to KB or MB dynamically + if video_filesize_bytes >= 1024 * 1024: + # If size is >= 1 MB + video_filesize = \ + f"{video_filesize_bytes / (1024 * 1024):.2f} MB" + else: + video_filesize = f"{video_filesize_bytes / 1024:.2f} KB" + + resolutions_with_sizes.append( + (stream.resolution, video_filesize)) + + return resolutions_with_sizes + def download_video(self): """ Download a video from a given URL to a specified path. diff --git a/pyutube/utils.py b/pyutube/utils.py index fd21285..66f9ed6 100644 --- a/pyutube/utils.py +++ b/pyutube/utils.py @@ -167,7 +167,7 @@ def file_type() -> str: return answer -def ask_resolution(resolutions: set) -> str: +def ask_resolution(resolutions: set, sizes) -> str: """ Prompts the user to choose a resolution for download and returns the chosen resolution as a string. @@ -177,7 +177,14 @@ def ask_resolution(resolutions: set) -> str: Returns: str: The chosen resolution as a string. """ - resolution_choices = list(resolutions) + ["Cancel the download"] + # Create a dictionary to relate each size with its resolution + size_resolution_mapping = dict(zip(resolutions, sizes)) + + # Generate the choices for the user prompt + resolution_choices = [ + f"{size} ~= {resolution}" for size, resolution in size_resolution_mapping.items() + ] + ["Cancel the download"] + questions = [ inquirer.List( "resolution", @@ -195,7 +202,8 @@ def ask_resolution(resolutions: set) -> str: except Exception as error: error_console.print(f"Error: {error}") - return answer.lower() + # Extract the resolution part from the user's choice + return answer.split(" ~= ")[0] def ask_rename_file(filename: str) -> str: diff --git a/setup.py b/setup.py index cba3e71..e2f74d8 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ name="pyutube", # Version of the package - version="1.1.5", + version="1.1.7", # Required dependencies install_requires=[