This commit is contained in:
pacnpal
2024-11-16 22:32:08 +00:00
parent b7d99490cf
commit dac21f2fcd
30 changed files with 5854 additions and 2279 deletions

View File

@@ -1,8 +1,14 @@
"""Module for managing VideoArchiver settings"""
from typing import Dict, Any, List, Optional
from dataclasses import dataclass
from enum import Enum
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"""
@@ -17,133 +23,177 @@ class VideoQuality(Enum):
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: str
category: SettingCategory
default_value: Any
description: str
data_type: type
required: bool = True
min_value: Optional[int] = None
max_value: Optional[int] = None
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
class SettingCategory(Enum):
"""Setting categories"""
GENERAL = "general"
CHANNELS = "channels"
PERMISSIONS = "permissions"
VIDEO = "video"
MESSAGES = "messages"
PERFORMANCE = "performance"
FEATURES = "features"
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 = {
SETTINGS: ClassVar[Dict[str, SettingDefinition]] = {
"enabled": SettingDefinition(
name="enabled",
category=SettingCategory.GENERAL.value,
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.value,
category=SettingCategory.CHANNELS,
default_value=None,
description="Channel where archived videos are posted",
data_type=int,
required=False
required=False,
error_message="Archive channel must be a valid channel ID"
),
"log_channel": SettingDefinition(
name="log_channel",
category=SettingCategory.CHANNELS.value,
category=SettingCategory.CHANNELS,
default_value=None,
description="Channel for logging archiver actions",
data_type=int,
required=False
required=False,
error_message="Log channel must be a valid channel ID"
),
"enabled_channels": SettingDefinition(
name="enabled_channels",
category=SettingCategory.CHANNELS.value,
category=SettingCategory.CHANNELS,
default_value=[],
description="Channels to monitor (empty means all channels)",
data_type=list
data_type=list,
error_message="Enabled channels must be a list of valid channel IDs"
),
"allowed_roles": SettingDefinition(
name="allowed_roles",
category=SettingCategory.PERMISSIONS.value,
category=SettingCategory.PERMISSIONS,
default_value=[],
description="Roles allowed to use archiver (empty means all roles)",
data_type=list
data_type=list,
error_message="Allowed roles must be a list of valid role IDs"
),
"video_format": SettingDefinition(
name="video_format",
category=SettingCategory.VIDEO.value,
category=SettingCategory.VIDEO,
default_value=VideoFormat.MP4.value,
description="Format for archived videos",
data_type=str,
choices=[format.value for format in VideoFormat]
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.value,
category=SettingCategory.VIDEO,
default_value=VideoQuality.HIGH.value,
description="Quality preset for archived videos",
data_type=str,
choices=[quality.value for quality in VideoQuality]
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.value,
category=SettingCategory.VIDEO,
default_value=8,
description="Maximum file size in MB",
data_type=int,
min_value=1,
max_value=100
max_value=100,
error_message="Max file size must be between 1 and 100 MB"
),
"message_duration": SettingDefinition(
name="message_duration",
category=SettingCategory.MESSAGES.value,
category=SettingCategory.MESSAGES,
default_value=30,
description="Duration to show status messages (seconds)",
data_type=int,
min_value=5,
max_value=300
max_value=300,
error_message="Message duration must be between 5 and 300 seconds"
),
"message_template": SettingDefinition(
name="message_template",
category=SettingCategory.MESSAGES.value,
category=SettingCategory.MESSAGES,
default_value="{author} archived a video from {channel}",
description="Template for archive messages",
data_type=str
data_type=str,
error_message="Message template must contain {author} and {channel} placeholders"
),
"concurrent_downloads": SettingDefinition(
name="concurrent_downloads",
category=SettingCategory.PERFORMANCE.value,
category=SettingCategory.PERFORMANCE,
default_value=2,
description="Maximum concurrent downloads",
data_type=int,
min_value=1,
max_value=5
max_value=5,
error_message="Concurrent downloads must be between 1 and 5"
),
"enabled_sites": SettingDefinition(
name="enabled_sites",
category=SettingCategory.FEATURES.value,
category=SettingCategory.FEATURES,
default_value=None,
description="Sites to enable archiving for (None means all sites)",
data_type=list,
required=False
required=False,
error_message="Enabled sites must be a list of valid site identifiers"
),
"use_database": SettingDefinition(
name="use_database",
category=SettingCategory.FEATURES.value,
category=SettingCategory.FEATURES,
default_value=False,
description="Enable database tracking of archived videos",
data_type=bool
@@ -152,12 +202,28 @@ class Settings:
@classmethod
def get_setting_definition(cls, setting: str) -> Optional[SettingDefinition]:
"""Get definition for a setting"""
"""
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: str) -> Dict[str, SettingDefinition]:
"""Get all settings in a category"""
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()
@@ -165,36 +231,109 @@ class Settings:
}
@classmethod
def validate_setting(cls, setting: str, value: Any) -> bool:
"""Validate a setting value"""
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:
return False
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 False
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 False
return ValidationResult(
valid=False,
error="Required setting cannot be None",
details=details
)
# Check choices
if definition.choices and value not in definition.choices:
return False
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 False
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 False
return ValidationResult(
valid=False,
error=f"Value must be at most {definition.max_value}",
details=details
)
return True
# 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"""
"""
Default settings for guild configuration.
Returns:
Dictionary of default settings
"""
return {
name: definition.default_value
for name, definition in self.SETTINGS.items()
@@ -202,14 +341,22 @@ class Settings:
@classmethod
def get_setting_help(cls, setting: str) -> Optional[str]:
"""Get help text for a setting"""
"""
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}",
f"Category: {definition.category.name}",
f"Description: {definition.description}",
f"Type: {definition.data_type.__name__}",
f"Required: {definition.required}",
@@ -224,5 +371,7 @@ class Settings:
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)