mirror of
https://github.com/pacnpal/Pac-cogs.git
synced 2025-12-20 02:41:06 -05:00
base.py: Streamlined to core cog functionality and lifecycle management initialization.py: Centralized initialization logic error_handler.py: Unified error handling response_handler.py: Consistent response handling Command Organization: commands/archiver_commands.py: Core archiver functionality commands/database_commands.py: Database operations commands/settings_commands.py: Settings management All commands properly integrated with the cog using setup functions Improved Architecture: Consistent command registration pattern Better separation of concerns Maintained all original functionality Enhanced error handling and response management Proper command integration with the cog structure
158 lines
5.7 KiB
Python
158 lines
5.7 KiB
Python
"""Base module containing core VideoArchiver class"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import logging
|
|
from pathlib import Path
|
|
from redbot.core.bot import Red
|
|
from redbot.core.commands import GroupCog
|
|
|
|
from .initialization import initialize_cog, init_callback
|
|
from .error_handler import handle_command_error
|
|
from .cleanup import cleanup_resources, force_cleanup_resources
|
|
from .commands import setup_archiver_commands, setup_database_commands, setup_settings_commands
|
|
from ..utils.exceptions import VideoArchiverError as ProcessingError
|
|
|
|
logger = logging.getLogger("VideoArchiver")
|
|
|
|
# Constants for timeouts
|
|
UNLOAD_TIMEOUT = 30 # seconds
|
|
CLEANUP_TIMEOUT = 15 # seconds
|
|
|
|
class VideoArchiver(GroupCog):
|
|
"""Archive videos from Discord channels"""
|
|
|
|
default_guild_settings = {
|
|
"enabled": False,
|
|
"archive_channel": None,
|
|
"log_channel": None,
|
|
"enabled_channels": [], # Empty list means all channels
|
|
"allowed_roles": [], # Empty list means all roles
|
|
"video_format": "mp4",
|
|
"video_quality": "high",
|
|
"max_file_size": 8, # MB
|
|
"message_duration": 30, # seconds
|
|
"message_template": "{author} archived a video from {channel}",
|
|
"concurrent_downloads": 2,
|
|
"enabled_sites": None, # None means all sites
|
|
"use_database": False, # Database tracking is off by default
|
|
}
|
|
|
|
def __init__(self, bot: Red) -> None:
|
|
"""Initialize the cog with minimal setup"""
|
|
super().__init__()
|
|
self.bot = bot
|
|
self.ready = asyncio.Event()
|
|
self._init_task = None
|
|
self._cleanup_task = None
|
|
self._queue_task = None
|
|
self._unloading = False
|
|
self.db = None
|
|
self.queue_manager = None
|
|
self.processor = None
|
|
self.components = {}
|
|
self.config_manager = None
|
|
self.update_checker = None
|
|
self.ffmpeg_mgr = None
|
|
self.data_path = None
|
|
self.download_path = None
|
|
|
|
# Set up commands
|
|
setup_archiver_commands(self)
|
|
setup_database_commands(self)
|
|
setup_settings_commands(self)
|
|
|
|
# Set up events - non-blocking
|
|
from .events import setup_events
|
|
setup_events(self)
|
|
|
|
async def cog_load(self) -> None:
|
|
"""Handle cog loading without blocking"""
|
|
try:
|
|
# Start initialization as background task without waiting
|
|
self._init_task = asyncio.create_task(initialize_cog(self))
|
|
self._init_task.add_done_callback(lambda t: init_callback(self, t))
|
|
logger.info("Initialization started in background")
|
|
except Exception as e:
|
|
# Ensure cleanup on any error
|
|
try:
|
|
await asyncio.wait_for(
|
|
force_cleanup_resources(self), timeout=CLEANUP_TIMEOUT
|
|
)
|
|
except asyncio.TimeoutError:
|
|
logger.error("Force cleanup during load error timed out")
|
|
raise ProcessingError(f"Error during cog load: {str(e)}")
|
|
|
|
async def cog_unload(self) -> None:
|
|
"""Clean up when cog is unloaded with proper timeout handling"""
|
|
self._unloading = True
|
|
try:
|
|
# Cancel any pending tasks
|
|
if self._init_task and not self._init_task.done():
|
|
self._init_task.cancel()
|
|
|
|
if self._cleanup_task and not self._cleanup_task.done():
|
|
self._cleanup_task.cancel()
|
|
|
|
# Cancel queue processing task if it exists
|
|
if (
|
|
hasattr(self, "_queue_task")
|
|
and self._queue_task
|
|
and not self._queue_task.done()
|
|
):
|
|
self._queue_task.cancel()
|
|
try:
|
|
await self._queue_task
|
|
except asyncio.CancelledError:
|
|
pass
|
|
except Exception as e:
|
|
logger.error(f"Error cancelling queue task: {e}")
|
|
|
|
# Try normal cleanup first
|
|
cleanup_task = asyncio.create_task(cleanup_resources(self))
|
|
try:
|
|
await asyncio.wait_for(cleanup_task, timeout=UNLOAD_TIMEOUT)
|
|
logger.info("Normal cleanup completed")
|
|
except (asyncio.TimeoutError, Exception) as e:
|
|
if isinstance(e, asyncio.TimeoutError):
|
|
logger.warning("Normal cleanup timed out, forcing cleanup")
|
|
else:
|
|
logger.error(f"Error during normal cleanup: {str(e)}")
|
|
|
|
# Cancel normal cleanup and force cleanup
|
|
cleanup_task.cancel()
|
|
try:
|
|
# Force cleanup with timeout
|
|
await asyncio.wait_for(
|
|
force_cleanup_resources(self), timeout=CLEANUP_TIMEOUT
|
|
)
|
|
logger.info("Force cleanup completed")
|
|
except asyncio.TimeoutError:
|
|
logger.error("Force cleanup timed out")
|
|
except Exception as e:
|
|
logger.error(f"Error during force cleanup: {str(e)}")
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error during cog unload: {str(e)}")
|
|
finally:
|
|
self._unloading = False
|
|
# Ensure ready flag is cleared
|
|
self.ready.clear()
|
|
# Clear all references
|
|
self.bot = None
|
|
self.processor = None
|
|
self.queue_manager = None
|
|
self.update_checker = None
|
|
self.ffmpeg_mgr = None
|
|
self.components.clear()
|
|
self.db = None
|
|
self._init_task = None
|
|
self._cleanup_task = None
|
|
if hasattr(self, "_queue_task"):
|
|
self._queue_task = None
|
|
|
|
async def cog_command_error(self, ctx, error):
|
|
"""Handle command errors"""
|
|
await handle_command_error(ctx, error)
|