@@ -0,0 +1,172 @@
|
||||
"""
|
||||
Custom exceptions for NFOGuard
|
||||
Provides structured error handling and better error reporting
|
||||
"""
|
||||
from typing import Optional, Dict, Any
|
||||
|
||||
|
||||
class NFOGuardException(Exception):
|
||||
"""Base exception for all NFOGuard errors"""
|
||||
|
||||
def __init__(self, message: str, details: Optional[Dict[str, Any]] = None):
|
||||
super().__init__(message)
|
||||
self.message = message
|
||||
self.details = details or {}
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Convert exception to dictionary for structured logging"""
|
||||
return {
|
||||
"error_type": self.__class__.__name__,
|
||||
"message": self.message,
|
||||
"details": self.details
|
||||
}
|
||||
|
||||
|
||||
class MediaPathNotFoundError(NFOGuardException):
|
||||
"""Raised when media directory cannot be found"""
|
||||
|
||||
def __init__(self, media_type: str, title: str, imdb_id: Optional[str] = None, search_paths: Optional[list] = None):
|
||||
details = {
|
||||
"media_type": media_type,
|
||||
"title": title,
|
||||
"imdb_id": imdb_id,
|
||||
"search_paths": [str(p) for p in (search_paths or [])]
|
||||
}
|
||||
message = f"{media_type.title()} directory not found: {title}"
|
||||
if imdb_id:
|
||||
message += f" (IMDb: {imdb_id})"
|
||||
super().__init__(message, details)
|
||||
|
||||
|
||||
class IMDbIDNotFoundError(NFOGuardException):
|
||||
"""Raised when IMDb ID cannot be extracted from path or files"""
|
||||
|
||||
def __init__(self, path: str, media_type: str = "media"):
|
||||
details = {
|
||||
"path": path,
|
||||
"media_type": media_type
|
||||
}
|
||||
message = f"No IMDb ID found for {media_type}: {path}"
|
||||
super().__init__(message, details)
|
||||
|
||||
|
||||
class WebhookProcessingError(NFOGuardException):
|
||||
"""Raised when webhook processing fails"""
|
||||
|
||||
def __init__(self, webhook_type: str, reason: str, payload: Optional[Dict] = None):
|
||||
details = {
|
||||
"webhook_type": webhook_type,
|
||||
"reason": reason,
|
||||
"payload": payload
|
||||
}
|
||||
message = f"{webhook_type} webhook processing failed: {reason}"
|
||||
super().__init__(message, details)
|
||||
|
||||
|
||||
class ExternalAPIError(NFOGuardException):
|
||||
"""Raised when external API calls fail"""
|
||||
|
||||
def __init__(self, api_name: str, operation: str, status_code: Optional[int] = None, response: Optional[str] = None):
|
||||
details = {
|
||||
"api_name": api_name,
|
||||
"operation": operation,
|
||||
"status_code": status_code,
|
||||
"response": response
|
||||
}
|
||||
message = f"{api_name} API error during {operation}"
|
||||
if status_code:
|
||||
message += f" (HTTP {status_code})"
|
||||
super().__init__(message, details)
|
||||
|
||||
|
||||
class DatabaseError(NFOGuardException):
|
||||
"""Raised when database operations fail"""
|
||||
|
||||
def __init__(self, operation: str, table: Optional[str] = None, original_error: Optional[Exception] = None):
|
||||
details = {
|
||||
"operation": operation,
|
||||
"table": table,
|
||||
"original_error": str(original_error) if original_error else None
|
||||
}
|
||||
message = f"Database error during {operation}"
|
||||
if table:
|
||||
message += f" on table {table}"
|
||||
super().__init__(message, details)
|
||||
|
||||
|
||||
class NFOCreationError(NFOGuardException):
|
||||
"""Raised when NFO file creation fails"""
|
||||
|
||||
def __init__(self, nfo_path: str, reason: str, media_type: str = "media"):
|
||||
details = {
|
||||
"nfo_path": nfo_path,
|
||||
"reason": reason,
|
||||
"media_type": media_type
|
||||
}
|
||||
message = f"Failed to create {media_type} NFO file: {reason}"
|
||||
super().__init__(message, details)
|
||||
|
||||
|
||||
class ConfigurationError(NFOGuardException):
|
||||
"""Raised when configuration is invalid or missing"""
|
||||
|
||||
def __init__(self, setting: str, reason: str, current_value: Optional[Any] = None):
|
||||
details = {
|
||||
"setting": setting,
|
||||
"reason": reason,
|
||||
"current_value": current_value
|
||||
}
|
||||
message = f"Configuration error for {setting}: {reason}"
|
||||
super().__init__(message, details)
|
||||
|
||||
|
||||
class FileOperationError(NFOGuardException):
|
||||
"""Raised when file operations fail"""
|
||||
|
||||
def __init__(self, operation: str, file_path: str, reason: str):
|
||||
details = {
|
||||
"operation": operation,
|
||||
"file_path": file_path,
|
||||
"reason": reason
|
||||
}
|
||||
message = f"File {operation} failed for {file_path}: {reason}"
|
||||
super().__init__(message, details)
|
||||
|
||||
|
||||
class DateProcessingError(NFOGuardException):
|
||||
"""Raised when date processing or parsing fails"""
|
||||
|
||||
def __init__(self, date_value: str, operation: str, media_type: str = "media"):
|
||||
details = {
|
||||
"date_value": date_value,
|
||||
"operation": operation,
|
||||
"media_type": media_type
|
||||
}
|
||||
message = f"Date processing error during {operation} for {media_type}: {date_value}"
|
||||
super().__init__(message, details)
|
||||
|
||||
|
||||
class RetryableError(NFOGuardException):
|
||||
"""Base class for errors that can be retried"""
|
||||
|
||||
def __init__(self, message: str, details: Optional[Dict[str, Any]] = None, retry_after: Optional[int] = None):
|
||||
super().__init__(message, details)
|
||||
self.retry_after = retry_after # Seconds to wait before retry
|
||||
|
||||
|
||||
class NetworkRetryableError(RetryableError):
|
||||
"""Network errors that can be retried"""
|
||||
|
||||
def __init__(self, url: str, reason: str, retry_after: Optional[int] = 30):
|
||||
details = {"url": url, "reason": reason}
|
||||
message = f"Network error for {url}: {reason}"
|
||||
super().__init__(message, details, retry_after)
|
||||
|
||||
|
||||
class TemporaryFileError(RetryableError):
|
||||
"""Temporary file system errors that can be retried"""
|
||||
|
||||
def __init__(self, file_path: str, operation: str, reason: str, retry_after: Optional[int] = 5):
|
||||
details = {"file_path": file_path, "operation": operation, "reason": reason}
|
||||
message = f"Temporary file error during {operation} for {file_path}: {reason}"
|
||||
super().__init__(message, details, retry_after)
|
||||
Reference in New Issue
Block a user