diabolical

This commit is contained in:
pacnpal
2024-11-18 05:17:37 +00:00
parent fc06e54d8a
commit df9099f2c8
75 changed files with 744 additions and 719 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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} "

View File

@@ -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"""

View File

@@ -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",

View File

@@ -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")

View File

@@ -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")

View File

@@ -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")

View File

@@ -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

View File

@@ -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

View File

@@ -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")

View File

@@ -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

View File

@@ -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

View File

@@ -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")

View File

@@ -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

View File

@@ -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