mirror of
https://github.com/pacnpal/Pac-cogs.git
synced 2025-12-20 02:41:06 -05:00
Component-based architecture with lifecycle management Enhanced error handling and recovery mechanisms Comprehensive state management and tracking Event-driven architecture with monitoring Queue Management: Multiple processing strategies for different scenarios Advanced state management with recovery Comprehensive metrics and health monitoring Sophisticated cleanup system with multiple strategies Processing Pipeline: Enhanced message handling with validation Improved URL extraction and processing Better queue management and monitoring Advanced cleanup mechanisms Overall Benefits: Better code organization and maintainability Improved error handling and recovery Enhanced monitoring and reporting More robust and reliable system
317 lines
11 KiB
Python
317 lines
11 KiB
Python
"""Module for handling queue status display and formatting"""
|
|
|
|
import discord
|
|
from enum import Enum
|
|
from dataclasses import dataclass
|
|
from datetime import datetime
|
|
from typing import Dict, Any, List, Optional
|
|
import logging
|
|
|
|
logger = logging.getLogger("VideoArchiver")
|
|
|
|
class DisplayTheme:
|
|
"""Defines display themes"""
|
|
DEFAULT = {
|
|
"title_color": discord.Color.blue(),
|
|
"success_color": discord.Color.green(),
|
|
"warning_color": discord.Color.gold(),
|
|
"error_color": discord.Color.red(),
|
|
"info_color": discord.Color.blurple()
|
|
}
|
|
|
|
@dataclass
|
|
class DisplayTemplate:
|
|
"""Template for status display sections"""
|
|
name: str
|
|
format_string: str
|
|
inline: bool = False
|
|
order: int = 0
|
|
condition: Optional[str] = None
|
|
|
|
class DisplaySection(Enum):
|
|
"""Available display sections"""
|
|
QUEUE_STATS = "queue_stats"
|
|
DOWNLOADS = "downloads"
|
|
COMPRESSIONS = "compressions"
|
|
ERRORS = "errors"
|
|
HARDWARE = "hardware"
|
|
|
|
class StatusFormatter:
|
|
"""Formats status information for display"""
|
|
|
|
@staticmethod
|
|
def format_bytes(bytes: int) -> str:
|
|
"""Format bytes into human readable format"""
|
|
for unit in ['B', 'KB', 'MB', 'GB']:
|
|
if bytes < 1024:
|
|
return f"{bytes:.1f}{unit}"
|
|
bytes /= 1024
|
|
return f"{bytes:.1f}TB"
|
|
|
|
@staticmethod
|
|
def format_time(seconds: float) -> str:
|
|
"""Format time duration"""
|
|
if seconds < 60:
|
|
return f"{seconds:.1f}s"
|
|
minutes = seconds / 60
|
|
if minutes < 60:
|
|
return f"{minutes:.1f}m"
|
|
hours = minutes / 60
|
|
return f"{hours:.1f}h"
|
|
|
|
@staticmethod
|
|
def format_percentage(value: float) -> str:
|
|
"""Format percentage value"""
|
|
return f"{value:.1f}%"
|
|
|
|
class DisplayManager:
|
|
"""Manages status display configuration"""
|
|
|
|
def __init__(self):
|
|
self.templates: Dict[DisplaySection, DisplayTemplate] = {
|
|
DisplaySection.QUEUE_STATS: DisplayTemplate(
|
|
name="Queue Statistics",
|
|
format_string=(
|
|
"```\n"
|
|
"Pending: {pending}\n"
|
|
"Processing: {processing}\n"
|
|
"Completed: {completed}\n"
|
|
"Failed: {failed}\n"
|
|
"Success Rate: {success_rate}\n"
|
|
"Avg Processing Time: {avg_processing_time}\n"
|
|
"```"
|
|
),
|
|
order=1
|
|
),
|
|
DisplaySection.DOWNLOADS: DisplayTemplate(
|
|
name="Active Downloads",
|
|
format_string=(
|
|
"```\n"
|
|
"URL: {url}\n"
|
|
"Progress: {percent}\n"
|
|
"Speed: {speed}\n"
|
|
"ETA: {eta}\n"
|
|
"Size: {size}\n"
|
|
"Started: {start_time}\n"
|
|
"Retries: {retries}\n"
|
|
"```"
|
|
),
|
|
order=2
|
|
),
|
|
DisplaySection.COMPRESSIONS: DisplayTemplate(
|
|
name="Active Compressions",
|
|
format_string=(
|
|
"```\n"
|
|
"File: {filename}\n"
|
|
"Progress: {percent}\n"
|
|
"Time Elapsed: {elapsed_time}\n"
|
|
"Input Size: {input_size}\n"
|
|
"Current Size: {current_size}\n"
|
|
"Target Size: {target_size}\n"
|
|
"Codec: {codec}\n"
|
|
"Hardware Accel: {hardware_accel}\n"
|
|
"```"
|
|
),
|
|
order=3
|
|
),
|
|
DisplaySection.ERRORS: DisplayTemplate(
|
|
name="Error Statistics",
|
|
format_string="```\n{error_stats}```",
|
|
condition="has_errors",
|
|
order=4
|
|
),
|
|
DisplaySection.HARDWARE: DisplayTemplate(
|
|
name="Hardware Statistics",
|
|
format_string=(
|
|
"```\n"
|
|
"Hardware Accel Failures: {hw_failures}\n"
|
|
"Compression Failures: {comp_failures}\n"
|
|
"Peak Memory Usage: {memory_usage}\n"
|
|
"```"
|
|
),
|
|
order=5
|
|
)
|
|
}
|
|
self.theme = DisplayTheme.DEFAULT
|
|
|
|
class StatusDisplay:
|
|
"""Handles formatting and display of queue status information"""
|
|
|
|
def __init__(self):
|
|
self.display_manager = DisplayManager()
|
|
self.formatter = StatusFormatter()
|
|
|
|
async def create_queue_status_embed(
|
|
self,
|
|
queue_status: Dict[str, Any],
|
|
active_ops: Dict[str, Any]
|
|
) -> discord.Embed:
|
|
"""Create an embed displaying queue status and active operations"""
|
|
embed = discord.Embed(
|
|
title="Queue Status Details",
|
|
color=self.display_manager.theme["title_color"],
|
|
timestamp=datetime.utcnow()
|
|
)
|
|
|
|
# Add sections in order
|
|
sections = sorted(
|
|
self.display_manager.templates.items(),
|
|
key=lambda x: x[1].order
|
|
)
|
|
|
|
for section, template in sections:
|
|
# Check condition if exists
|
|
if template.condition:
|
|
if not self._check_condition(template.condition, queue_status, active_ops):
|
|
continue
|
|
|
|
# Add section based on type
|
|
if section == DisplaySection.QUEUE_STATS:
|
|
self._add_queue_statistics(embed, queue_status, template)
|
|
elif section == DisplaySection.DOWNLOADS:
|
|
self._add_active_downloads(embed, active_ops.get('downloads', {}), template)
|
|
elif section == DisplaySection.COMPRESSIONS:
|
|
self._add_active_compressions(embed, active_ops.get('compressions', {}), template)
|
|
elif section == DisplaySection.ERRORS:
|
|
self._add_error_statistics(embed, queue_status, template)
|
|
elif section == DisplaySection.HARDWARE:
|
|
self._add_hardware_statistics(embed, queue_status, template)
|
|
|
|
return embed
|
|
|
|
def _check_condition(
|
|
self,
|
|
condition: str,
|
|
queue_status: Dict[str, Any],
|
|
active_ops: Dict[str, Any]
|
|
) -> bool:
|
|
"""Check if condition for displaying section is met"""
|
|
if condition == "has_errors":
|
|
return bool(queue_status["metrics"]["errors_by_type"])
|
|
return True
|
|
|
|
def _add_queue_statistics(
|
|
self,
|
|
embed: discord.Embed,
|
|
queue_status: Dict[str, Any],
|
|
template: DisplayTemplate
|
|
) -> None:
|
|
"""Add queue statistics to the embed"""
|
|
embed.add_field(
|
|
name=template.name,
|
|
value=template.format_string.format(
|
|
pending=queue_status['pending'],
|
|
processing=queue_status['processing'],
|
|
completed=queue_status['completed'],
|
|
failed=queue_status['failed'],
|
|
success_rate=self.formatter.format_percentage(
|
|
queue_status['metrics']['success_rate'] * 100
|
|
),
|
|
avg_processing_time=self.formatter.format_time(
|
|
queue_status['metrics']['avg_processing_time']
|
|
)
|
|
),
|
|
inline=template.inline
|
|
)
|
|
|
|
def _add_active_downloads(
|
|
self,
|
|
embed: discord.Embed,
|
|
downloads: Dict[str, Any],
|
|
template: DisplayTemplate
|
|
) -> None:
|
|
"""Add active downloads information to the embed"""
|
|
if downloads:
|
|
content = []
|
|
for url, progress in downloads.items():
|
|
content.append(template.format_string.format(
|
|
url=url[:50] + "..." if len(url) > 50 else url,
|
|
percent=self.formatter.format_percentage(progress.get('percent', 0)),
|
|
speed=progress.get('speed', 'N/A'),
|
|
eta=progress.get('eta', 'N/A'),
|
|
size=f"{self.formatter.format_bytes(progress.get('downloaded_bytes', 0))}/"
|
|
f"{self.formatter.format_bytes(progress.get('total_bytes', 0))}",
|
|
start_time=progress.get('start_time', 'N/A'),
|
|
retries=progress.get('retries', 0)
|
|
))
|
|
embed.add_field(
|
|
name=template.name,
|
|
value="".join(content),
|
|
inline=template.inline
|
|
)
|
|
else:
|
|
embed.add_field(
|
|
name=template.name,
|
|
value="```\nNo active downloads```",
|
|
inline=template.inline
|
|
)
|
|
|
|
def _add_active_compressions(
|
|
self,
|
|
embed: discord.Embed,
|
|
compressions: Dict[str, Any],
|
|
template: DisplayTemplate
|
|
) -> None:
|
|
"""Add active compressions information to the embed"""
|
|
if compressions:
|
|
content = []
|
|
for file_id, progress in compressions.items():
|
|
content.append(template.format_string.format(
|
|
filename=progress.get('filename', 'Unknown'),
|
|
percent=self.formatter.format_percentage(progress.get('percent', 0)),
|
|
elapsed_time=progress.get('elapsed_time', 'N/A'),
|
|
input_size=self.formatter.format_bytes(progress.get('input_size', 0)),
|
|
current_size=self.formatter.format_bytes(progress.get('current_size', 0)),
|
|
target_size=self.formatter.format_bytes(progress.get('target_size', 0)),
|
|
codec=progress.get('codec', 'Unknown'),
|
|
hardware_accel=progress.get('hardware_accel', False)
|
|
))
|
|
embed.add_field(
|
|
name=template.name,
|
|
value="".join(content),
|
|
inline=template.inline
|
|
)
|
|
else:
|
|
embed.add_field(
|
|
name=template.name,
|
|
value="```\nNo active compressions```",
|
|
inline=template.inline
|
|
)
|
|
|
|
def _add_error_statistics(
|
|
self,
|
|
embed: discord.Embed,
|
|
queue_status: Dict[str, Any],
|
|
template: DisplayTemplate
|
|
) -> None:
|
|
"""Add error statistics to the embed"""
|
|
if queue_status["metrics"]["errors_by_type"]:
|
|
error_stats = "\n".join(
|
|
f"{error_type}: {count}"
|
|
for error_type, count in queue_status["metrics"]["errors_by_type"].items()
|
|
)
|
|
embed.add_field(
|
|
name=template.name,
|
|
value=template.format_string.format(error_stats=error_stats),
|
|
inline=template.inline
|
|
)
|
|
|
|
def _add_hardware_statistics(
|
|
self,
|
|
embed: discord.Embed,
|
|
queue_status: Dict[str, Any],
|
|
template: DisplayTemplate
|
|
) -> None:
|
|
"""Add hardware statistics to the embed"""
|
|
embed.add_field(
|
|
name=template.name,
|
|
value=template.format_string.format(
|
|
hw_failures=queue_status['metrics']['hardware_accel_failures'],
|
|
comp_failures=queue_status['metrics']['compression_failures'],
|
|
memory_usage=self.formatter.format_bytes(
|
|
queue_status['metrics']['peak_memory_usage'] * 1024 * 1024 # Convert MB to bytes
|
|
)
|
|
),
|
|
inline=template.inline
|
|
)
|