mirror of
https://github.com/pacnpal/Pac-cogs.git
synced 2025-12-20 10:51:05 -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
262 lines
9.3 KiB
Python
262 lines
9.3 KiB
Python
"""Module for managing VideoArchiver components"""
|
|
|
|
import logging
|
|
import asyncio
|
|
from typing import Dict, Any, Optional, Set, List
|
|
from enum import Enum
|
|
from datetime import datetime
|
|
|
|
logger = logging.getLogger("VideoArchiver")
|
|
|
|
class ComponentState(Enum):
|
|
"""Possible states of a component"""
|
|
UNREGISTERED = "unregistered"
|
|
REGISTERED = "registered"
|
|
INITIALIZING = "initializing"
|
|
READY = "ready"
|
|
ERROR = "error"
|
|
SHUTDOWN = "shutdown"
|
|
|
|
class ComponentDependencyError(Exception):
|
|
"""Raised when component dependencies cannot be satisfied"""
|
|
pass
|
|
|
|
class ComponentLifecycleError(Exception):
|
|
"""Raised when component lifecycle operations fail"""
|
|
pass
|
|
|
|
class Component:
|
|
"""Base class for managed components"""
|
|
|
|
def __init__(self, name: str):
|
|
self.name = name
|
|
self.state = ComponentState.UNREGISTERED
|
|
self.dependencies: Set[str] = set()
|
|
self.dependents: Set[str] = set()
|
|
self.registration_time: Optional[datetime] = None
|
|
self.initialization_time: Optional[datetime] = None
|
|
self.error: Optional[str] = None
|
|
|
|
async def initialize(self) -> None:
|
|
"""Initialize the component"""
|
|
pass
|
|
|
|
async def shutdown(self) -> None:
|
|
"""Shutdown the component"""
|
|
pass
|
|
|
|
class ComponentTracker:
|
|
"""Tracks component states and relationships"""
|
|
|
|
def __init__(self):
|
|
self.states: Dict[str, ComponentState] = {}
|
|
self.history: List[Dict[str, Any]] = []
|
|
|
|
def update_state(self, name: str, state: ComponentState, error: Optional[str] = None) -> None:
|
|
"""Update component state"""
|
|
self.states[name] = state
|
|
self.history.append({
|
|
"component": name,
|
|
"state": state.value,
|
|
"timestamp": datetime.utcnow(),
|
|
"error": error
|
|
})
|
|
|
|
def get_component_history(self, name: str) -> List[Dict[str, Any]]:
|
|
"""Get state history for a component"""
|
|
return [
|
|
entry for entry in self.history
|
|
if entry["component"] == name
|
|
]
|
|
|
|
class DependencyManager:
|
|
"""Manages component dependencies"""
|
|
|
|
def __init__(self):
|
|
self.dependencies: Dict[str, Set[str]] = {}
|
|
self.dependents: Dict[str, Set[str]] = {}
|
|
|
|
def add_dependency(self, component: str, dependency: str) -> None:
|
|
"""Add a dependency relationship"""
|
|
if component not in self.dependencies:
|
|
self.dependencies[component] = set()
|
|
self.dependencies[component].add(dependency)
|
|
|
|
if dependency not in self.dependents:
|
|
self.dependents[dependency] = set()
|
|
self.dependents[dependency].add(component)
|
|
|
|
def get_dependencies(self, component: str) -> Set[str]:
|
|
"""Get dependencies for a component"""
|
|
return self.dependencies.get(component, set())
|
|
|
|
def get_dependents(self, component: str) -> Set[str]:
|
|
"""Get components that depend on this component"""
|
|
return self.dependents.get(component, set())
|
|
|
|
def get_initialization_order(self) -> List[str]:
|
|
"""Get components in dependency order"""
|
|
visited = set()
|
|
order = []
|
|
|
|
def visit(component: str) -> None:
|
|
if component in visited:
|
|
return
|
|
visited.add(component)
|
|
for dep in self.dependencies.get(component, set()):
|
|
visit(dep)
|
|
order.append(component)
|
|
|
|
for component in self.dependencies:
|
|
visit(component)
|
|
|
|
return order
|
|
|
|
class ComponentManager:
|
|
"""Manages VideoArchiver components"""
|
|
|
|
def __init__(self, cog):
|
|
self.cog = cog
|
|
self._components: Dict[str, Component] = {}
|
|
self.tracker = ComponentTracker()
|
|
self.dependency_manager = DependencyManager()
|
|
|
|
def register(
|
|
self,
|
|
name: str,
|
|
component: Any,
|
|
dependencies: Optional[Set[str]] = None
|
|
) -> None:
|
|
"""Register a component with dependencies"""
|
|
try:
|
|
# Wrap non-Component objects
|
|
if not isinstance(component, Component):
|
|
component = Component(name)
|
|
|
|
# Register dependencies
|
|
if dependencies:
|
|
for dep in dependencies:
|
|
if dep not in self._components:
|
|
raise ComponentDependencyError(
|
|
f"Dependency {dep} not registered for {name}"
|
|
)
|
|
self.dependency_manager.add_dependency(name, dep)
|
|
|
|
# Register component
|
|
self._components[name] = component
|
|
component.registration_time = datetime.utcnow()
|
|
self.tracker.update_state(name, ComponentState.REGISTERED)
|
|
logger.debug(f"Registered component: {name}")
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error registering component {name}: {e}")
|
|
self.tracker.update_state(name, ComponentState.ERROR, str(e))
|
|
raise ComponentLifecycleError(f"Failed to register component: {str(e)}")
|
|
|
|
async def initialize_components(self) -> None:
|
|
"""Initialize all components in dependency order"""
|
|
try:
|
|
# Get initialization order
|
|
init_order = self.dependency_manager.get_initialization_order()
|
|
|
|
# Initialize core components first
|
|
await self._initialize_core_components()
|
|
|
|
# Initialize remaining components
|
|
for name in init_order:
|
|
if name not in self._components:
|
|
continue
|
|
|
|
component = self._components[name]
|
|
try:
|
|
self.tracker.update_state(name, ComponentState.INITIALIZING)
|
|
await component.initialize()
|
|
component.initialization_time = datetime.utcnow()
|
|
self.tracker.update_state(name, ComponentState.READY)
|
|
except Exception as e:
|
|
logger.error(f"Error initializing component {name}: {e}")
|
|
self.tracker.update_state(name, ComponentState.ERROR, str(e))
|
|
raise ComponentLifecycleError(
|
|
f"Failed to initialize component {name}: {str(e)}"
|
|
)
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error during component initialization: {e}")
|
|
raise ComponentLifecycleError(f"Component initialization failed: {str(e)}")
|
|
|
|
async def _initialize_core_components(self) -> None:
|
|
"""Initialize core system components"""
|
|
from ..config_manager import ConfigManager
|
|
from ..processor.core import Processor
|
|
from ..queue.manager import QueueManager
|
|
from ..ffmpeg.ffmpeg_manager import FFmpegManager
|
|
|
|
core_components = {
|
|
"config_manager": (ConfigManager(self.cog), set()),
|
|
"processor": (Processor(self.cog), {"config_manager"}),
|
|
"queue_manager": (QueueManager(self.cog), {"config_manager"}),
|
|
"ffmpeg_mgr": (FFmpegManager(self.cog), set())
|
|
}
|
|
|
|
for name, (component, deps) in core_components.items():
|
|
self.register(name, component, deps)
|
|
|
|
# Initialize paths
|
|
await self._initialize_paths()
|
|
|
|
async def _initialize_paths(self) -> None:
|
|
"""Initialize required paths"""
|
|
from pathlib import Path
|
|
from ..utils.path_manager import ensure_directory
|
|
|
|
data_dir = Path(self.cog.bot.data_path) / "VideoArchiver"
|
|
download_dir = data_dir / "downloads"
|
|
|
|
# Ensure directories exist
|
|
await ensure_directory(data_dir)
|
|
await ensure_directory(download_dir)
|
|
|
|
# Register paths
|
|
self.register("data_path", data_dir)
|
|
self.register("download_path", download_dir)
|
|
|
|
def get(self, name: str) -> Optional[Any]:
|
|
"""Get a registered component"""
|
|
component = self._components.get(name)
|
|
return component if isinstance(component, Component) else None
|
|
|
|
async def shutdown_components(self) -> None:
|
|
"""Shutdown components in reverse dependency order"""
|
|
shutdown_order = reversed(self.dependency_manager.get_initialization_order())
|
|
|
|
for name in shutdown_order:
|
|
if name not in self._components:
|
|
continue
|
|
|
|
component = self._components[name]
|
|
try:
|
|
await component.shutdown()
|
|
self.tracker.update_state(name, ComponentState.SHUTDOWN)
|
|
except Exception as e:
|
|
logger.error(f"Error shutting down component {name}: {e}")
|
|
self.tracker.update_state(name, ComponentState.ERROR, str(e))
|
|
|
|
def clear(self) -> None:
|
|
"""Clear all registered components"""
|
|
self._components.clear()
|
|
logger.debug("Cleared all components")
|
|
|
|
def get_component_status(self) -> Dict[str, Any]:
|
|
"""Get status of all components"""
|
|
return {
|
|
name: {
|
|
"state": self.tracker.states.get(name, ComponentState.UNREGISTERED).value,
|
|
"registration_time": component.registration_time,
|
|
"initialization_time": component.initialization_time,
|
|
"dependencies": self.dependency_manager.get_dependencies(name),
|
|
"dependents": self.dependency_manager.get_dependents(name),
|
|
"error": component.error
|
|
}
|
|
for name, component in self._components.items()
|
|
}
|