mirror of
https://github.com/pacnpal/Pac-cogs.git
synced 2025-12-20 10:51:05 -05:00
diabolical
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
# try:
|
||||
# Try relative imports first
|
||||
from .base import VideoArchiver
|
||||
from base import VideoArchiver
|
||||
|
||||
# except ImportError:
|
||||
# Fall back to absolute imports if relative imports fail
|
||||
|
||||
@@ -14,21 +14,21 @@ from redbot.core.commands import GroupCog, Context # type: ignore
|
||||
|
||||
# try:
|
||||
# Try relative imports first
|
||||
from .settings import Settings
|
||||
from .lifecycle import LifecycleManager, LifecycleState
|
||||
from .component_manager import ComponentManager, ComponentState
|
||||
from .error_handler import error_manager, handle_command_error
|
||||
from .response_handler import ResponseManager
|
||||
from .commands.archiver_commands import setup_archiver_commands
|
||||
from .commands.database_commands import setup_database_commands
|
||||
from .commands.settings_commands import setup_settings_commands
|
||||
from .events import setup_events, EventManager
|
||||
from ..processor.core import VideoProcessor
|
||||
from ..queue.manager import EnhancedVideoQueueManager
|
||||
from ..ffmpeg.ffmpeg_manager import FFmpegManager
|
||||
from ..database.video_archive_db import VideoArchiveDB
|
||||
from ..config_manager import ConfigManager
|
||||
from ..utils.exceptions import CogError, ErrorContext, ErrorSeverity
|
||||
from settings import Settings
|
||||
from lifecycle import LifecycleManager, LifecycleState
|
||||
from component_manager import ComponentManager, ComponentState
|
||||
from error_handler import error_manager, handle_command_error
|
||||
from response_handler import ResponseManager
|
||||
from commands.archiver_commands import setup_archiver_commands
|
||||
from commands.database_commands import setup_database_commands
|
||||
from commands.settings_commands import setup_settings_commands
|
||||
from events import setup_events, EventManager
|
||||
from processor.core import VideoProcessor
|
||||
from queue.manager import EnhancedVideoQueueManager
|
||||
from ffmpeg.ffmpeg_manager import FFmpegManager
|
||||
from database.video_archive_db import VideoArchiveDB
|
||||
from config_manager import ConfigManager
|
||||
from utils.exceptions import CogError, ErrorContext, ErrorSeverity
|
||||
|
||||
# except ImportError:
|
||||
# Fall back to absolute imports if relative imports fail
|
||||
|
||||
@@ -8,25 +8,29 @@ from enum import Enum, auto
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Dict, Any, Optional, TypedDict, ClassVar
|
||||
|
||||
#try:
|
||||
# Try relative imports first
|
||||
from ..utils.file_ops import cleanup_downloads
|
||||
from ..utils.exceptions import CleanupError, ErrorContext, ErrorSeverity
|
||||
#except ImportError:
|
||||
# Fall back to absolute imports if relative imports fail
|
||||
# from videoarchiver.utils.file_ops import cleanup_downloads
|
||||
# from videoarchiver.utils.exceptions import CleanupError, ErrorContext, ErrorSeverity
|
||||
# try:
|
||||
# Try relative imports first
|
||||
from utils.file_ops import cleanup_downloads
|
||||
from utils.exceptions import CleanupError, ErrorContext, ErrorSeverity
|
||||
|
||||
# except ImportError:
|
||||
# Fall back to absolute imports if relative imports fail
|
||||
# from videoarchiver.utils.file_ops import cleanup_downloads
|
||||
# from videoarchiver.utils.exceptions import CleanupError, ErrorContext, ErrorSeverity
|
||||
|
||||
if TYPE_CHECKING:
|
||||
#try:
|
||||
# try:
|
||||
from .base import VideoArchiver
|
||||
#except ImportError:
|
||||
# from videoarchiver.core.base import VideoArchiver
|
||||
|
||||
# except ImportError:
|
||||
# from videoarchiver.core.base import VideoArchiver
|
||||
|
||||
logger = logging.getLogger("VideoArchiver")
|
||||
|
||||
|
||||
class CleanupPhase(Enum):
|
||||
"""Cleanup phases"""
|
||||
|
||||
INITIALIZATION = auto()
|
||||
UPDATE_CHECKER = auto()
|
||||
PROCESSOR = auto()
|
||||
@@ -36,21 +40,26 @@ class CleanupPhase(Enum):
|
||||
DOWNLOADS = auto()
|
||||
REFERENCES = auto()
|
||||
|
||||
|
||||
class CleanupStatus(Enum):
|
||||
"""Cleanup status"""
|
||||
|
||||
SUCCESS = auto()
|
||||
TIMEOUT = auto()
|
||||
ERROR = auto()
|
||||
SKIPPED = auto()
|
||||
|
||||
|
||||
class CleanupResult(TypedDict):
|
||||
"""Type definition for cleanup result"""
|
||||
|
||||
phase: CleanupPhase
|
||||
status: CleanupStatus
|
||||
error: Optional[str]
|
||||
duration: float
|
||||
timestamp: str
|
||||
|
||||
|
||||
class CleanupManager:
|
||||
"""Manages cleanup operations"""
|
||||
|
||||
@@ -65,7 +74,7 @@ class CleanupManager:
|
||||
phase: CleanupPhase,
|
||||
status: CleanupStatus,
|
||||
error: Optional[str] = None,
|
||||
duration: float = 0.0
|
||||
duration: float = 0.0,
|
||||
) -> None:
|
||||
"""Record result of a cleanup phase"""
|
||||
self.results[phase] = CleanupResult(
|
||||
@@ -73,20 +82,21 @@ class CleanupManager:
|
||||
status=status,
|
||||
error=error,
|
||||
duration=duration,
|
||||
timestamp=datetime.utcnow().isoformat()
|
||||
timestamp=datetime.utcnow().isoformat(),
|
||||
)
|
||||
|
||||
def get_results(self) -> Dict[CleanupPhase, CleanupResult]:
|
||||
"""Get cleanup results"""
|
||||
return self.results.copy()
|
||||
|
||||
|
||||
async def cleanup_resources(cog: "VideoArchiver") -> None:
|
||||
"""
|
||||
Clean up all resources with proper handling.
|
||||
|
||||
|
||||
Args:
|
||||
cog: VideoArchiver cog instance
|
||||
|
||||
|
||||
Raises:
|
||||
CleanupError: If cleanup fails
|
||||
"""
|
||||
@@ -102,11 +112,13 @@ async def cleanup_resources(cog: "VideoArchiver") -> None:
|
||||
try:
|
||||
logger.info("Cancelling initialization task")
|
||||
cog._init_task.cancel()
|
||||
await asyncio.wait_for(cog._init_task, timeout=cleanup_manager.CLEANUP_TIMEOUT)
|
||||
await asyncio.wait_for(
|
||||
cog._init_task, timeout=cleanup_manager.CLEANUP_TIMEOUT
|
||||
)
|
||||
cleanup_manager.record_result(
|
||||
CleanupPhase.INITIALIZATION,
|
||||
CleanupStatus.SUCCESS,
|
||||
duration=(datetime.utcnow() - phase_start).total_seconds()
|
||||
duration=(datetime.utcnow() - phase_start).total_seconds(),
|
||||
)
|
||||
except (asyncio.TimeoutError, asyncio.CancelledError) as e:
|
||||
logger.warning("Initialization task cancellation timed out")
|
||||
@@ -114,7 +126,7 @@ async def cleanup_resources(cog: "VideoArchiver") -> None:
|
||||
CleanupPhase.INITIALIZATION,
|
||||
CleanupStatus.TIMEOUT,
|
||||
str(e),
|
||||
(datetime.utcnow() - phase_start).total_seconds()
|
||||
(datetime.utcnow() - phase_start).total_seconds(),
|
||||
)
|
||||
|
||||
# Stop update checker
|
||||
@@ -123,13 +135,12 @@ async def cleanup_resources(cog: "VideoArchiver") -> None:
|
||||
try:
|
||||
logger.info("Stopping update checker")
|
||||
await asyncio.wait_for(
|
||||
cog.update_checker.stop(),
|
||||
timeout=cleanup_manager.CLEANUP_TIMEOUT
|
||||
cog.update_checker.stop(), timeout=cleanup_manager.CLEANUP_TIMEOUT
|
||||
)
|
||||
cleanup_manager.record_result(
|
||||
CleanupPhase.UPDATE_CHECKER,
|
||||
CleanupStatus.SUCCESS,
|
||||
duration=(datetime.utcnow() - phase_start).total_seconds()
|
||||
duration=(datetime.utcnow() - phase_start).total_seconds(),
|
||||
)
|
||||
except asyncio.TimeoutError as e:
|
||||
logger.warning("Update checker stop timed out")
|
||||
@@ -137,7 +148,7 @@ async def cleanup_resources(cog: "VideoArchiver") -> None:
|
||||
CleanupPhase.UPDATE_CHECKER,
|
||||
CleanupStatus.TIMEOUT,
|
||||
str(e),
|
||||
(datetime.utcnow() - phase_start).total_seconds()
|
||||
(datetime.utcnow() - phase_start).total_seconds(),
|
||||
)
|
||||
cog.update_checker = None
|
||||
|
||||
@@ -147,13 +158,12 @@ async def cleanup_resources(cog: "VideoArchiver") -> None:
|
||||
try:
|
||||
logger.info("Cleaning up processor")
|
||||
await asyncio.wait_for(
|
||||
cog.processor.cleanup(),
|
||||
timeout=cleanup_manager.CLEANUP_TIMEOUT
|
||||
cog.processor.cleanup(), timeout=cleanup_manager.CLEANUP_TIMEOUT
|
||||
)
|
||||
cleanup_manager.record_result(
|
||||
CleanupPhase.PROCESSOR,
|
||||
CleanupStatus.SUCCESS,
|
||||
duration=(datetime.utcnow() - phase_start).total_seconds()
|
||||
duration=(datetime.utcnow() - phase_start).total_seconds(),
|
||||
)
|
||||
except asyncio.TimeoutError as e:
|
||||
logger.warning("Processor cleanup timed out, forcing cleanup")
|
||||
@@ -162,7 +172,7 @@ async def cleanup_resources(cog: "VideoArchiver") -> None:
|
||||
CleanupPhase.PROCESSOR,
|
||||
CleanupStatus.TIMEOUT,
|
||||
str(e),
|
||||
(datetime.utcnow() - phase_start).total_seconds()
|
||||
(datetime.utcnow() - phase_start).total_seconds(),
|
||||
)
|
||||
cog.processor = None
|
||||
|
||||
@@ -172,13 +182,12 @@ async def cleanup_resources(cog: "VideoArchiver") -> None:
|
||||
try:
|
||||
logger.info("Cleaning up queue manager")
|
||||
await asyncio.wait_for(
|
||||
cog.queue_manager.cleanup(),
|
||||
timeout=cleanup_manager.CLEANUP_TIMEOUT
|
||||
cog.queue_manager.cleanup(), timeout=cleanup_manager.CLEANUP_TIMEOUT
|
||||
)
|
||||
cleanup_manager.record_result(
|
||||
CleanupPhase.QUEUE_MANAGER,
|
||||
CleanupStatus.SUCCESS,
|
||||
duration=(datetime.utcnow() - phase_start).total_seconds()
|
||||
duration=(datetime.utcnow() - phase_start).total_seconds(),
|
||||
)
|
||||
except asyncio.TimeoutError as e:
|
||||
logger.warning("Queue manager cleanup timed out, forcing stop")
|
||||
@@ -187,7 +196,7 @@ async def cleanup_resources(cog: "VideoArchiver") -> None:
|
||||
CleanupPhase.QUEUE_MANAGER,
|
||||
CleanupStatus.TIMEOUT,
|
||||
str(e),
|
||||
(datetime.utcnow() - phase_start).total_seconds()
|
||||
(datetime.utcnow() - phase_start).total_seconds(),
|
||||
)
|
||||
cog.queue_manager = None
|
||||
|
||||
@@ -214,14 +223,14 @@ async def cleanup_resources(cog: "VideoArchiver") -> None:
|
||||
CleanupPhase.COMPONENTS,
|
||||
status,
|
||||
"\n".join(errors) if errors else None,
|
||||
(datetime.utcnow() - phase_start).total_seconds()
|
||||
(datetime.utcnow() - phase_start).total_seconds(),
|
||||
)
|
||||
except Exception as e:
|
||||
cleanup_manager.record_result(
|
||||
CleanupPhase.COMPONENTS,
|
||||
CleanupStatus.ERROR,
|
||||
str(e),
|
||||
(datetime.utcnow() - phase_start).total_seconds()
|
||||
(datetime.utcnow() - phase_start).total_seconds(),
|
||||
)
|
||||
|
||||
# Kill any FFmpeg processes
|
||||
@@ -233,7 +242,7 @@ async def cleanup_resources(cog: "VideoArchiver") -> None:
|
||||
cog.ffmpeg_mgr = None
|
||||
|
||||
# Kill any remaining FFmpeg processes system-wide
|
||||
if os.name != 'nt': # Unix-like systems
|
||||
if os.name != "nt": # Unix-like systems
|
||||
os.system("pkill -9 ffmpeg")
|
||||
else: # Windows
|
||||
os.system("taskkill /F /IM ffmpeg.exe")
|
||||
@@ -241,14 +250,14 @@ async def cleanup_resources(cog: "VideoArchiver") -> None:
|
||||
cleanup_manager.record_result(
|
||||
CleanupPhase.FFMPEG,
|
||||
CleanupStatus.SUCCESS,
|
||||
duration=(datetime.utcnow() - phase_start).total_seconds()
|
||||
duration=(datetime.utcnow() - phase_start).total_seconds(),
|
||||
)
|
||||
except Exception as e:
|
||||
cleanup_manager.record_result(
|
||||
CleanupPhase.FFMPEG,
|
||||
CleanupStatus.ERROR,
|
||||
str(e),
|
||||
(datetime.utcnow() - phase_start).total_seconds()
|
||||
(datetime.utcnow() - phase_start).total_seconds(),
|
||||
)
|
||||
|
||||
# Clean up download directory
|
||||
@@ -258,21 +267,21 @@ async def cleanup_resources(cog: "VideoArchiver") -> None:
|
||||
logger.info("Cleaning up download directory")
|
||||
await asyncio.wait_for(
|
||||
cleanup_downloads(str(cog.download_path)),
|
||||
timeout=cleanup_manager.CLEANUP_TIMEOUT
|
||||
timeout=cleanup_manager.CLEANUP_TIMEOUT,
|
||||
)
|
||||
if cog.download_path.exists():
|
||||
cog.download_path.rmdir()
|
||||
cleanup_manager.record_result(
|
||||
CleanupPhase.DOWNLOADS,
|
||||
CleanupStatus.SUCCESS,
|
||||
duration=(datetime.utcnow() - phase_start).total_seconds()
|
||||
duration=(datetime.utcnow() - phase_start).total_seconds(),
|
||||
)
|
||||
except Exception as e:
|
||||
cleanup_manager.record_result(
|
||||
CleanupPhase.DOWNLOADS,
|
||||
CleanupStatus.ERROR,
|
||||
str(e),
|
||||
(datetime.utcnow() - phase_start).total_seconds()
|
||||
(datetime.utcnow() - phase_start).total_seconds(),
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
@@ -284,8 +293,8 @@ async def cleanup_resources(cog: "VideoArchiver") -> None:
|
||||
"Cleanup",
|
||||
"cleanup_resources",
|
||||
{"duration": (datetime.utcnow() - start_time).total_seconds()},
|
||||
ErrorSeverity.HIGH
|
||||
)
|
||||
ErrorSeverity.HIGH,
|
||||
),
|
||||
)
|
||||
finally:
|
||||
logger.info("Clearing ready flag")
|
||||
@@ -294,17 +303,18 @@ async def cleanup_resources(cog: "VideoArchiver") -> None:
|
||||
# Log cleanup results
|
||||
for phase, result in cleanup_manager.get_results().items():
|
||||
status_str = f"{result['status'].name}"
|
||||
if result['error']:
|
||||
if result["error"]:
|
||||
status_str += f" ({result['error']})"
|
||||
logger.info(
|
||||
f"Cleanup phase {phase.name}: {status_str} "
|
||||
f"(Duration: {result['duration']:.2f}s)"
|
||||
)
|
||||
|
||||
|
||||
async def force_cleanup_resources(cog: "VideoArchiver") -> None:
|
||||
"""
|
||||
Force cleanup of resources when timeout occurs.
|
||||
|
||||
|
||||
Args:
|
||||
cog: VideoArchiver cog instance
|
||||
"""
|
||||
@@ -323,14 +333,14 @@ async def force_cleanup_resources(cog: "VideoArchiver") -> None:
|
||||
cleanup_manager.record_result(
|
||||
CleanupPhase.PROCESSOR,
|
||||
CleanupStatus.SUCCESS,
|
||||
duration=(datetime.utcnow() - phase_start).total_seconds()
|
||||
duration=(datetime.utcnow() - phase_start).total_seconds(),
|
||||
)
|
||||
except Exception as e:
|
||||
cleanup_manager.record_result(
|
||||
CleanupPhase.PROCESSOR,
|
||||
CleanupStatus.ERROR,
|
||||
str(e),
|
||||
(datetime.utcnow() - phase_start).total_seconds()
|
||||
(datetime.utcnow() - phase_start).total_seconds(),
|
||||
)
|
||||
cog.processor = None
|
||||
|
||||
@@ -343,14 +353,14 @@ async def force_cleanup_resources(cog: "VideoArchiver") -> None:
|
||||
cleanup_manager.record_result(
|
||||
CleanupPhase.QUEUE_MANAGER,
|
||||
CleanupStatus.SUCCESS,
|
||||
duration=(datetime.utcnow() - phase_start).total_seconds()
|
||||
duration=(datetime.utcnow() - phase_start).total_seconds(),
|
||||
)
|
||||
except Exception as e:
|
||||
cleanup_manager.record_result(
|
||||
CleanupPhase.QUEUE_MANAGER,
|
||||
CleanupStatus.ERROR,
|
||||
str(e),
|
||||
(datetime.utcnow() - phase_start).total_seconds()
|
||||
(datetime.utcnow() - phase_start).total_seconds(),
|
||||
)
|
||||
cog.queue_manager = None
|
||||
|
||||
@@ -363,7 +373,7 @@ async def force_cleanup_resources(cog: "VideoArchiver") -> None:
|
||||
cog.ffmpeg_mgr = None
|
||||
|
||||
# Force kill any remaining FFmpeg processes system-wide
|
||||
if os.name != 'nt': # Unix-like systems
|
||||
if os.name != "nt": # Unix-like systems
|
||||
os.system("pkill -9 ffmpeg")
|
||||
else: # Windows
|
||||
os.system("taskkill /F /IM ffmpeg.exe")
|
||||
@@ -371,14 +381,14 @@ async def force_cleanup_resources(cog: "VideoArchiver") -> None:
|
||||
cleanup_manager.record_result(
|
||||
CleanupPhase.FFMPEG,
|
||||
CleanupStatus.SUCCESS,
|
||||
duration=(datetime.utcnow() - phase_start).total_seconds()
|
||||
duration=(datetime.utcnow() - phase_start).total_seconds(),
|
||||
)
|
||||
except Exception as e:
|
||||
cleanup_manager.record_result(
|
||||
CleanupPhase.FFMPEG,
|
||||
CleanupStatus.ERROR,
|
||||
str(e),
|
||||
(datetime.utcnow() - phase_start).total_seconds()
|
||||
(datetime.utcnow() - phase_start).total_seconds(),
|
||||
)
|
||||
|
||||
# Clean up download directory
|
||||
@@ -388,21 +398,21 @@ async def force_cleanup_resources(cog: "VideoArchiver") -> None:
|
||||
logger.info("Force cleaning download directory")
|
||||
await asyncio.wait_for(
|
||||
cleanup_downloads(str(cog.download_path)),
|
||||
timeout=cleanup_manager.FORCE_CLEANUP_TIMEOUT
|
||||
timeout=cleanup_manager.FORCE_CLEANUP_TIMEOUT,
|
||||
)
|
||||
if cog.download_path.exists():
|
||||
cog.download_path.rmdir()
|
||||
cleanup_manager.record_result(
|
||||
CleanupPhase.DOWNLOADS,
|
||||
CleanupStatus.SUCCESS,
|
||||
duration=(datetime.utcnow() - phase_start).total_seconds()
|
||||
duration=(datetime.utcnow() - phase_start).total_seconds(),
|
||||
)
|
||||
except Exception as e:
|
||||
cleanup_manager.record_result(
|
||||
CleanupPhase.DOWNLOADS,
|
||||
CleanupStatus.ERROR,
|
||||
str(e),
|
||||
(datetime.utcnow() - phase_start).total_seconds()
|
||||
(datetime.utcnow() - phase_start).total_seconds(),
|
||||
)
|
||||
|
||||
# Clear all components
|
||||
@@ -414,14 +424,14 @@ async def force_cleanup_resources(cog: "VideoArchiver") -> None:
|
||||
cleanup_manager.record_result(
|
||||
CleanupPhase.COMPONENTS,
|
||||
CleanupStatus.SUCCESS,
|
||||
duration=(datetime.utcnow() - phase_start).total_seconds()
|
||||
duration=(datetime.utcnow() - phase_start).total_seconds(),
|
||||
)
|
||||
except Exception as e:
|
||||
cleanup_manager.record_result(
|
||||
CleanupPhase.COMPONENTS,
|
||||
CleanupStatus.ERROR,
|
||||
str(e),
|
||||
(datetime.utcnow() - phase_start).total_seconds()
|
||||
(datetime.utcnow() - phase_start).total_seconds(),
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
@@ -443,25 +453,25 @@ async def force_cleanup_resources(cog: "VideoArchiver") -> None:
|
||||
cog.db = None
|
||||
cog._init_task = None
|
||||
cog._cleanup_task = None
|
||||
if hasattr(cog, '_queue_task'):
|
||||
if hasattr(cog, "_queue_task"):
|
||||
cog._queue_task = None
|
||||
cleanup_manager.record_result(
|
||||
CleanupPhase.REFERENCES,
|
||||
CleanupStatus.SUCCESS,
|
||||
duration=(datetime.utcnow() - phase_start).total_seconds()
|
||||
duration=(datetime.utcnow() - phase_start).total_seconds(),
|
||||
)
|
||||
except Exception as e:
|
||||
cleanup_manager.record_result(
|
||||
CleanupPhase.REFERENCES,
|
||||
CleanupStatus.ERROR,
|
||||
str(e),
|
||||
(datetime.utcnow() - phase_start).total_seconds()
|
||||
(datetime.utcnow() - phase_start).total_seconds(),
|
||||
)
|
||||
|
||||
# Log cleanup results
|
||||
for phase, result in cleanup_manager.get_results().items():
|
||||
status_str = f"{result['status'].name}"
|
||||
if result['error']:
|
||||
if result["error"]:
|
||||
status_str += f" ({result['error']})"
|
||||
logger.info(
|
||||
f"Force cleanup phase {phase.name}: {status_str} "
|
||||
|
||||
@@ -7,9 +7,10 @@ from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
# try:
|
||||
from .base import VideoArchiver
|
||||
# except ImportError:
|
||||
# from videoarchiver.core.base import VideoArchiver
|
||||
from base import VideoArchiver
|
||||
# except ImportError:
|
||||
# from videoarchiver.core.base import VideoArchiver
|
||||
|
||||
|
||||
def setup_commands(cog: "VideoArchiver") -> None:
|
||||
"""Command setup is now handled in the VideoArchiver class"""
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"""Command handlers for VideoArchiver"""
|
||||
|
||||
from .archiver_commands import setup_archiver_commands
|
||||
from .database_commands import setup_database_commands
|
||||
from .settings_commands import setup_settings_commands
|
||||
from archiver_commands import setup_archiver_commands
|
||||
from database_commands import setup_database_commands
|
||||
from settings_commands import setup_settings_commands
|
||||
|
||||
__all__ = [
|
||||
"setup_archiver_commands",
|
||||
|
||||
@@ -9,8 +9,8 @@ from discord import app_commands # type: ignore
|
||||
from redbot.core import commands # type: ignore
|
||||
from redbot.core.commands import Context, hybrid_group, guild_only, admin_or_permissions # type: ignore
|
||||
|
||||
from ...core.response_handler import handle_response, ResponseType
|
||||
from ...utils.exceptions import CommandError, ErrorContext, ErrorSeverity
|
||||
from core.response_handler import handle_response, ResponseType
|
||||
from utils.exceptions import CommandError, ErrorContext, ErrorSeverity
|
||||
|
||||
logger = logging.getLogger("VideoArchiver")
|
||||
|
||||
|
||||
@@ -10,9 +10,9 @@ from discord import app_commands # type: ignore
|
||||
from redbot.core import commands # type: ignore
|
||||
from redbot.core.commands import Context, hybrid_group, guild_only, admin_or_permissions # type: ignore
|
||||
|
||||
from ...core.response_handler import handle_response, ResponseType
|
||||
from ...utils.exceptions import CommandError, ErrorContext, ErrorSeverity, DatabaseError
|
||||
from ...database.video_archive_db import VideoArchiveDB
|
||||
from core.response_handler import handle_response, ResponseType
|
||||
from utils.exceptions import CommandError, ErrorContext, ErrorSeverity, DatabaseError
|
||||
from database.video_archive_db import VideoArchiveDB
|
||||
|
||||
logger = logging.getLogger("VideoArchiver")
|
||||
|
||||
|
||||
@@ -9,9 +9,9 @@ from discord import app_commands # type: ignore
|
||||
from redbot.core import commands # type: ignore
|
||||
from redbot.core.commands import Context, hybrid_group, guild_only, admin_or_permissions # type: ignore
|
||||
|
||||
from ...core.settings import VideoFormat, VideoQuality
|
||||
from ...core.response_handler import handle_response, ResponseType
|
||||
from ...utils.exceptions import CommandError, ErrorContext, ErrorSeverity
|
||||
from core.settings import VideoFormat, VideoQuality
|
||||
from core.response_handler import handle_response, ResponseType
|
||||
from utils.exceptions import CommandError, ErrorContext, ErrorSeverity
|
||||
|
||||
logger = logging.getLogger("VideoArchiver")
|
||||
|
||||
|
||||
@@ -22,12 +22,12 @@ import importlib
|
||||
|
||||
# try:
|
||||
# Try relative imports first
|
||||
from ..utils.exceptions import ComponentError, ErrorContext, ErrorSeverity
|
||||
from ..utils.path_manager import PathManager
|
||||
from ..config_manager import ConfigManager
|
||||
from ..processor.core import VideoProcessor
|
||||
from ..queue.manager import EnhancedVideoQueueManager
|
||||
from ..ffmpeg.ffmpeg_manager import FFmpegManager
|
||||
from utils.exceptions import ComponentError, ErrorContext, ErrorSeverity
|
||||
from utils.path_manager import PathManager
|
||||
from config_manager import ConfigManager
|
||||
from processor.core import VideoProcessor
|
||||
from queue.manager import EnhancedVideoQueueManager
|
||||
from ffmpeg.ffmpeg_manager import FFmpegManager
|
||||
|
||||
# except ImportError:
|
||||
# Fall back to absolute imports if relative imports fail
|
||||
|
||||
@@ -4,19 +4,19 @@ import logging
|
||||
import traceback
|
||||
from typing import Dict, Optional, Tuple, Type, TypedDict, ClassVar
|
||||
from enum import Enum, auto
|
||||
import discord # type: ignore
|
||||
from redbot.core.commands import ( # type: ignore
|
||||
import discord # type: ignore
|
||||
from redbot.core.commands import ( # type: ignore
|
||||
Context,
|
||||
MissingPermissions,
|
||||
BotMissingPermissions,
|
||||
MissingRequiredArgument,
|
||||
BadArgument,
|
||||
CommandError
|
||||
CommandError,
|
||||
)
|
||||
|
||||
#try:
|
||||
# Try relative imports first
|
||||
from ..utils.exceptions import (
|
||||
# try:
|
||||
# Try relative imports first
|
||||
from utils.exceptions import (
|
||||
VideoArchiverError,
|
||||
ErrorSeverity,
|
||||
ErrorContext,
|
||||
@@ -33,9 +33,10 @@ from ..utils.exceptions import (
|
||||
TrackingError,
|
||||
NetworkError,
|
||||
ResourceExhaustedError,
|
||||
ConfigurationError
|
||||
ConfigurationError,
|
||||
)
|
||||
from ..core.response_handler import response_manager
|
||||
from core.response_handler import response_manager
|
||||
|
||||
# except ImportError:
|
||||
# # Fall back to absolute imports if relative imports fail
|
||||
# # from videoarchiver.utils.exceptions import (
|
||||
@@ -57,12 +58,14 @@ from ..core.response_handler import response_manager
|
||||
# ResourceExhaustedError,
|
||||
# ConfigurationError
|
||||
# )
|
||||
# from videoarchiver.core.response_handler import response_manager
|
||||
# from videoarchiver.core.response_handler import response_manager
|
||||
|
||||
logger = logging.getLogger("VideoArchiver")
|
||||
|
||||
|
||||
class ErrorCategory(Enum):
|
||||
"""Categories of errors"""
|
||||
|
||||
PERMISSION = auto()
|
||||
ARGUMENT = auto()
|
||||
CONFIGURATION = auto()
|
||||
@@ -76,17 +79,22 @@ class ErrorCategory(Enum):
|
||||
HEALTH = auto()
|
||||
UNEXPECTED = auto()
|
||||
|
||||
|
||||
class ErrorStats(TypedDict):
|
||||
"""Type definition for error statistics"""
|
||||
|
||||
counts: Dict[str, int]
|
||||
patterns: Dict[str, Dict[str, int]]
|
||||
severities: Dict[str, Dict[str, int]]
|
||||
|
||||
|
||||
class ErrorFormatter:
|
||||
"""Formats error messages for display"""
|
||||
|
||||
@staticmethod
|
||||
def format_error_message(error: Exception, context: Optional[ErrorContext] = None) -> str:
|
||||
def format_error_message(
|
||||
error: Exception, context: Optional[ErrorContext] = None
|
||||
) -> str:
|
||||
"""Format error message with context"""
|
||||
base_message = str(error)
|
||||
if context:
|
||||
@@ -110,16 +118,18 @@ class ErrorFormatter:
|
||||
return "An unexpected error occurred. Please check the logs for details."
|
||||
return str(error)
|
||||
|
||||
|
||||
class ErrorCategorizer:
|
||||
"""Categorizes errors and determines handling strategy"""
|
||||
|
||||
ERROR_MAPPING: ClassVar[Dict[Type[Exception], Tuple[ErrorCategory, ErrorSeverity]]] = {
|
||||
ERROR_MAPPING: ClassVar[
|
||||
Dict[Type[Exception], Tuple[ErrorCategory, ErrorSeverity]]
|
||||
] = {
|
||||
# Discord command errors
|
||||
MissingPermissions: (ErrorCategory.PERMISSION, ErrorSeverity.MEDIUM),
|
||||
BotMissingPermissions: (ErrorCategory.PERMISSION, ErrorSeverity.HIGH),
|
||||
MissingRequiredArgument: (ErrorCategory.ARGUMENT, ErrorSeverity.LOW),
|
||||
BadArgument: (ErrorCategory.ARGUMENT, ErrorSeverity.LOW),
|
||||
|
||||
# VideoArchiver errors
|
||||
ProcessorError: (ErrorCategory.PROCESSING, ErrorSeverity.HIGH),
|
||||
ValidationError: (ErrorCategory.VALIDATION, ErrorSeverity.MEDIUM),
|
||||
@@ -134,17 +144,17 @@ class ErrorCategorizer:
|
||||
TrackingError: (ErrorCategory.PROCESSING, ErrorSeverity.MEDIUM),
|
||||
NetworkError: (ErrorCategory.NETWORK, ErrorSeverity.MEDIUM),
|
||||
ResourceExhaustedError: (ErrorCategory.RESOURCE, ErrorSeverity.HIGH),
|
||||
ConfigurationError: (ErrorCategory.CONFIGURATION, ErrorSeverity.HIGH)
|
||||
ConfigurationError: (ErrorCategory.CONFIGURATION, ErrorSeverity.HIGH),
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def categorize_error(cls, error: Exception) -> Tuple[ErrorCategory, ErrorSeverity]:
|
||||
"""
|
||||
Categorize an error and determine its severity.
|
||||
|
||||
|
||||
Args:
|
||||
error: Exception to categorize
|
||||
|
||||
|
||||
Returns:
|
||||
Tuple of (Error category, Severity level)
|
||||
"""
|
||||
@@ -153,6 +163,7 @@ class ErrorCategorizer:
|
||||
return category, severity
|
||||
return ErrorCategory.UNEXPECTED, ErrorSeverity.HIGH
|
||||
|
||||
|
||||
class ErrorTracker:
|
||||
"""Tracks error occurrences and patterns"""
|
||||
|
||||
@@ -162,31 +173,28 @@ class ErrorTracker:
|
||||
self.error_severities: Dict[str, Dict[str, int]] = {}
|
||||
|
||||
def track_error(
|
||||
self,
|
||||
error: Exception,
|
||||
category: ErrorCategory,
|
||||
severity: ErrorSeverity
|
||||
self, error: Exception, category: ErrorCategory, severity: ErrorSeverity
|
||||
) -> None:
|
||||
"""
|
||||
Track an error occurrence.
|
||||
|
||||
|
||||
Args:
|
||||
error: Exception that occurred
|
||||
category: Error category
|
||||
severity: Error severity
|
||||
"""
|
||||
error_type = type(error).__name__
|
||||
|
||||
|
||||
# Track error counts
|
||||
self.error_counts[error_type] = self.error_counts.get(error_type, 0) + 1
|
||||
|
||||
|
||||
# Track error patterns by category
|
||||
if category.value not in self.error_patterns:
|
||||
self.error_patterns[category.value] = {}
|
||||
self.error_patterns[category.value][error_type] = (
|
||||
self.error_patterns[category.value].get(error_type, 0) + 1
|
||||
)
|
||||
|
||||
|
||||
# Track error severities
|
||||
if severity.value not in self.error_severities:
|
||||
self.error_severities[severity.value] = {}
|
||||
@@ -197,16 +205,17 @@ class ErrorTracker:
|
||||
def get_error_stats(self) -> ErrorStats:
|
||||
"""
|
||||
Get error statistics.
|
||||
|
||||
|
||||
Returns:
|
||||
Dictionary containing error statistics
|
||||
"""
|
||||
return ErrorStats(
|
||||
counts=self.error_counts.copy(),
|
||||
patterns=self.error_patterns.copy(),
|
||||
severities=self.error_severities.copy()
|
||||
severities=self.error_severities.copy(),
|
||||
)
|
||||
|
||||
|
||||
class ErrorManager:
|
||||
"""Manages error handling and reporting"""
|
||||
|
||||
@@ -215,14 +224,10 @@ class ErrorManager:
|
||||
self.categorizer = ErrorCategorizer()
|
||||
self.tracker = ErrorTracker()
|
||||
|
||||
async def handle_error(
|
||||
self,
|
||||
ctx: Context,
|
||||
error: Exception
|
||||
) -> None:
|
||||
async def handle_error(self, ctx: Context, error: Exception) -> None:
|
||||
"""
|
||||
Handle a command error.
|
||||
|
||||
|
||||
Args:
|
||||
ctx: Command context
|
||||
error: The error that occurred
|
||||
@@ -230,7 +235,7 @@ class ErrorManager:
|
||||
try:
|
||||
# Categorize error
|
||||
category, severity = self.categorizer.categorize_error(error)
|
||||
|
||||
|
||||
# Create error context
|
||||
context = ErrorContext(
|
||||
component=ctx.command.qualified_name if ctx.command else "unknown",
|
||||
@@ -238,26 +243,24 @@ class ErrorManager:
|
||||
details={
|
||||
"guild_id": str(ctx.guild.id) if ctx.guild else "DM",
|
||||
"channel_id": str(ctx.channel.id),
|
||||
"user_id": str(ctx.author.id)
|
||||
"user_id": str(ctx.author.id),
|
||||
},
|
||||
severity=severity
|
||||
severity=severity,
|
||||
)
|
||||
|
||||
|
||||
# Track error
|
||||
self.tracker.track_error(error, category, severity)
|
||||
|
||||
|
||||
# Format error messages
|
||||
log_message = self.formatter.format_error_message(error, context)
|
||||
user_message = self.formatter.format_user_message(error, category)
|
||||
|
||||
|
||||
# Log error details
|
||||
self._log_error(log_message, severity)
|
||||
|
||||
|
||||
# Send response
|
||||
await response_manager.send_response(
|
||||
ctx,
|
||||
content=user_message,
|
||||
response_type=severity.name.lower()
|
||||
ctx, content=user_message, response_type=severity.name.lower()
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
@@ -269,19 +272,15 @@ class ErrorManager:
|
||||
await response_manager.send_response(
|
||||
ctx,
|
||||
content="An error occurred while handling another error. Please check the logs.",
|
||||
response_type="error"
|
||||
response_type="error",
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _log_error(
|
||||
self,
|
||||
message: str,
|
||||
severity: ErrorSeverity
|
||||
) -> None:
|
||||
def _log_error(self, message: str, severity: ErrorSeverity) -> None:
|
||||
"""
|
||||
Log error details.
|
||||
|
||||
|
||||
Args:
|
||||
message: Error message to log
|
||||
severity: Error severity
|
||||
@@ -296,13 +295,15 @@ class ErrorManager:
|
||||
except Exception as e:
|
||||
logger.error(f"Error logging error details: {e}")
|
||||
|
||||
|
||||
# Global error manager instance
|
||||
error_manager = ErrorManager()
|
||||
|
||||
|
||||
async def handle_command_error(ctx: Context, error: Exception) -> None:
|
||||
"""
|
||||
Helper function to handle command errors using the error manager.
|
||||
|
||||
|
||||
Args:
|
||||
ctx: Command context
|
||||
error: Exception to handle
|
||||
|
||||
@@ -9,28 +9,29 @@ from typing import TYPE_CHECKING, Dict, Any, Optional, TypedDict, ClassVar, List
|
||||
|
||||
import discord # type: ignore
|
||||
|
||||
#try:
|
||||
# Try relative imports first
|
||||
from ..processor.constants import REACTIONS
|
||||
from ..processor.reactions import handle_archived_reaction
|
||||
from .guild import initialize_guild_components, cleanup_guild_components
|
||||
from .error_handler import ErrorManager
|
||||
from .response_handler import response_manager
|
||||
from ..utils.exceptions import EventError, ErrorContext, ErrorSeverity
|
||||
#except ImportError:
|
||||
# Fall back to absolute imports if relative imports fail
|
||||
# from videoarchiver.processor.constants import REACTIONS
|
||||
# from videoarchiver.processor.reactions import handle_archived_reaction
|
||||
# from videoarchiver.core.guild import initialize_guild_components, cleanup_guild_components
|
||||
# from videoarchiver.core.error_handler import ErrorManager
|
||||
# from videoarchiver.core.response_handler import response_manager
|
||||
# from videoarchiver.utils.exceptions import EventError, ErrorContext, ErrorSeverity
|
||||
# try:
|
||||
# Try relative imports first
|
||||
from processor.constants import REACTIONS
|
||||
from processor.reactions import handle_archived_reaction
|
||||
from guild import initialize_guild_components, cleanup_guild_components
|
||||
from error_handler import ErrorManager
|
||||
from response_handler import response_manager
|
||||
from utils.exceptions import EventError, ErrorContext, ErrorSeverity
|
||||
|
||||
# except ImportError:
|
||||
# Fall back to absolute imports if relative imports fail
|
||||
# from videoarchiver.processor.constants import REACTIONS
|
||||
# from videoarchiver.processor.reactions import handle_archived_reaction
|
||||
# from videoarchiver.core.guild import initialize_guild_components, cleanup_guild_components
|
||||
# from videoarchiver.core.error_handler import ErrorManager
|
||||
# from videoarchiver.core.response_handler import response_manager
|
||||
# from videoarchiver.utils.exceptions import EventError, ErrorContext, ErrorSeverity
|
||||
|
||||
if TYPE_CHECKING:
|
||||
#try:
|
||||
from .base import VideoArchiver
|
||||
# except ImportError:
|
||||
# from videoarchiver.core.base import VideoArchiver
|
||||
# try:
|
||||
from base import VideoArchiver
|
||||
# except ImportError:
|
||||
# from videoarchiver.core.base import VideoArchiver
|
||||
|
||||
logger = logging.getLogger("VideoArchiver")
|
||||
|
||||
|
||||
@@ -6,10 +6,10 @@ from typing import TYPE_CHECKING, Dict, Any, Optional
|
||||
|
||||
# try:
|
||||
# Try relative imports first
|
||||
from ..utils.download_core import DownloadCore
|
||||
from ..utils.message_manager import MessageManager
|
||||
from ..utils.file_ops import cleanup_downloads
|
||||
from ..utils.exceptions import VideoArchiverError as ProcessingError
|
||||
from utils.download_core import DownloadCore
|
||||
from utils.message_manager import MessageManager
|
||||
from utils.file_ops import cleanup_downloads
|
||||
from utils.exceptions import VideoArchiverError as ProcessingError
|
||||
|
||||
# except ImportError:
|
||||
# Fall back to absolute imports if relative imports fail
|
||||
@@ -20,7 +20,7 @@ from ..utils.exceptions import VideoArchiverError as ProcessingError
|
||||
|
||||
if TYPE_CHECKING:
|
||||
# try:
|
||||
from .base import VideoArchiver
|
||||
from base import VideoArchiver
|
||||
# except ImportError:
|
||||
# from videoarchiver.core.base import VideoArchiver
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ import logging
|
||||
|
||||
# try:
|
||||
# Try relative imports first
|
||||
from ..utils.exceptions import ComponentError, ErrorContext, ErrorSeverity
|
||||
from .lifecycle import LifecycleState
|
||||
from utils.exceptions import ComponentError, ErrorContext, ErrorSeverity
|
||||
from lifecycle import LifecycleState
|
||||
|
||||
# except ImportError:
|
||||
# Fall back to absolute imports if relative imports fail
|
||||
@@ -16,7 +16,7 @@ from .lifecycle import LifecycleState
|
||||
|
||||
if TYPE_CHECKING:
|
||||
# try:
|
||||
from .base import VideoArchiver
|
||||
from base import VideoArchiver
|
||||
# except ImportError:
|
||||
# from videoarchiver.core.base import VideoArchiver
|
||||
|
||||
|
||||
@@ -8,25 +8,26 @@ from enum import Enum, auto
|
||||
from datetime import datetime
|
||||
|
||||
# try:
|
||||
# Try relative imports first
|
||||
from .cleanup import cleanup_resources, force_cleanup_resources
|
||||
from ..utils.exceptions import (
|
||||
# Try relative imports first
|
||||
from cleanup import cleanup_resources, force_cleanup_resources
|
||||
from utils.exceptions import (
|
||||
VideoArchiverError,
|
||||
ErrorContext,
|
||||
ErrorSeverity,
|
||||
ComponentError,
|
||||
CleanupError,
|
||||
)
|
||||
#except ImportError:
|
||||
# Fall back to absolute imports if relative imports fail
|
||||
# from videoarchiver.core.cleanup import cleanup_resources, force_cleanup_resources
|
||||
# from videoarchiver.utils.exceptions import (
|
||||
# VideoArchiverError,
|
||||
# ErrorContext,
|
||||
# ErrorSeverity,
|
||||
# ComponentError,
|
||||
# CleanupError,
|
||||
# )
|
||||
|
||||
# except ImportError:
|
||||
# Fall back to absolute imports if relative imports fail
|
||||
# from videoarchiver.core.cleanup import cleanup_resources, force_cleanup_resources
|
||||
# from videoarchiver.utils.exceptions import (
|
||||
# VideoArchiverError,
|
||||
# ErrorContext,
|
||||
# ErrorSeverity,
|
||||
# ComponentError,
|
||||
# CleanupError,
|
||||
# )
|
||||
|
||||
logger = logging.getLogger("VideoArchiver")
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ from redbot.core.commands import Context # type: ignore
|
||||
|
||||
# try:
|
||||
# Try relative imports first
|
||||
from ..utils.exceptions import ErrorSeverity
|
||||
from utils.exceptions import ErrorSeverity
|
||||
|
||||
# except ImportError:
|
||||
# Fall back to absolute imports if relative imports fail
|
||||
|
||||
@@ -6,7 +6,7 @@ from enum import Enum, auto
|
||||
|
||||
# try:
|
||||
# Try relative imports first
|
||||
from ..utils.exceptions import ConfigurationError, ErrorContext, ErrorSeverity
|
||||
from utils.exceptions import ConfigurationError, ErrorContext, ErrorSeverity
|
||||
|
||||
# except ImportError:
|
||||
# Fall back to absolute imports if relative imports fail
|
||||
|
||||
Reference in New Issue
Block a user