mirror of
https://github.com/pacnpal/Pac-cogs.git
synced 2025-12-20 10:51:05 -05:00
378 lines
13 KiB
Python
378 lines
13 KiB
Python
"""Module for managing VideoArchiver settings"""
|
|
|
|
from typing import Dict, Any, List, Optional, Union, TypedDict, ClassVar
|
|
from dataclasses import dataclass, field
|
|
from enum import Enum, auto
|
|
|
|
from .utils.exceptions import (
|
|
ConfigurationError,
|
|
ErrorContext,
|
|
ErrorSeverity
|
|
)
|
|
|
|
class VideoFormat(Enum):
|
|
"""Supported video formats"""
|
|
MP4 = "mp4"
|
|
WEBM = "webm"
|
|
MKV = "mkv"
|
|
|
|
class VideoQuality(Enum):
|
|
"""Video quality presets"""
|
|
LOW = "low" # 480p
|
|
MEDIUM = "medium" # 720p
|
|
HIGH = "high" # 1080p
|
|
ULTRA = "ultra" # 4K
|
|
|
|
class SettingCategory(Enum):
|
|
"""Setting categories"""
|
|
GENERAL = auto()
|
|
CHANNELS = auto()
|
|
PERMISSIONS = auto()
|
|
VIDEO = auto()
|
|
MESSAGES = auto()
|
|
PERFORMANCE = auto()
|
|
FEATURES = auto()
|
|
|
|
class ValidationResult(TypedDict):
|
|
"""Type definition for validation result"""
|
|
valid: bool
|
|
error: Optional[str]
|
|
details: Dict[str, Any]
|
|
|
|
@dataclass
|
|
class SettingDefinition:
|
|
"""Defines a setting's properties"""
|
|
name: str
|
|
category: SettingCategory
|
|
default_value: Any
|
|
description: str
|
|
data_type: type
|
|
required: bool = True
|
|
min_value: Optional[Union[int, float]] = None
|
|
max_value: Optional[Union[int, float]] = None
|
|
choices: Optional[List[Any]] = None
|
|
depends_on: Optional[str] = None
|
|
validation_func: Optional[callable] = None
|
|
error_message: Optional[str] = None
|
|
|
|
def __post_init__(self) -> None:
|
|
"""Validate setting definition"""
|
|
if self.choices and self.default_value not in self.choices:
|
|
raise ConfigurationError(
|
|
f"Default value {self.default_value} not in choices {self.choices}",
|
|
context=ErrorContext(
|
|
"Settings",
|
|
"definition_validation",
|
|
{"setting": self.name},
|
|
ErrorSeverity.HIGH
|
|
)
|
|
)
|
|
|
|
if self.min_value is not None and self.max_value is not None:
|
|
if self.min_value > self.max_value:
|
|
raise ConfigurationError(
|
|
f"Min value {self.min_value} greater than max value {self.max_value}",
|
|
context=ErrorContext(
|
|
"Settings",
|
|
"definition_validation",
|
|
{"setting": self.name},
|
|
ErrorSeverity.HIGH
|
|
)
|
|
)
|
|
|
|
class Settings:
|
|
"""Manages VideoArchiver settings"""
|
|
|
|
# Setting definitions
|
|
SETTINGS: ClassVar[Dict[str, SettingDefinition]] = {
|
|
"enabled": SettingDefinition(
|
|
name="enabled",
|
|
category=SettingCategory.GENERAL,
|
|
default_value=False,
|
|
description="Whether the archiver is enabled for this guild",
|
|
data_type=bool
|
|
),
|
|
"archive_channel": SettingDefinition(
|
|
name="archive_channel",
|
|
category=SettingCategory.CHANNELS,
|
|
default_value=None,
|
|
description="Channel where archived videos are posted",
|
|
data_type=int,
|
|
required=False,
|
|
error_message="Archive channel must be a valid channel ID"
|
|
),
|
|
"log_channel": SettingDefinition(
|
|
name="log_channel",
|
|
category=SettingCategory.CHANNELS,
|
|
default_value=None,
|
|
description="Channel for logging archiver actions",
|
|
data_type=int,
|
|
required=False,
|
|
error_message="Log channel must be a valid channel ID"
|
|
),
|
|
"enabled_channels": SettingDefinition(
|
|
name="enabled_channels",
|
|
category=SettingCategory.CHANNELS,
|
|
default_value=[],
|
|
description="Channels to monitor (empty means all channels)",
|
|
data_type=list,
|
|
error_message="Enabled channels must be a list of valid channel IDs"
|
|
),
|
|
"allowed_roles": SettingDefinition(
|
|
name="allowed_roles",
|
|
category=SettingCategory.PERMISSIONS,
|
|
default_value=[],
|
|
description="Roles allowed to use archiver (empty means all roles)",
|
|
data_type=list,
|
|
error_message="Allowed roles must be a list of valid role IDs"
|
|
),
|
|
"video_format": SettingDefinition(
|
|
name="video_format",
|
|
category=SettingCategory.VIDEO,
|
|
default_value=VideoFormat.MP4.value,
|
|
description="Format for archived videos",
|
|
data_type=str,
|
|
choices=[format.value for format in VideoFormat],
|
|
error_message=f"Video format must be one of: {', '.join(f.value for f in VideoFormat)}"
|
|
),
|
|
"video_quality": SettingDefinition(
|
|
name="video_quality",
|
|
category=SettingCategory.VIDEO,
|
|
default_value=VideoQuality.HIGH.value,
|
|
description="Quality preset for archived videos",
|
|
data_type=str,
|
|
choices=[quality.value for quality in VideoQuality],
|
|
error_message=f"Video quality must be one of: {', '.join(q.value for q in VideoQuality)}"
|
|
),
|
|
"max_file_size": SettingDefinition(
|
|
name="max_file_size",
|
|
category=SettingCategory.VIDEO,
|
|
default_value=8,
|
|
description="Maximum file size in MB",
|
|
data_type=int,
|
|
min_value=1,
|
|
max_value=100,
|
|
error_message="Max file size must be between 1 and 100 MB"
|
|
),
|
|
"message_duration": SettingDefinition(
|
|
name="message_duration",
|
|
category=SettingCategory.MESSAGES,
|
|
default_value=30,
|
|
description="Duration to show status messages (seconds)",
|
|
data_type=int,
|
|
min_value=5,
|
|
max_value=300,
|
|
error_message="Message duration must be between 5 and 300 seconds"
|
|
),
|
|
"message_template": SettingDefinition(
|
|
name="message_template",
|
|
category=SettingCategory.MESSAGES,
|
|
default_value="{author} archived a video from {channel}",
|
|
description="Template for archive messages",
|
|
data_type=str,
|
|
error_message="Message template must contain {author} and {channel} placeholders"
|
|
),
|
|
"concurrent_downloads": SettingDefinition(
|
|
name="concurrent_downloads",
|
|
category=SettingCategory.PERFORMANCE,
|
|
default_value=2,
|
|
description="Maximum concurrent downloads",
|
|
data_type=int,
|
|
min_value=1,
|
|
max_value=5,
|
|
error_message="Concurrent downloads must be between 1 and 5"
|
|
),
|
|
"enabled_sites": SettingDefinition(
|
|
name="enabled_sites",
|
|
category=SettingCategory.FEATURES,
|
|
default_value=None,
|
|
description="Sites to enable archiving for (None means all sites)",
|
|
data_type=list,
|
|
required=False,
|
|
error_message="Enabled sites must be a list of valid site identifiers"
|
|
),
|
|
"use_database": SettingDefinition(
|
|
name="use_database",
|
|
category=SettingCategory.FEATURES,
|
|
default_value=False,
|
|
description="Enable database tracking of archived videos",
|
|
data_type=bool
|
|
),
|
|
}
|
|
|
|
@classmethod
|
|
def get_setting_definition(cls, setting: str) -> Optional[SettingDefinition]:
|
|
"""
|
|
Get definition for a setting.
|
|
|
|
Args:
|
|
setting: Setting name
|
|
|
|
Returns:
|
|
Setting definition or None if not found
|
|
"""
|
|
return cls.SETTINGS.get(setting)
|
|
|
|
@classmethod
|
|
def get_settings_by_category(cls, category: SettingCategory) -> Dict[str, SettingDefinition]:
|
|
"""
|
|
Get all settings in a category.
|
|
|
|
Args:
|
|
category: Setting category
|
|
|
|
Returns:
|
|
Dictionary of settings in the category
|
|
"""
|
|
return {
|
|
name: definition
|
|
for name, definition in cls.SETTINGS.items()
|
|
if definition.category == category
|
|
}
|
|
|
|
@classmethod
|
|
def validate_setting(cls, setting: str, value: Any) -> ValidationResult:
|
|
"""
|
|
Validate a setting value.
|
|
|
|
Args:
|
|
setting: Setting name
|
|
value: Value to validate
|
|
|
|
Returns:
|
|
Validation result dictionary
|
|
|
|
Raises:
|
|
ConfigurationError: If setting definition is not found
|
|
"""
|
|
definition = cls.get_setting_definition(setting)
|
|
if not definition:
|
|
raise ConfigurationError(
|
|
f"Unknown setting: {setting}",
|
|
context=ErrorContext(
|
|
"Settings",
|
|
"validation",
|
|
{"setting": setting},
|
|
ErrorSeverity.HIGH
|
|
)
|
|
)
|
|
|
|
details = {
|
|
"setting": setting,
|
|
"value": value,
|
|
"type": type(value).__name__,
|
|
"expected_type": definition.data_type.__name__
|
|
}
|
|
|
|
# Check type
|
|
if not isinstance(value, definition.data_type):
|
|
return ValidationResult(
|
|
valid=False,
|
|
error=f"Invalid type: expected {definition.data_type.__name__}, got {type(value).__name__}",
|
|
details=details
|
|
)
|
|
|
|
# Check required
|
|
if definition.required and value is None:
|
|
return ValidationResult(
|
|
valid=False,
|
|
error="Required setting cannot be None",
|
|
details=details
|
|
)
|
|
|
|
# Check choices
|
|
if definition.choices and value not in definition.choices:
|
|
return ValidationResult(
|
|
valid=False,
|
|
error=f"Value must be one of: {', '.join(map(str, definition.choices))}",
|
|
details=details
|
|
)
|
|
|
|
# Check numeric bounds
|
|
if isinstance(value, (int, float)):
|
|
if definition.min_value is not None and value < definition.min_value:
|
|
return ValidationResult(
|
|
valid=False,
|
|
error=f"Value must be at least {definition.min_value}",
|
|
details=details
|
|
)
|
|
if definition.max_value is not None and value > definition.max_value:
|
|
return ValidationResult(
|
|
valid=False,
|
|
error=f"Value must be at most {definition.max_value}",
|
|
details=details
|
|
)
|
|
|
|
# Custom validation
|
|
if definition.validation_func:
|
|
try:
|
|
result = definition.validation_func(value)
|
|
if not result:
|
|
return ValidationResult(
|
|
valid=False,
|
|
error=definition.error_message or "Validation failed",
|
|
details=details
|
|
)
|
|
except Exception as e:
|
|
return ValidationResult(
|
|
valid=False,
|
|
error=str(e),
|
|
details=details
|
|
)
|
|
|
|
return ValidationResult(
|
|
valid=True,
|
|
error=None,
|
|
details=details
|
|
)
|
|
|
|
@property
|
|
def default_guild_settings(self) -> Dict[str, Any]:
|
|
"""
|
|
Default settings for guild configuration.
|
|
|
|
Returns:
|
|
Dictionary of default settings
|
|
"""
|
|
return {
|
|
name: definition.default_value
|
|
for name, definition in self.SETTINGS.items()
|
|
}
|
|
|
|
@classmethod
|
|
def get_setting_help(cls, setting: str) -> Optional[str]:
|
|
"""
|
|
Get help text for a setting.
|
|
|
|
Args:
|
|
setting: Setting name
|
|
|
|
Returns:
|
|
Help text or None if setting not found
|
|
"""
|
|
definition = cls.get_setting_definition(setting)
|
|
if not definition:
|
|
return None
|
|
|
|
help_text = [
|
|
f"Setting: {definition.name}",
|
|
f"Category: {definition.category.name}",
|
|
f"Description: {definition.description}",
|
|
f"Type: {definition.data_type.__name__}",
|
|
f"Required: {definition.required}",
|
|
f"Default: {definition.default_value}"
|
|
]
|
|
|
|
if definition.choices:
|
|
help_text.append(f"Choices: {', '.join(map(str, definition.choices))}")
|
|
if definition.min_value is not None:
|
|
help_text.append(f"Minimum: {definition.min_value}")
|
|
if definition.max_value is not None:
|
|
help_text.append(f"Maximum: {definition.max_value}")
|
|
if definition.depends_on:
|
|
help_text.append(f"Depends on: {definition.depends_on}")
|
|
if definition.error_message:
|
|
help_text.append(f"Error: {definition.error_message}")
|
|
|
|
return "\n".join(help_text)
|