AST
This commit is contained in:
143
helper/progress.py
Normal file
143
helper/progress.py
Normal file
@@ -0,0 +1,143 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Text-based progress bar utilities for consistent display across all downloads."""
|
||||
|
||||
import sys
|
||||
|
||||
from helper.logger import log, debug
|
||||
|
||||
|
||||
def format_progress_bar(current: int, total: int, width: int = 40, label: str = "") -> str:
|
||||
"""Create a text-based progress bar.
|
||||
|
||||
Args:
|
||||
current: Current progress (bytes/items)
|
||||
total: Total to complete (bytes/items)
|
||||
width: Width of the bar in characters (default 40)
|
||||
label: Optional label prefix
|
||||
|
||||
Returns:
|
||||
Formatted progress bar string
|
||||
|
||||
Examples:
|
||||
format_progress_bar(50, 100)
|
||||
# Returns: "[████████████████░░░░░░░░░░░░░░░░░░░░] 50.0%"
|
||||
|
||||
format_progress_bar(256*1024*1024, 1024*1024*1024, label="download.zip")
|
||||
# Returns: "download.zip: [████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] 25.0%"
|
||||
"""
|
||||
if total <= 0:
|
||||
percentage = 0
|
||||
filled = 0
|
||||
else:
|
||||
percentage = (current / total) * 100
|
||||
filled = int((current / total) * width)
|
||||
|
||||
# Create bar: filled blocks + empty blocks
|
||||
bar = "█" * filled + "░" * (width - filled)
|
||||
|
||||
# Format percentage
|
||||
pct_str = f"{percentage:.1f}%"
|
||||
|
||||
# Build result
|
||||
if label:
|
||||
result = f"{label}: [{bar}] {pct_str}"
|
||||
else:
|
||||
result = f"[{bar}] {pct_str}"
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def format_size(bytes_val: float) -> str:
|
||||
"""Format bytes to human-readable size.
|
||||
|
||||
Examples:
|
||||
format_size(1024) -> "1.00 KB"
|
||||
format_size(1024*1024) -> "1.00 MB"
|
||||
format_size(1024*1024*1024) -> "1.00 GB"
|
||||
"""
|
||||
for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
|
||||
if bytes_val < 1024:
|
||||
return f"{bytes_val:.2f} {unit}"
|
||||
bytes_val /= 1024
|
||||
return f"{bytes_val:.2f} PB"
|
||||
|
||||
|
||||
def format_download_status(filename: str, current: int, total: int, speed: float = 0) -> str:
|
||||
"""Format download status with progress bar and details.
|
||||
|
||||
Args:
|
||||
filename: Name of file being downloaded
|
||||
current: Current bytes downloaded
|
||||
total: Total file size
|
||||
speed: Download speed in bytes/sec
|
||||
|
||||
Returns:
|
||||
Formatted status line
|
||||
|
||||
Examples:
|
||||
format_download_status("movie.mkv", 512*1024*1024, 2*1024*1024*1024, 10*1024*1024)
|
||||
# Returns: "movie.mkv: [████████████░░░░░░░░░░░░░░░░░░░░░░░░░░] 25.0% (512.00 MB / 2.00 GB @ 10.00 MB/s)"
|
||||
"""
|
||||
bar = format_progress_bar(current, total, width=30)
|
||||
size_current = format_size(current)
|
||||
size_total = format_size(total)
|
||||
|
||||
if speed > 0:
|
||||
speed_str = f" @ {format_size(speed)}/s"
|
||||
else:
|
||||
speed_str = ""
|
||||
|
||||
return f"{bar} ({size_current} / {size_total}{speed_str})"
|
||||
|
||||
|
||||
def print_progress(filename: str, current: int, total: int, speed: float = 0, end: str = "\r") -> None:
|
||||
"""Print download progress to stderr (doesn't interfere with piped output).
|
||||
|
||||
Args:
|
||||
filename: File being downloaded
|
||||
current: Current bytes
|
||||
total: Total bytes
|
||||
speed: Speed in bytes/sec
|
||||
end: Line ending (default "\r" for overwriting, use "\n" for final)
|
||||
"""
|
||||
status = format_download_status(filename, current, total, speed)
|
||||
debug(status, end=end, flush=True)
|
||||
|
||||
|
||||
def print_final_progress(filename: str, total: int, elapsed: float) -> None:
|
||||
"""Print final progress line (100%) with time elapsed.
|
||||
|
||||
Args:
|
||||
filename: File that was downloaded
|
||||
total: Total size
|
||||
elapsed: Time elapsed in seconds
|
||||
"""
|
||||
bar = format_progress_bar(total, total, width=30)
|
||||
size_str = format_size(total)
|
||||
|
||||
# Format elapsed time
|
||||
if elapsed < 60:
|
||||
time_str = f"{elapsed:.1f}s"
|
||||
elif elapsed < 3600:
|
||||
minutes = elapsed / 60
|
||||
time_str = f"{minutes:.1f}m"
|
||||
else:
|
||||
hours = elapsed / 3600
|
||||
time_str = f"{hours:.2f}h"
|
||||
|
||||
debug(f"{bar} ({size_str}) - {time_str}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Demo
|
||||
import time
|
||||
|
||||
log("Progress Bar Demo:", file=sys.stderr)
|
||||
|
||||
# Demo 1: Simple progress
|
||||
for i in range(101):
|
||||
print_progress("demo.bin", i * 10 * 1024 * 1024, 1024 * 1024 * 1024)
|
||||
time.sleep(0.02)
|
||||
|
||||
print_final_progress("demo.bin", 1024 * 1024 * 1024, 2.0)
|
||||
log()
|
||||
Reference in New Issue
Block a user