Files
Pac-cogs/videoarchiver/core/component_manager.py
pacnpal 08d5dc56cf Fixed role_manager.py typing issues by adding missing imports (Optional, Any)
Added process_queue method to EnhancedVideoQueueManager and updated its initialization
Updated component_manager.py to use EnhancedVideoQueueManager correctly
Fixed circular imports in the core module by:
Moving initialization logic to lifecycle.py
Making initialization.py provide thin wrappers that delegate to lifecycle.py
Ensuring proper import order in base.py
Verified all module init.py files are properly exposing their components:
core/init.py exposes VideoArchiver
queue/init.py exposes EnhancedVideoQueueManager and dependencies
processor/init.py exposes VideoProcessor and related components
commands/init.py exposes command setup functions
The import chain is now clean:

base.py imports from lifecycle.py
lifecycle.py contains all initialization logic
initialization.py delegates to lifecycle.py
No circular dependencies
All components are properly exposed through their respective init.py files
2024-11-16 17:13:11 +00:00

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 EnhancedVideoQueueManager
from ..ffmpeg.ffmpeg_manager import FFmpegManager
core_components = {
"config_manager": (ConfigManager(self.cog), set()),
"processor": (Processor(self.cog), {"config_manager"}),
"queue_manager": (EnhancedVideoQueueManager(), {"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()
}