From 0726175503764477e83c16d7e43857c6fe257e71 Mon Sep 17 00:00:00 2001 From: Robert Honz Date: Sat, 1 Mar 2025 21:53:46 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=91=B7=20CLI:=20Download=20list=20is=20sc?= =?UTF-8?q?rolling=20vertically=20and=20overall=20progress=20is=20shown=20?= =?UTF-8?q?at=20the=20bottom.=20Fixes=20#374?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tidal_dl_ng/cli.py | 109 +++++++++++++++++++++++++--------------- tidal_dl_ng/download.py | 17 ++++--- 2 files changed, 78 insertions(+), 48 deletions(-) diff --git a/tidal_dl_ng/cli.py b/tidal_dl_ng/cli.py index f99132e..e951fc5 100644 --- a/tidal_dl_ng/cli.py +++ b/tidal_dl_ng/cli.py @@ -4,9 +4,16 @@ from pathlib import Path from typing import Annotated, Optional import typer +from rich.console import Group from rich.live import Live -from rich.panel import Panel -from rich.progress import BarColumn, Console, Progress, SpinnerColumn, TextColumn +from rich.progress import ( + BarColumn, + Console, + Progress, + SpinnerColumn, + TaskProgressColumn, + TextColumn, +) from rich.table import Table from tidal_dl_ng import __version__ @@ -58,10 +65,20 @@ def _download(ctx: typer.Context, urls: list[str], try_login: bool = True) -> bo # Create initial objects. settings: Settings = Settings() progress: Progress = Progress( - "{task.description}", + TextColumn("[progress.description]{task.description}"), SpinnerColumn(), BarColumn(), - TextColumn("[progress.percentage]{task.percentage:>3.0f}%"), + TaskProgressColumn(), + refresh_per_second=20, + auto_refresh=True, + expand=True, + transient=False, # Prevent progress from disappearing + ) + progress_overall = Progress( + TextColumn("[progress.description]{task.description}"), + SpinnerColumn(), + BarColumn(), + TaskProgressColumn(), refresh_per_second=20, auto_refresh=True, expand=True, @@ -74,59 +91,69 @@ def _download(ctx: typer.Context, urls: list[str], try_login: bool = True) -> bo path_base=settings.data.download_base_path, fn_logger=fn_logger, progress=progress, + progress_overall=progress_overall, ) + progress_table = Table.grid() # Style Progress display. - progress_table.add_row(Panel.fit(progress, title="Download Progress", border_style="green", padding=(2, 2))) + progress_table.add_row(progress) + progress_table.add_row(progress_overall) + + progress_group = Group( + progress_table, + ) urls_pos_last = len(urls) - 1 # Use a single Live display for both progress and table - with Live(progress_table, refresh_per_second=20): - for item in urls: - media_type: MediaType | bool = False + with Live(progress_group, refresh_per_second=20, vertical_overflow="visible"): + try: + for item in urls: + media_type: MediaType | bool = False - # Extract media name and id from link. - if "http" in item: - media_type = get_tidal_media_type(item) - item_id = get_tidal_media_id(item) - file_template = get_format_template(media_type, settings) - else: - print(f"It seems like that you have supplied an invalid URL: {item}") - - continue - - # Download media. - if media_type in [MediaType.TRACK, MediaType.VIDEO]: - download_delay: bool = bool(settings.data.download_delay and urls.index(item) < urls_pos_last) - - dl.item( - media_id=item_id, media_type=media_type, file_template=file_template, download_delay=download_delay - ) - elif media_type in [MediaType.ALBUM, MediaType.PLAYLIST, MediaType.MIX, MediaType.ARTIST]: - item_ids: [int] = [] - - if media_type == MediaType.ARTIST: - media = instantiate_media(ctx.obj[CTX_TIDAL].session, media_type, item_id) - media_type = MediaType.ALBUM - item_ids = item_ids + all_artist_album_ids(media) + # Extract media name and id from link. + if "http" in item: + media_type = get_tidal_media_type(item) + item_id = get_tidal_media_id(item) + file_template = get_format_template(media_type, settings) else: - item_ids.append(item_id) + print(f"It seems like that you have supplied an invalid URL: {item}") - for item_id in item_ids: - dl.items( + continue + + # Download media. + if media_type in [MediaType.TRACK, MediaType.VIDEO]: + download_delay: bool = bool(settings.data.download_delay and urls.index(item) < urls_pos_last) + + dl.item( media_id=item_id, media_type=media_type, file_template=file_template, - video_download=ctx.obj[CTX_TIDAL].settings.data.video_download, - download_delay=settings.data.download_delay, + download_delay=download_delay, ) + elif media_type in [MediaType.ALBUM, MediaType.PLAYLIST, MediaType.MIX, MediaType.ARTIST]: + item_ids: [int] = [] - # Clear and stop progress display - progress.refresh() - progress.stop() - print("\nDownload completed!") + if media_type == MediaType.ARTIST: + media = instantiate_media(ctx.obj[CTX_TIDAL].session, media_type, item_id) + media_type = MediaType.ALBUM + item_ids = item_ids + all_artist_album_ids(media) + else: + item_ids.append(item_id) + + for item_id in item_ids: + dl.items( + media_id=item_id, + media_type=media_type, + file_template=file_template, + video_download=ctx.obj[CTX_TIDAL].settings.data.video_download, + download_delay=settings.data.download_delay, + ) + finally: + # Clear and stop progress display + progress.refresh() + progress.stop() return True diff --git a/tidal_dl_ng/download.py b/tidal_dl_ng/download.py index 73305b0..c147ce9 100644 --- a/tidal_dl_ng/download.py +++ b/tidal_dl_ng/download.py @@ -73,6 +73,7 @@ class Download: fn_logger: Callable progress_gui: ProgressBars progress: Progress + progress_overall: Progress def __init__( self, @@ -82,6 +83,7 @@ class Download: skip_existing: bool = False, progress_gui: ProgressBars = None, progress: Progress = None, + progress_overall: Progress = None, ): self.settings = Settings() self.session = session @@ -89,6 +91,7 @@ class Download: self.fn_logger = fn_logger self.progress_gui = progress_gui self.progress = progress + self.progress_overall = progress_overall self.path_base = path_base if not self.settings.data.path_binary_ffmpeg and ( @@ -150,8 +153,6 @@ class Download: total_size_in_bytes: int = int(r.headers.get("content-length", 0)) block_size: int | None = 1048576 progress_total: float = total_size_in_bytes / block_size - except: - raise finally: r.close() else: @@ -709,10 +710,12 @@ class Download: progress_stdout: bool = True else: progress_stdout: bool = False - self.progress_gui.list_name.emit(list_media_name_short[:30]) + self.progress_gui.list_name.emit(list_media_name_short) + + progress: Progress = self.progress_overall if self.progress_overall else self.progress # Create the list progress task. - p_task1: TaskID = self.progress.add_task( + p_task1: TaskID = progress.add_task( f"[green]List '{list_media_name_short}'", total=len(items), visible=progress_stdout ) @@ -720,7 +723,7 @@ class Download: result_dirs: [pathlib.Path] = [] # Iterate through list items - while not self.progress.finished: + while not progress.finished: with futures.ThreadPoolExecutor(max_workers=self.settings.data.downloads_concurrent_max) as executor: # Dispatch all download tasks to worker threads l_futures: [any] = [ @@ -744,10 +747,10 @@ class Download: result_dirs.append(result_path_file.parent) # Advance progress bar. - self.progress.advance(p_task1) + progress.advance(p_task1) if not progress_stdout: - self.progress_gui.list_item.emit(self.progress.tasks[p_task1].percentage) + self.progress_gui.list_item.emit(progress.tasks[p_task1].percentage) # Create playlist file if self.settings.data.playlist_create: