From 19bd44b3dc107041bcd5c5bf7d3f6c3abf4a0905 Mon Sep 17 00:00:00 2001 From: Lukas Pupka-Lipinski Date: Mon, 15 Sep 2025 10:28:32 +0200 Subject: [PATCH] added: better progressbars --- src/Loaders/AniWorldLoader.py | 13 +++--- src/Loaders/Loader.py | 2 +- src/Loaders/Loaders.py | 4 +- src/Loaders/Providers.py | 4 +- src/Loaders/provider/doodstream.py | 2 +- src/Loaders/provider/voe.py | 2 +- src/Main.py | 64 +++++++++++++++++------------- src/SerieScanner.py | 2 +- 8 files changed, 49 insertions(+), 44 deletions(-) diff --git a/src/Loaders/AniWorldLoader.py b/src/Loaders/AniWorldLoader.py index f818f5b..a259455 100644 --- a/src/Loaders/AniWorldLoader.py +++ b/src/Loaders/AniWorldLoader.py @@ -12,8 +12,8 @@ from fake_useragent import UserAgent from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry -from src.Loaders.Loader import Loader -from src.Loaders.Providers import Providers +from Loaders.Loader import Loader +from Loaders.Providers import Providers from yt_dlp import YoutubeDL import shutil @@ -146,7 +146,7 @@ class AniworldLoader(Loader): return languageCode in languages - def Download(self, baseDirectory: str, serieFolder: str, season: int, episode: int, key: str, language: str = "German Dub") -> bool: + def Download(self, baseDirectory: str, serieFolder: str, season: int, episode: int, key: str, language: str = "German Dub", progress_callback: callable = None) -> bool: sanitized_anime_title = ''.join( char for char in self.GetTitle(key) if char not in self.INVALID_PATH_CHARS ) @@ -168,8 +168,6 @@ class AniworldLoader(Loader): output_path = os.path.join(folderPath, output_file) os.makedirs(os.path.dirname(output_path), exist_ok=True) - - # Get the system-designated temp directory temp_dir = "./Temp/" os.makedirs(os.path.dirname(temp_dir), exist_ok=True) temp_Path = os.path.join(temp_dir, output_file) @@ -187,6 +185,8 @@ class AniworldLoader(Loader): if header: ydl_opts['http_headers'] = header + if progress_callback: + ydl_opts['progress_hooks'] = [progress_callback] with YoutubeDL(ydl_opts) as ydl: ydl.download([link]) @@ -341,6 +341,3 @@ class AniworldLoader(Loader): episode_counts[season] = len(unique_links) return episode_counts - - - diff --git a/src/Loaders/Loader.py b/src/Loaders/Loader.py index 7d2ed25..9cd8f88 100644 --- a/src/Loaders/Loader.py +++ b/src/Loaders/Loader.py @@ -11,7 +11,7 @@ class Loader(ABC): pass @abstractmethod - def Download(self, baseDirectory: str, serieFolder: str, season: int, episode: int, key: str) -> bool: + def Download(self, baseDirectory: str, serieFolder: str, season: int, episode: int, key: str, progress_callback: callable = None) -> bool: pass @abstractmethod diff --git a/src/Loaders/Loaders.py b/src/Loaders/Loaders.py index 0e5d53e..db3921e 100644 --- a/src/Loaders/Loaders.py +++ b/src/Loaders/Loaders.py @@ -1,5 +1,5 @@ -from src.Loaders.AniWorldLoader import AniworldLoader -from src.Loaders.Loader import Loader +from Loaders.AniWorldLoader import AniworldLoader +from Loaders.Loader import Loader class Loaders: diff --git a/src/Loaders/Providers.py b/src/Loaders/Providers.py index c4ac974..382ad4e 100644 --- a/src/Loaders/Providers.py +++ b/src/Loaders/Providers.py @@ -1,7 +1,7 @@ -from src.Loaders.provider.Provider import Provider -from src.Loaders.provider.voe import VOE +from Loaders.provider.Provider import Provider +from Loaders.provider.voe import VOE class Providers: diff --git a/src/Loaders/provider/doodstream.py b/src/Loaders/provider/doodstream.py index b472703..fce86e9 100644 --- a/src/Loaders/provider/doodstream.py +++ b/src/Loaders/provider/doodstream.py @@ -4,7 +4,7 @@ import time from fake_useragent import UserAgent import requests -from src.Loaders.provider.Provider import Provider +from Loaders.provider.Provider import Provider class Doodstream(Provider): def __init__(self): diff --git a/src/Loaders/provider/voe.py b/src/Loaders/provider/voe.py index 6e06cc7..9e25f9a 100644 --- a/src/Loaders/provider/voe.py +++ b/src/Loaders/provider/voe.py @@ -7,7 +7,7 @@ from urllib3.util.retry import Retry import requests from bs4 import BeautifulSoup from fake_useragent import UserAgent -from src.Loaders.provider.Provider import Provider +from Loaders.provider.Provider import Provider # Compile regex patterns once for better performance REDIRECT_PATTERN = re.compile(r"https?://[^'\"<>]+") diff --git a/src/Main.py b/src/Main.py index 5014d7a..f11e689 100644 --- a/src/Main.py +++ b/src/Main.py @@ -1,12 +1,13 @@ import sys import os import logging -from src.Loaders import AniWorldLoader -import progressbar +from Loaders import AniWorldLoader + +from rich.progress import Progress import SerieList import SerieScanner -from src.Loaders.Loaders import Loaders -from src.Serie import Serie +from Loaders.Loaders import Loaders +from Serie import Serie import time # Configure logging @@ -38,8 +39,7 @@ class SeriesApp: def __init__(self, directory_to_search: str): print("Please wait while initializing...") - self.barTotal = None - self.bar = None + self.progress = None self.directory_to_search = directory_to_search self.Loaders = Loaders() loader = self.Loaders.GetLoader(key="aniworld.to") @@ -109,40 +109,47 @@ class SeriesApp: """Simulate the downloading process with a progress bar.""" total_downloaded = 0 total_episodes = sum(sum(len(ep) for ep in serie.episodeDict.values()) for serie in series) - self.barTotal = progressbar.ProgressBar( - max_value=total_episodes, - widgets=[ - progressbar.FormatLabel(f'Processing:'), - ' ', progressbar.Percentage(), - ' ', progressbar.Bar(), - ' ', progressbar.ETA() - ] - ) - + self.progress = Progress() + task1 = self.progress.add_task("[red]Processing...", total=total_episodes) + task2 = self.progress.add_task(f"[green]...", total=0) + self.task3 = self.progress.add_task(f"[Gray]...", total=100) # Setze total auf 100 für Prozentanzeige + self.progress.start() for serie in series: serie_episodes = sum(len(ep) for ep in serie.episodeDict.values()) + self.progress.update(task2, description=f"[green]{serie.name}", total=serie_episodes) downloaded = 0 - self.bar = progressbar.ProgressBar( - max_value=serie_episodes, - widgets=[ - progressbar.FormatLabel(f'Processing {serie.name}:'), - ' ', progressbar.Percentage(), - ' ', progressbar.Bar(), - ' ', progressbar.ETA() - ] - ) for season, episodes in serie.episodeDict.items(): for episode in episodes: loader = self.Loaders.GetLoader(key="aniworld.to") if loader.IsLanguage(season, episode, serie.key): - self.retry(loader.Download, 3, 1, self.directory_to_search, serie.folder, season, episode, serie.key) + self.retry(loader.Download, 3, 1, self.directory_to_search, serie.folder, season, episode, serie.key, self.print_Download_Progress) downloaded += 1 total_downloaded += 1 - self.bar.update(downloaded) - self.bar.update(total_downloaded) + self.progress.update(task1, advance=1) + self.progress.update(task2, advance=1) + time.sleep(0.02) + + self.progress.stop() + self.progress = None + + def print_Download_Progress(self, d): + # Nutze self.progress und self.task3 für Fortschrittsanzeige + if self.progress is None or not hasattr(self, 'task3'): + return + + if d['status'] == 'downloading': + total = d.get('total_bytes') or d.get('total_bytes_estimate') + downloaded = d.get('downloaded_bytes', 0) + if total: + percent = downloaded / total * 100 + self.progress.update(self.task3, completed=percent, description=f"[gray]Download: {percent:.1f}%") + else: + self.progress.update(self.task3, description=f"[gray]{downloaded/1024/1024:.2f}MB geladen") + elif d['status'] == 'finished': + self.progress.update(self.task3, completed=100, description="[gray]Download abgeschlossen.") def search_mode(self): """Search for a series and allow user to select an option.""" @@ -209,6 +216,7 @@ class SeriesApp: # Run the app if __name__ == "__main__": + # Read the base directory from an environment variable directory_to_search = os.getenv("ANIME_DIRECTORY", "\\\\sshfs.r\\ubuntu@192.168.178.43\\media\\serien\\Serien") app = SeriesApp(directory_to_search) diff --git a/src/SerieScanner.py b/src/SerieScanner.py index ec4b876..a33c989 100644 --- a/src/SerieScanner.py +++ b/src/SerieScanner.py @@ -5,7 +5,7 @@ from Serie import Serie import traceback from GlobalLogger import error_logger, noKeyFound_logger from Exceptions import NoKeyFoundException, MatchNotFoundError -from src.Loaders.Loader import Loader +from Loaders.Loader import Loader class SerieScanner: