Files
Pac-cogs/videoarchiver/utils/progress_tracker.py
pacnpal dac21f2fcd fixed
2024-11-16 22:32:08 +00:00

313 lines
11 KiB
Python

"""Progress tracking module."""
import logging
from typing import Dict, Any, Optional
from datetime import datetime
logger = logging.getLogger(__name__)
class ProgressTracker:
"""Progress tracker singleton."""
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance._initialized = False
return cls._instance
def __init__(self):
if not hasattr(self, '_initialized'):
self._data: Dict[str, Dict[str, Any]] = {}
self._initialized = True
def update(self, key: str, data: Dict[str, Any]) -> None:
"""Update progress for a key."""
if key not in self._data:
self._data[key] = {
'active': True,
'start_time': datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S"),
'percent': 0
}
self._data[key].update(data)
self._data[key]['last_update'] = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
logger.debug(f"Progress for {key}: {self._data[key].get('percent', 0)}%")
def get(self, key: Optional[str] = None) -> Dict[str, Any]:
"""Get progress for a key."""
if key is None:
return self._data
return self._data.get(key, {})
def complete(self, key: str) -> None:
"""Mark progress as complete."""
if key in self._data:
self._data[key]['active'] = False
logger.info(f"Operation completed for {key}")
def clear(self) -> None:
"""Clear all progress data."""
self._data.clear()
logger.info("Progress data cleared")
_tracker = ProgressTracker()
def get_compression(self, file_path: Optional[str] = None) -> Dict[str, Any]:
"""Get compression progress."""
if file_path is None:
return self._compressions
return self._compressions.get(file_path, {})
def complete_download(self, url: str) -> None:
"""Mark download as complete."""
if url in self._downloads:
self._downloads[url]['active'] = False
logger.info(f"Download completed for {url}")
def complete_compression(self, file_path: str) -> None:
"""Mark compression as complete."""
if file_path in self._compressions:
self._compressions[file_path]['active'] = False
logger.info(f"Compression completed for {file_path}")
def clear(self) -> None:
"""Clear all progress data."""
self._downloads.clear()
self._compressions.clear()
logger.info("Progress data cleared")
# Global instance
_tracker = ProgressTrack
# Global instance
_tracker = ProgressTracker()
def get_tracker() -> Progre
"""Clear all progress tracking"""
self._download_progress.clear()
self._compression_progress.clear()
logger.info("Cleared all progress tracking data")
# Create singleton instance
progress_tracker = ProgressTracker()
def get_progress_tracker() -> ProgressTracker:
def mark_compression_complete(self, file_path: str) -> None:
"""Mark a compression operation as complete"""
if file_path in self._compression_progress:
self._compression_progress[file_path]['active'] = False
logger.info(f"Compression completed for {file_path}")
def clear_progress(self) -> None:
"""Clear all progress tracking"""
self._download_progress.clear()
self._compression_progress.clear()
logger.info("Cleared all progress tracking data")
# Create singleton instance
progress_tracker = ProgressTracker()
# Export the singleton instance
def get_progress_tracker() -> ProgressTracker:
Args:
data: Dictionary containing download progress data
"""
try:
info_dict = data.get("info_dict", {})
url = info_dict.get("webpage_url")
if not url or url not in self._download_progress:
return
if data.get("status") == "downloading":
percent_str = data.get("_percent_str", "0").replace("%", "")
try:
percent = float(percent_str)
except ValueError:
percent = 0.0
total_bytes = (
data.get("total_bytes", 0) or
data.get("total_bytes_estimate", 0)
)
self._download_progress[url].update({
"active": True,
"percent": percent,
"speed": data.get("_speed_str", "N/A"),
"eta": data.get("_eta_str", "N/A"),
"downloaded_bytes": data.get("downloaded_bytes", 0),
"total_bytes": total_bytes,
"retries": data.get("retry_count", 0),
"fragment_count": data.get("fragment_count", 0),
"fragment_index": data.get("fragment_index", 0),
"video_title": info_dict.get("title", "Unknown"),
"extractor": info_dict.get("extractor", "Unknown"),
"format": info_dict.get("format", "Unknown"),
"resolution": info_dict.get("resolution", "Unknown"),
"fps": info_dict.get("fps", "Unknown"),
"last_update": datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
})
logger.debug(
f"Download progress for {url}: "
f"{percent:.1f}% at {self._download_progress[url]['speed']}, "
f"ETA: {self._download_progress[url]['eta']}"
)
except Exception as e:
logger.error(f"Error updating download progress: {e}", exc_info=True)
def end_download(self, url: str, status: ProgressStatus = ProgressStatus.COMPLETED) -> None:
"""
Mark a download as completed.
Args:
url: The URL being downloaded
status: The final status of the download
"""
if url in self._download_progress:
self._download_progress[url]["active"] = False
logger.info(f"Download {status.value} for {url}")
def start_compression(self, params: CompressionParams) -> None:
"""
Initialize progress tracking for compression.
Args:
params: Compression parameters
"""
current_time = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
self._compression_progress[params.input_file] = CompressionProgress(
active=True,
filename=params.input_file,
start_time=current_time,
percent=0.0,
elapsed_time="0:00",
input_size=params.input_size,
current_size=0,
target_size=params.target_size,
codec=params.codec_params.get("c:v", "unknown"),
hardware_accel=params.use_hardware,
preset=params.codec_params.get("preset", "unknown"),
crf=params.codec_params.get("crf", "unknown"),
duration=params.duration,
bitrate=params.codec_params.get("b:v", "unknown"),
audio_codec=params.codec_params.get("c:a", "unknown"),
audio_bitrate=params.codec_params.get("b:a", "unknown"),
last_update=current_time,
current_time=None
)
def update_compression_progress(
self,
input_file: str,
progress: float,
elapsed_time: str,
current_size: int,
current_time: float
) -> None:
"""
Update compression progress information.
Args:
input_file: The input file being compressed
progress: Current progress percentage (0-100)
elapsed_time: Time elapsed as string
current_size: Current file size in bytes
current_time: Current timestamp in seconds
"""
if input_file in self._compression_progress:
self._compression_progress[input_file].update({
"percent": max(0.0, min(100.0, progress)),
"elapsed_time": elapsed_time,
"current_size": current_size,
"current_time": current_time,
"last_update": datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
})
logger.debug(
f"Compression progress for {input_file}: "
f"{progress:.1f}%, Size: {current_size}/{self._compression_progress[input_file]['target_size']} bytes"
)
def end_compression(
self,
input_file: str,
status: ProgressStatus = ProgressStatus.COMPLETED
) -> None:
"""
Mark a compression operation as completed.
Args:
input_file: The input file being compressed
status: The final status of the compression
"""
if input_file in self._compression_progress:
self._compression_progress[input_file]["active"] = False
logger.info(f"Compression {status.value} for {input_file}")
def get_download_progress(self, url: Optional[str] = None) -> Optional[DownloadProgress]:
"""
Get progress information for a download.
Args:
url: Optional URL to get progress for. If None, returns all progress.
Returns:
Progress information for the specified download or None if not found
"""
if url is None:
return self._download_progress
return self._download_progress.get(url)
def get_compression_progress(
self,
input_file: Optional[str] = None
) -> Optional[CompressionProgress]:
"""
Get progress information for a compression operation.
Args:
input_file: Optional file to get progress for. If None, returns all progress.
Returns:
Progress information for the specified compression or None if not found
"""
if input_file is None:
return self._compression_progress
return self._compression_progress.get(input_file)
def get_active_downloads(self) -> Dict[str, DownloadProgress]:
"""
Get all active downloads.
Returns:
Dictionary of active downloads and their progress
"""
return {
url: progress
for url, progress in self._download_progress.items()
if progress.get("active", False)
}
def get_active_compressions(self) -> Dict[str, CompressionProgress]:
"""
Get all active compression operations.
Returns:
Dictionary of active compressions and their progress
"""
return {
input_file: progress
for input_file, progress in self._compression_progress.items()
if progress.get("active", False)
}
def clear_progress(self) -> None:
"""Clear all progress tracking"""
self._download_progress.clear()
self._compression_progress.clear()
logger.info("Cleared