From d6e9542dc43dc443a21911aca6e5eb1012088845 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 22 Nov 2025 14:46:30 +0000 Subject: [PATCH 1/5] Initial plan From f4de9b23da189a15d4a82bf391ae9bc646b7f256 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 22 Nov 2025 14:51:54 +0000 Subject: [PATCH 2/5] Improve code efficiency and fix performance issues Co-authored-by: d4lion <111100025+d4lion@users.noreply.github.com> --- .../src/MonoDownloads/audio_dowload_gui.py | 8 ++- MonoLoad/src/MonoDownloads/audio_download.py | 12 ++--- MonoLoad/src/MonoDownloads/video_download.py | 41 +++++++++----- .../src/MultiDownloads/audio_downloads.py | 7 +-- .../src/MultiDownloads/video_downloads.py | 8 ++- MonoLoad/src/Routes/pages.py | 5 +- MonoLoad/src/transformations.py | 53 ++++++++++++------- 7 files changed, 85 insertions(+), 49 deletions(-) diff --git a/MonoLoad/src/MonoDownloads/audio_dowload_gui.py b/MonoLoad/src/MonoDownloads/audio_dowload_gui.py index 0dd15e9..b497ba3 100644 --- a/MonoLoad/src/MonoDownloads/audio_dowload_gui.py +++ b/MonoLoad/src/MonoDownloads/audio_dowload_gui.py @@ -1,6 +1,6 @@ from pytube import YouTube from typing import Optional -from os import getcwd +from os import getcwd, path from re import sub @@ -22,7 +22,11 @@ def download_audio(video: YouTube, resolution: Optional[str] = '128kbps', video_title: Optional[str] = '', - out_path: Optional[str] = f'{cwd}/downloads/audios') -> str: + out_path: Optional[str] = None) -> str: + """Download audio from YouTube video""" + if out_path is None: + out_path = path.join(cwd, 'downloads', 'audios') + quality = audioTagsQuality.get(resolution) try: stream = video.streams.get_by_itag(quality) diff --git a/MonoLoad/src/MonoDownloads/audio_download.py b/MonoLoad/src/MonoDownloads/audio_download.py index 6095fd9..87f8323 100644 --- a/MonoLoad/src/MonoDownloads/audio_download.py +++ b/MonoLoad/src/MonoDownloads/audio_download.py @@ -7,15 +7,13 @@ init(autoreset=True) -def download(url: Required[str], out_path: Optional[str] = f"{getcwd()}/downloads/audios") -> NoReturn: +def download(url: Required[str], out_path: Optional[str] = None) -> NoReturn: + if out_path is None: + out_path = path.join(getcwd(), "downloads", "audios") def get_resolution(streams_data: StreamQuery) -> list[str]: - resolutions = list() - - for stream in streams_data: - resolutions.append(stream.abr) - - return resolutions + """Extract available resolutions from streams""" + return [stream.abr for stream in streams_data] def audio_quality_download(resolutions: Required[list[str]], video) -> str: print(f"\n video title: {Fore.RED + video.title} \n {Fore.WHITE} \n qualities: {resolutions}") diff --git a/MonoLoad/src/MonoDownloads/video_download.py b/MonoLoad/src/MonoDownloads/video_download.py index afcc954..e19e021 100644 --- a/MonoLoad/src/MonoDownloads/video_download.py +++ b/MonoLoad/src/MonoDownloads/video_download.py @@ -1,6 +1,5 @@ -from os import getcwd +from os import getcwd, path from tqdm import tqdm -from time import sleep from pytube import YouTube, streams from .itags import videos_itags_resolution as video_itags @@ -9,12 +8,23 @@ init(autoreset=True) -def download(url, out_path=f"{getcwd()}/downloads/videos"): +def download(url, out_path=None): + if out_path is None: + out_path = path.join(getcwd(), "downloads", "videos") - def progress(stream, data_chunk, bytes_remaing ): - bar_format = '{l_bar}{bar}| {n_fmt}/{total_fmt} {postfix}' - bar = tqdm(total=stream.filesize, bar_format=bar_format) - bar.update(stream.filesize - bytes_remaing) + def progress(stream, data_chunk, bytes_remaining): + """Callback for download progress""" + # Calculate progress + total_size = stream.filesize + bytes_downloaded = total_size - bytes_remaining + percentage = (bytes_downloaded / total_size) * 100 + + # Update progress bar + if not hasattr(progress, 'bar'): + progress.bar = tqdm(total=total_size, unit='B', unit_scale=True, + desc='Downloading', bar_format='{l_bar}{bar}| {n_fmt}/{total_fmt}') + + progress.bar.update(len(data_chunk)) def dowload_by_resolution(resolutions, video): print(f"\n video title: {Fore.RED + video.title} \n {Fore.WHITE} \n qualities: {resolutions}") @@ -23,21 +33,24 @@ def dowload_by_resolution(resolutions, video): return video_quality_dowload def get_resolutions(stream_data): - resolutions = list() + resolutions = set() for data in stream_data: - resolutions.append(data.resolution) + if data.resolution: + resolutions.add(data.resolution) - resolutions = list(filter(lambda x: x is not None, set(resolutions))) - return resolutions + return sorted(list(resolutions)) - - video = YouTube(url) + video = YouTube(url, on_progress_callback=progress) stream_data = video.streams.filter(file_extension="mp4") resolutions = get_resolutions(stream_data) video_quality_to_dowload = dowload_by_resolution(resolutions=resolutions, video=video) stream = video.streams.get_by_itag(video_itags.get(video_quality_to_dowload)) - stream.download(output_path=out_path) \ No newline at end of file + stream.download(output_path=out_path) + + # Close progress bar if it exists + if hasattr(progress, 'bar'): + progress.bar.close() \ No newline at end of file diff --git a/MonoLoad/src/MultiDownloads/audio_downloads.py b/MonoLoad/src/MultiDownloads/audio_downloads.py index dc34d7c..3b0f675 100644 --- a/MonoLoad/src/MultiDownloads/audio_downloads.py +++ b/MonoLoad/src/MultiDownloads/audio_downloads.py @@ -1,4 +1,4 @@ -from os import getcwd, listdir, rename +from os import getcwd, listdir, path from .itags import audio_itags_quality from pytube import YouTube, streams from colorama import Fore, init @@ -17,7 +17,7 @@ def multi_download(url: Required[str], quality: Required[str] , out_path: Requir def download(audios_url: Required[list], quality: Optional[str]="128kbps", threads: Optional[int]=2, out_path: Optional[ - str] = f"{getcwd()}/downloads/audios") -> list[str]: + str] = None) -> list[str]: """ Variables --------- @@ -30,7 +30,8 @@ def download(audios_url: Required[list], quality: Optional[str]="128kbps", threa * out_path (str): In this variable, the proportional folder is searched for where the download folder with the audios is to be stored. """ - + if out_path is None: + out_path = path.join(getcwd(), "downloads", "audios") with ThreadPoolExecutor(max_workers=threads) as executor: for url in audios_url: diff --git a/MonoLoad/src/MultiDownloads/video_downloads.py b/MonoLoad/src/MultiDownloads/video_downloads.py index 09a4b30..4f439f2 100644 --- a/MonoLoad/src/MultiDownloads/video_downloads.py +++ b/MonoLoad/src/MultiDownloads/video_downloads.py @@ -1,4 +1,4 @@ -from os import getcwd +from os import getcwd, path from .itags import videos_itags_resolution from pytube import YouTube, streams from colorama import init, Fore @@ -17,7 +17,11 @@ def multi_download(url: str, quality:str ,out_path: str) -> None: -def download(videos_url: list, threads: int , quality: str , filename: str ,out_path=f"{getcwd()}/downloads/videos") -> None: +def download(videos_url: list, threads: int , quality: str , filename: str ,out_path=None) -> None: + """Download multiple videos concurrently""" + if out_path is None: + out_path = path.join(getcwd(), "downloads", "videos") + # Send in a pool Threads the videos url to download with ThreadPoolExecutor(threads) as executor: for url in videos_url: diff --git a/MonoLoad/src/Routes/pages.py b/MonoLoad/src/Routes/pages.py index 4c12254..755930c 100644 --- a/MonoLoad/src/Routes/pages.py +++ b/MonoLoad/src/Routes/pages.py @@ -1,4 +1,6 @@ import flet as ft +from time import sleep +from MonoLoad.src.MonoDownloads.audio_dowload_gui import get_audio_resolution, download_audio def HomeComponent(page): @@ -26,9 +28,6 @@ def HomeComponent(page): def DowloadAudioComponent(page): - from time import sleep - from MonoLoad.src.MonoDownloads.audio_dowload_gui import get_audio_resolution, download_audio - qualities = ft.Row(alignment=ft.MainAxisAlignment.CENTER, spacing=40, scroll=ft.ScrollMode.AUTO) NameTextField = ft.TextField(label='Name') diff --git a/MonoLoad/src/transformations.py b/MonoLoad/src/transformations.py index 575318d..7a7c9d7 100644 --- a/MonoLoad/src/transformations.py +++ b/MonoLoad/src/transformations.py @@ -1,23 +1,40 @@ -from os import getcwd -from typing import Optional, Union +from os import getcwd, path +from typing import Optional -def transform_config_to_dict(config_path: Optional[str] = getcwd()+"/src/page.config") -> dict: +def transform_config_to_dict(config_path: Optional[str] = None) -> dict: + """ + Transform a configuration file into a dictionary. + + Args: + config_path: Path to the configuration file. Defaults to src/page.config + + Returns: + Dictionary with configuration keys and values + """ + if config_path is None: + # Get the directory of this file, then navigate to page.config + base_dir = path.dirname(path.abspath(__file__)) + config_path = path.join(base_dir, "page.config") - with open(config_path, "r+") as f: - lineas: list = list(f.readlines()) - - listas_limpia: list[str] = [linea.strip() for linea in lineas] - - lista_valores_tupla: list[tuple[Union[str, int]]] = [] - - for elemento in listas_limpia: - clave, valor = elemento.split("=") - if valor.isdigit(): - lista_valores_tupla.append((clave, int(valor))) - elif isinstance(eval(valor), bool): - lista_valores_tupla.append((clave, eval(valor))) + config_dict = {} + + with open(config_path, "r") as f: + for line in f: + line = line.strip() + if not line or "=" not in line: + continue + + key, value = line.split("=", 1) + key = key.strip() + value = value.strip() + + # Parse value type without using eval() + if value.isdigit(): + config_dict[key] = int(value) + elif value.lower() in ("true", "false"): + config_dict[key] = value.lower() == "true" else: - lista_valores_tupla.append((clave, valor)) + config_dict[key] = value - return dict(lista_valores_tupla) + return config_dict From 49a5b94bac6adc70850ce04167fccba3f8d1e5df Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 22 Nov 2025 14:53:58 +0000 Subject: [PATCH 3/5] Address code review feedback - fix negative number parsing and typos Co-authored-by: d4lion <111100025+d4lion@users.noreply.github.com> --- MonoLoad/src/MonoDownloads/video_download.py | 34 ++++++++++---------- MonoLoad/src/transformations.py | 2 +- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/MonoLoad/src/MonoDownloads/video_download.py b/MonoLoad/src/MonoDownloads/video_download.py index e19e021..e20dd88 100644 --- a/MonoLoad/src/MonoDownloads/video_download.py +++ b/MonoLoad/src/MonoDownloads/video_download.py @@ -12,25 +12,25 @@ def download(url, out_path=None): if out_path is None: out_path = path.join(getcwd(), "downloads", "videos") + # Progress bar state + progress_bar = {'bar': None} + def progress(stream, data_chunk, bytes_remaining): """Callback for download progress""" - # Calculate progress - total_size = stream.filesize - bytes_downloaded = total_size - bytes_remaining - percentage = (bytes_downloaded / total_size) * 100 - - # Update progress bar - if not hasattr(progress, 'bar'): - progress.bar = tqdm(total=total_size, unit='B', unit_scale=True, - desc='Downloading', bar_format='{l_bar}{bar}| {n_fmt}/{total_fmt}') + # Create progress bar on first call + if progress_bar['bar'] is None: + total_size = stream.filesize + progress_bar['bar'] = tqdm(total=total_size, unit='B', unit_scale=True, + desc='Downloading', bar_format='{l_bar}{bar}| {n_fmt}/{total_fmt}') - progress.bar.update(len(data_chunk)) + # Update with the size of the chunk just downloaded + progress_bar['bar'].update(len(data_chunk)) - def dowload_by_resolution(resolutions, video): + def download_by_resolution(resolutions, video): print(f"\n video title: {Fore.RED + video.title} \n {Fore.WHITE} \n qualities: {resolutions}") - video_quality_dowload = input("\n Type the quality: ") - return video_quality_dowload + video_quality_download = input("\n Type the quality: ") + return video_quality_download def get_resolutions(stream_data): resolutions = set() @@ -46,11 +46,11 @@ def get_resolutions(stream_data): stream_data = video.streams.filter(file_extension="mp4") resolutions = get_resolutions(stream_data) - video_quality_to_dowload = dowload_by_resolution(resolutions=resolutions, video=video) + video_quality_to_download = download_by_resolution(resolutions=resolutions, video=video) - stream = video.streams.get_by_itag(video_itags.get(video_quality_to_dowload)) + stream = video.streams.get_by_itag(video_itags.get(video_quality_to_download)) stream.download(output_path=out_path) # Close progress bar if it exists - if hasattr(progress, 'bar'): - progress.bar.close() \ No newline at end of file + if progress_bar['bar'] is not None: + progress_bar['bar'].close() \ No newline at end of file diff --git a/MonoLoad/src/transformations.py b/MonoLoad/src/transformations.py index 7a7c9d7..3adf672 100644 --- a/MonoLoad/src/transformations.py +++ b/MonoLoad/src/transformations.py @@ -30,7 +30,7 @@ def transform_config_to_dict(config_path: Optional[str] = None) -> dict: value = value.strip() # Parse value type without using eval() - if value.isdigit(): + if value.lstrip('-').isdigit(): config_dict[key] = int(value) elif value.lower() in ("true", "false"): config_dict[key] = value.lower() == "true" From a8023fe5978afcc2b2982c91fcf85f47c98b1286 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 22 Nov 2025 15:12:41 +0000 Subject: [PATCH 4/5] Update Flet API to use capitalized Icons and Colors Co-authored-by: d4lion <111100025+d4lion@users.noreply.github.com> --- MonoLoad/main.py | 2 +- MonoLoad/src/Routes/pages.py | 4 ++-- requirements.txt | 4 +--- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/MonoLoad/main.py b/MonoLoad/main.py index eb48df8..3c7b06d 100644 --- a/MonoLoad/main.py +++ b/MonoLoad/main.py @@ -38,7 +38,7 @@ def change_theme_mode(e): title=ft.Text('MonoLoad'), center_title=False, actions=[ - ft.IconButton(ft.icons.WB_SUNNY_OUTLINED, + ft.IconButton(ft.Icons.WB_SUNNY_OUTLINED, on_click=change_theme_mode), ft.PopupMenuButton( items=[ diff --git a/MonoLoad/src/Routes/pages.py b/MonoLoad/src/Routes/pages.py index 755930c..d518aa4 100644 --- a/MonoLoad/src/Routes/pages.py +++ b/MonoLoad/src/Routes/pages.py @@ -52,8 +52,8 @@ def close_banner(e): except Exception as e: print(e) page.banner = ft.Banner( - leading=ft.Icon(ft.icons.WARNING_AMBER_ROUNDED, - color=ft.colors.AMBER, size=40), + leading=ft.Icon(ft.Icons.WARNING_AMBER_ROUNDED, + color=ft.Colors.AMBER, size=40), content=ft.Text( "It seems that you have used an incorrect or invalid url, please check it and try again." ), diff --git a/requirements.txt b/requirements.txt index 459d9a2..2f04bf7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,9 +7,7 @@ charset-normalizer==3.3.2 click==8.1.7 colorama==0.4.6 cookiecutter==2.5.0 -flet==0.17.0 -flet-core==0.17.0 -flet-runtime==0.17.0 +flet==0.28.3 h11==0.14.0 httpcore==0.17.3 httpx==0.24.1 From 04bb91e9fd5d518bcde6626f4b37352c9e55e9bd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 22 Nov 2025 15:19:56 +0000 Subject: [PATCH 5/5] Fix HTTP 400 error by replacing pytube with pytubefix Co-authored-by: d4lion <111100025+d4lion@users.noreply.github.com> --- MonoLoad/src/MonoDownloads/audio_dowload_gui.py | 2 +- MonoLoad/src/MonoDownloads/audio_download.py | 2 +- MonoLoad/src/MonoDownloads/video_download.py | 2 +- MonoLoad/src/MultiDownloads/audio_downloads.py | 2 +- MonoLoad/src/MultiDownloads/video_downloads.py | 2 +- requirements.txt | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/MonoLoad/src/MonoDownloads/audio_dowload_gui.py b/MonoLoad/src/MonoDownloads/audio_dowload_gui.py index b497ba3..b49a5fb 100644 --- a/MonoLoad/src/MonoDownloads/audio_dowload_gui.py +++ b/MonoLoad/src/MonoDownloads/audio_dowload_gui.py @@ -1,4 +1,4 @@ -from pytube import YouTube +from pytubefix import YouTube from typing import Optional from os import getcwd, path from re import sub diff --git a/MonoLoad/src/MonoDownloads/audio_download.py b/MonoLoad/src/MonoDownloads/audio_download.py index 87f8323..3b46839 100644 --- a/MonoLoad/src/MonoDownloads/audio_download.py +++ b/MonoLoad/src/MonoDownloads/audio_download.py @@ -1,4 +1,4 @@ -from pytube import YouTube, streams, StreamQuery, Stream +from pytubefix import YouTube, streams, StreamQuery, Stream from colorama import init, Fore from .itags import audio_itags_quality from os import getcwd, rename, listdir, path diff --git a/MonoLoad/src/MonoDownloads/video_download.py b/MonoLoad/src/MonoDownloads/video_download.py index e20dd88..b8366ff 100644 --- a/MonoLoad/src/MonoDownloads/video_download.py +++ b/MonoLoad/src/MonoDownloads/video_download.py @@ -1,7 +1,7 @@ from os import getcwd, path from tqdm import tqdm -from pytube import YouTube, streams +from pytubefix import YouTube, streams from .itags import videos_itags_resolution as video_itags from colorama import init, Fore diff --git a/MonoLoad/src/MultiDownloads/audio_downloads.py b/MonoLoad/src/MultiDownloads/audio_downloads.py index 3b0f675..bced22e 100644 --- a/MonoLoad/src/MultiDownloads/audio_downloads.py +++ b/MonoLoad/src/MultiDownloads/audio_downloads.py @@ -1,6 +1,6 @@ from os import getcwd, listdir, path from .itags import audio_itags_quality -from pytube import YouTube, streams +from pytubefix import YouTube, streams from colorama import Fore, init from concurrent.futures import ThreadPoolExecutor from typing import Required, Optional diff --git a/MonoLoad/src/MultiDownloads/video_downloads.py b/MonoLoad/src/MultiDownloads/video_downloads.py index 4f439f2..8c6eb87 100644 --- a/MonoLoad/src/MultiDownloads/video_downloads.py +++ b/MonoLoad/src/MultiDownloads/video_downloads.py @@ -1,6 +1,6 @@ from os import getcwd, path from .itags import videos_itags_resolution -from pytube import YouTube, streams +from pytubefix import YouTube, streams from colorama import init, Fore from concurrent.futures import ThreadPoolExecutor diff --git a/requirements.txt b/requirements.txt index 2f04bf7..a7a22b2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -25,7 +25,7 @@ pypng==0.20220715.0 pytest==7.4.0 python-dateutil==2.8.2 python-slugify==8.0.1 -pytube==15.0.0 +pytubefix==10.3.5 PyYAML==6.0.1 qrcode==7.4.2 repath==0.9.0