fixed imports again

This commit is contained in:
pacnpal
2024-11-18 01:21:40 +00:00
parent d03e8dc8e8
commit fc06e54d8a
37 changed files with 879 additions and 882 deletions

View File

@@ -91,34 +91,34 @@ try:
) )
except ImportError: except ImportError:
# Fall back to absolute imports if relative imports fail # Fall back to absolute imports if relative imports fail
from videoarchiver import utils # from videoarchiver import utils
from videoarchiver import processor # from videoarchiver import processor
from videoarchiver import queue # from videoarchiver import queue
from videoarchiver import ffmpeg # from videoarchiver import ffmpeg
from videoarchiver import database # from videoarchiver import database
from videoarchiver import config # from videoarchiver import config
from videoarchiver import core # from videoarchiver import core
# from videoarchiver.core.base import VideoArchiver # from videoarchiver.core.base import VideoArchiver
# from videoarchiver.core.initialization import initialize_cog, init_callback # from videoarchiver.core.initialization import initialize_cog, init_callback
# from videoarchiver.core.cleanup import cleanup_resources # from videoarchiver.core.cleanup import cleanup_resources
# from videoarchiver.utils.exceptions import ( # from videoarchiver.utils.exceptions import (
VideoArchiverError, # VideoArchiverError,
CommandError, # CommandError,
EventError, # EventError,
CogError, # CogError,
ErrorContext, # ErrorContext,
ErrorSeverity, # ErrorSeverity,
ProcessingError, # ProcessingError,
) # )
# Reload all modules # Reload all modules
importlib.reload(utils) importlib.reload(utils)
importlib.reload(processor) importlib.reload(processor)
importlib.reload(queue) importlib.reload(queue)
importlib.reload(ffmpeg) importlib.reload(ffmpeg)
importlib.reload(database) importlib.reload(database)
importlib.reload(config) importlib.reload(config)
importlib.reload(core) importlib.reload(core)
# Import all submodules # Import all submodules
from .database import * from .database import *

View File

@@ -1,8 +1,8 @@
"""Configuration management module""" """Configuration management module"""
try: # try:
# Try relative imports first # Try relative imports first
from .exceptions import ( from .exceptions import (
ConfigurationError, ConfigurationError,
ValidationError, ValidationError,
PermissionError, PermissionError,
@@ -11,39 +11,40 @@ try:
MigrationError, MigrationError,
SchemaError, SchemaError,
DiscordAPIError, DiscordAPIError,
) )
from .channel_manager import ChannelManager from .channel_manager import ChannelManager
from .role_manager import RoleManager from .role_manager import RoleManager
from .settings_formatter import SettingsFormatter from .settings_formatter import SettingsFormatter
from .validation_manager import ValidationManager from .validation_manager import ValidationManager
except ImportError:
# Fall back to absolute imports if relative imports fail # except ImportError:
# from videoarchiver.config.exceptions import ( # Fall back to absolute imports if relative imports fail
ConfigurationError, # from videoarchiver.config.exceptions import (
ValidationError, # ConfigurationError,
PermissionError, # ValidationError,
LoadError, # PermissionError,
SaveError, # LoadError,
MigrationError, # SaveError,
SchemaError, # MigrationError,
DiscordAPIError, # SchemaError,
) # DiscordAPIError,
# from videoarchiver.config.channel_manager import ChannelManager # )
# from videoarchiver.config.role_manager import RoleManager # from videoarchiver.config.channel_manager import ChannelManager
# from videoarchiver.config.settings_formatter import SettingsFormatter # from videoarchiver.config.role_manager import RoleManager
# from videoarchiver.config.validation_manager import ValidationManager # from videoarchiver.config.settings_formatter import SettingsFormatter
# from videoarchiver.config.validation_manager import ValidationManager
__all__ = [ __all__ = [
'ConfigurationError', "ConfigurationError",
'ValidationError', "ValidationError",
'PermissionError', "PermissionError",
'LoadError', "LoadError",
'SaveError', "SaveError",
'MigrationError', "MigrationError",
'SchemaError', "SchemaError",
'DiscordAPIError', "DiscordAPIError",
'ChannelManager', "ChannelManager",
'RoleManager', "RoleManager",
'SettingsFormatter', "SettingsFormatter",
'ValidationManager', "ValidationManager",
] ]

View File

@@ -4,18 +4,19 @@ import logging
from typing import Dict, List, Optional, Tuple from typing import Dict, List, Optional, Tuple
import discord # type: ignore import discord # type: ignore
try: # try:
# Try relative imports first # Try relative imports first
from .exceptions import ( from .exceptions import (
ConfigurationError as ConfigError, ConfigurationError as ConfigError,
DiscordAPIError, DiscordAPIError,
) )
except ImportError:
# Fall back to absolute imports if relative imports fail # except ImportError:
# from videoarchiver.config.exceptions import ( # Fall back to absolute imports if relative imports fail
ConfigurationError as ConfigError, # from videoarchiver.config.exceptions import (
DiscordAPIError, # ConfigurationError as ConfigError,
) # DiscordAPIError,
# )
logger = logging.getLogger("ChannelManager") logger = logging.getLogger("ChannelManager")

View File

@@ -4,12 +4,13 @@ import logging
from typing import Dict, List, Set, Tuple, Optional, Any from typing import Dict, List, Set, Tuple, Optional, Any
import discord # type: ignore import discord # type: ignore
try: # try:
# Try relative imports first # Try relative imports first
from .exceptions import ConfigurationError as ConfigError from .exceptions import ConfigurationError as ConfigError
except ImportError:
# Fall back to absolute imports if relative imports fail # except ImportError:
# from videoarchiver.config.exceptions import ConfigurationError as ConfigError # Fall back to absolute imports if relative imports fail
# from videoarchiver.config.exceptions import ConfigurationError as ConfigError
logger = logging.getLogger("RoleManager") logger = logging.getLogger("RoleManager")
@@ -21,8 +22,7 @@ class RoleManager:
self.config_manager = config_manager self.config_manager = config_manager
async def check_user_roles( async def check_user_roles(
self, self, member: discord.Member
member: discord.Member
) -> Tuple[bool, Optional[str]]: ) -> Tuple[bool, Optional[str]]:
"""Check if user has permission based on allowed roles """Check if user has permission based on allowed roles
@@ -37,8 +37,7 @@ class RoleManager:
""" """
try: try:
allowed_roles = await self.config_manager.get_setting( allowed_roles = await self.config_manager.get_setting(
member.guild.id, member.guild.id, "allowed_roles"
"allowed_roles"
) )
# If no roles are set, allow all users # If no roles are set, allow all users
@@ -53,21 +52,16 @@ class RoleManager:
return True, None return True, None
# Get role names for error message # Get role names for error message
missing_roles = await self._get_role_names( missing_roles = await self._get_role_names(member.guild, allowed_role_set)
member.guild,
allowed_role_set
)
return False, f"Missing required roles: {', '.join(missing_roles)}" return False, f"Missing required roles: {', '.join(missing_roles)}"
except Exception as e: except Exception as e:
logger.error(f"Failed to check roles for user {member.id} in guild {member.guild.id}: {e}") logger.error(
f"Failed to check roles for user {member.id} in guild {member.guild.id}: {e}"
)
raise ConfigError(f"Failed to check user roles: {str(e)}") raise ConfigError(f"Failed to check user roles: {str(e)}")
async def add_allowed_role( async def add_allowed_role(self, guild_id: int, role_id: int) -> None:
self,
guild_id: int,
role_id: int
) -> None:
"""Add a role to allowed roles """Add a role to allowed roles
Args: Args:
@@ -78,20 +72,12 @@ class RoleManager:
ConfigError: If role cannot be added ConfigError: If role cannot be added
""" """
try: try:
await self.config_manager.add_to_list( await self.config_manager.add_to_list(guild_id, "allowed_roles", role_id)
guild_id,
"allowed_roles",
role_id
)
except Exception as e: except Exception as e:
logger.error(f"Failed to add allowed role {role_id}: {e}") logger.error(f"Failed to add allowed role {role_id}: {e}")
raise ConfigError(f"Failed to add allowed role: {str(e)}") raise ConfigError(f"Failed to add allowed role: {str(e)}")
async def remove_allowed_role( async def remove_allowed_role(self, guild_id: int, role_id: int) -> None:
self,
guild_id: int,
role_id: int
) -> None:
"""Remove a role from allowed roles """Remove a role from allowed roles
Args: Args:
@@ -103,18 +89,13 @@ class RoleManager:
""" """
try: try:
await self.config_manager.remove_from_list( await self.config_manager.remove_from_list(
guild_id, guild_id, "allowed_roles", role_id
"allowed_roles",
role_id
) )
except Exception as e: except Exception as e:
logger.error(f"Failed to remove allowed role {role_id}: {e}") logger.error(f"Failed to remove allowed role {role_id}: {e}")
raise ConfigError(f"Failed to remove allowed role: {str(e)}") raise ConfigError(f"Failed to remove allowed role: {str(e)}")
async def get_allowed_roles( async def get_allowed_roles(self, guild: discord.Guild) -> List[discord.Role]:
self,
guild: discord.Guild
) -> List[discord.Role]:
"""Get all allowed roles for a guild """Get all allowed roles for a guild
Args: Args:
@@ -152,9 +133,7 @@ class RoleManager:
raise ConfigError(f"Failed to get allowed roles: {str(e)}") raise ConfigError(f"Failed to get allowed roles: {str(e)}")
async def verify_role_hierarchy( async def verify_role_hierarchy(
self, self, guild: discord.Guild, role: discord.Role
guild: discord.Guild,
role: discord.Role
) -> Tuple[bool, Optional[str]]: ) -> Tuple[bool, Optional[str]]:
"""Verify bot's role hierarchy position for managing a role """Verify bot's role hierarchy position for managing a role
@@ -170,7 +149,10 @@ class RoleManager:
bot_top_role = bot_member.top_role bot_top_role = bot_member.top_role
if role >= bot_top_role: if role >= bot_top_role:
return False, f"Role {role.name} is higher than or equal to bot's highest role" return (
False,
f"Role {role.name} is higher than or equal to bot's highest role",
)
return True, None return True, None
@@ -179,9 +161,7 @@ class RoleManager:
return False, "Failed to check role hierarchy" return False, "Failed to check role hierarchy"
async def _get_role_names( async def _get_role_names(
self, self, guild: discord.Guild, role_ids: Set[int]
guild: discord.Guild,
role_ids: Set[int]
) -> List[str]: ) -> List[str]:
"""Get role names from role IDs """Get role names from role IDs
@@ -199,11 +179,7 @@ class RoleManager:
role_names.append(role.name) role_names.append(role.name)
return role_names return role_names
async def _remove_invalid_roles( async def _remove_invalid_roles(self, guild_id: int, role_ids: List[int]) -> None:
self,
guild_id: int,
role_ids: List[int]
) -> None:
"""Remove invalid roles from allowed roles """Remove invalid roles from allowed roles
Args: Args:
@@ -216,10 +192,7 @@ class RoleManager:
except Exception as e: except Exception as e:
logger.error(f"Error removing invalid roles: {e}") logger.error(f"Error removing invalid roles: {e}")
async def get_role_info( async def get_role_info(self, guild: discord.Guild) -> Dict[str, Any]:
self,
guild: discord.Guild
) -> Dict[str, Any]:
"""Get role configuration information """Get role configuration information
Args: Args:
@@ -233,16 +206,16 @@ class RoleManager:
bot_member = guild.me bot_member = guild.me
return { return {
'allowed_roles': allowed_roles, "allowed_roles": allowed_roles,
'bot_top_role': bot_member.top_role, "bot_top_role": bot_member.top_role,
'bot_permissions': bot_member.guild_permissions, "bot_permissions": bot_member.guild_permissions,
'role_count': len(allowed_roles) "role_count": len(allowed_roles),
} }
except Exception as e: except Exception as e:
logger.error(f"Error getting role info: {e}") logger.error(f"Error getting role info: {e}")
return { return {
'allowed_roles': [], "allowed_roles": [],
'bot_top_role': None, "bot_top_role": None,
'bot_permissions': None, "bot_permissions": None,
'role_count': 0 "role_count": 0,
} }

View File

@@ -5,12 +5,13 @@ from typing import Dict, Any, List
from datetime import datetime from datetime import datetime
import discord # type: ignore import discord # type: ignore
try: # try:
# Try relative imports first # Try relative imports first
from .exceptions import ConfigurationError as ConfigError from .exceptions import ConfigurationError as ConfigError
except ImportError:
# Fall back to absolute imports if relative imports fail # except ImportError:
# from videoarchiver.config.exceptions import ConfigurationError as ConfigError # Fall back to absolute imports if relative imports fail
# from videoarchiver.config.exceptions import ConfigurationError as ConfigError
logger = logging.getLogger("SettingsFormatter") logger = logging.getLogger("SettingsFormatter")
@@ -22,9 +23,7 @@ class SettingsFormatter:
self.embed_color = discord.Color.blue() self.embed_color = discord.Color.blue()
async def format_settings_embed( async def format_settings_embed(
self, self, guild: discord.Guild, settings: Dict[str, Any]
guild: discord.Guild,
settings: Dict[str, Any]
) -> discord.Embed: ) -> discord.Embed:
"""Format guild settings into a Discord embed """Format guild settings into a Discord embed
@@ -42,7 +41,7 @@ class SettingsFormatter:
embed = discord.Embed( embed = discord.Embed(
title="Video Archiver Settings", title="Video Archiver Settings",
color=self.embed_color, color=self.embed_color,
timestamp=datetime.utcnow() timestamp=datetime.utcnow(),
) )
# Add sections # Add sections
@@ -61,27 +60,23 @@ class SettingsFormatter:
raise ConfigError(f"Failed to format settings: {str(e)}") raise ConfigError(f"Failed to format settings: {str(e)}")
async def _add_core_settings( async def _add_core_settings(
self, self, embed: discord.Embed, guild: discord.Guild, settings: Dict[str, Any]
embed: discord.Embed,
guild: discord.Guild,
settings: Dict[str, Any]
) -> None: ) -> None:
"""Add core settings to embed""" """Add core settings to embed"""
embed.add_field( embed.add_field(
name="Core Settings", name="Core Settings",
value="\n".join([ value="\n".join(
[
f"**Enabled:** {settings['enabled']}", f"**Enabled:** {settings['enabled']}",
f"**Database Enabled:** {settings['use_database']}", f"**Database Enabled:** {settings['use_database']}",
f"**Update Check Disabled:** {settings['disable_update_check']}" f"**Update Check Disabled:** {settings['disable_update_check']}",
]), ]
inline=False ),
inline=False,
) )
async def _add_channel_settings( async def _add_channel_settings(
self, self, embed: discord.Embed, guild: discord.Guild, settings: Dict[str, Any]
embed: discord.Embed,
guild: discord.Guild,
settings: Dict[str, Any]
) -> None: ) -> None:
"""Add channel settings to embed""" """Add channel settings to embed"""
# Get channels with error handling # Get channels with error handling
@@ -89,20 +84,19 @@ class SettingsFormatter:
embed.add_field( embed.add_field(
name="Channel Settings", name="Channel Settings",
value="\n".join([ value="\n".join(
[
f"**Archive Channel:** {channels['archive']}", f"**Archive Channel:** {channels['archive']}",
f"**Notification Channel:** {channels['notification']}", f"**Notification Channel:** {channels['notification']}",
f"**Log Channel:** {channels['log']}", f"**Log Channel:** {channels['log']}",
f"**Monitored Channels:**\n{channels['monitored']}" f"**Monitored Channels:**\n{channels['monitored']}",
]), ]
inline=False ),
inline=False,
) )
async def _add_permission_settings( async def _add_permission_settings(
self, self, embed: discord.Embed, guild: discord.Guild, settings: Dict[str, Any]
embed: discord.Embed,
guild: discord.Guild,
settings: Dict[str, Any]
) -> None: ) -> None:
"""Add permission settings to embed""" """Add permission settings to embed"""
allowed_roles = await self._get_role_names(guild, settings["allowed_roles"]) allowed_roles = await self._get_role_names(guild, settings["allowed_roles"])
@@ -110,62 +104,54 @@ class SettingsFormatter:
embed.add_field( embed.add_field(
name="Permission Settings", name="Permission Settings",
value=f"**Allowed Roles:**\n{allowed_roles}", value=f"**Allowed Roles:**\n{allowed_roles}",
inline=False inline=False,
) )
async def _add_video_settings( async def _add_video_settings(
self, self, embed: discord.Embed, settings: Dict[str, Any]
embed: discord.Embed,
settings: Dict[str, Any]
) -> None: ) -> None:
"""Add video settings to embed""" """Add video settings to embed"""
embed.add_field( embed.add_field(
name="Video Settings", name="Video Settings",
value="\n".join([ value="\n".join(
[
f"**Format:** {settings['video_format']}", f"**Format:** {settings['video_format']}",
f"**Max Quality:** {settings['video_quality']}p", f"**Max Quality:** {settings['video_quality']}p",
f"**Max File Size:** {settings['max_file_size']}MB" f"**Max File Size:** {settings['max_file_size']}MB",
]), ]
inline=False ),
inline=False,
) )
async def _add_operation_settings( async def _add_operation_settings(
self, self, embed: discord.Embed, settings: Dict[str, Any]
embed: discord.Embed,
settings: Dict[str, Any]
) -> None: ) -> None:
"""Add operation settings to embed""" """Add operation settings to embed"""
embed.add_field( embed.add_field(
name="Operation Settings", name="Operation Settings",
value="\n".join([ value="\n".join(
[
f"**Delete After Repost:** {settings['delete_after_repost']}", f"**Delete After Repost:** {settings['delete_after_repost']}",
f"**Message Duration:** {settings['message_duration']} hours", f"**Message Duration:** {settings['message_duration']} hours",
f"**Concurrent Downloads:** {settings['concurrent_downloads']}", f"**Concurrent Downloads:** {settings['concurrent_downloads']}",
f"**Max Retries:** {settings['max_retries']}", f"**Max Retries:** {settings['max_retries']}",
f"**Retry Delay:** {settings['retry_delay']}s" f"**Retry Delay:** {settings['retry_delay']}s",
]), ]
inline=False ),
inline=False,
) )
async def _add_site_settings( async def _add_site_settings(
self, self, embed: discord.Embed, settings: Dict[str, Any]
embed: discord.Embed,
settings: Dict[str, Any]
) -> None: ) -> None:
"""Add site settings to embed""" """Add site settings to embed"""
enabled_sites = settings["enabled_sites"] enabled_sites = settings["enabled_sites"]
sites_text = ", ".join(enabled_sites) if enabled_sites else "All sites" sites_text = ", ".join(enabled_sites) if enabled_sites else "All sites"
embed.add_field( embed.add_field(name="Enabled Sites", value=sites_text, inline=False)
name="Enabled Sites",
value=sites_text,
inline=False
)
async def _get_channel_mentions( async def _get_channel_mentions(
self, self, guild: discord.Guild, settings: Dict[str, Any]
guild: discord.Guild,
settings: Dict[str, Any]
) -> Dict[str, str]: ) -> Dict[str, str]:
"""Get channel mentions with error handling""" """Get channel mentions with error handling"""
try: try:
@@ -183,9 +169,17 @@ class SettingsFormatter:
return { return {
"archive": archive_channel.mention if archive_channel else "Not set", "archive": archive_channel.mention if archive_channel else "Not set",
"notification": notification_channel.mention if notification_channel else "Same as archive", "notification": (
notification_channel.mention
if notification_channel
else "Same as archive"
),
"log": log_channel.mention if log_channel else "Not set", "log": log_channel.mention if log_channel else "Not set",
"monitored": "\n".join(monitored_channels) if monitored_channels else "All channels" "monitored": (
"\n".join(monitored_channels)
if monitored_channels
else "All channels"
),
} }
except Exception as e: except Exception as e:
@@ -194,14 +188,10 @@ class SettingsFormatter:
"archive": "Error", "archive": "Error",
"notification": "Error", "notification": "Error",
"log": "Error", "log": "Error",
"monitored": "Error getting channels" "monitored": "Error getting channels",
} }
async def _get_role_names( async def _get_role_names(self, guild: discord.Guild, role_ids: List[int]) -> str:
self,
guild: discord.Guild,
role_ids: List[int]
) -> str:
"""Get role names with error handling""" """Get role names with error handling"""
try: try:
role_names = [] role_names = []
@@ -210,7 +200,9 @@ class SettingsFormatter:
if role: if role:
role_names.append(role.name) role_names.append(role.name)
return ", ".join(role_names) if role_names else "All roles (no restrictions)" return (
", ".join(role_names) if role_names else "All roles (no restrictions)"
)
except Exception as e: except Exception as e:
logger.error(f"Error getting role names: {e}") logger.error(f"Error getting role names: {e}")

View File

@@ -3,12 +3,13 @@
import logging import logging
from typing import Any, Dict, List, Union from typing import Any, Dict, List, Union
try: # try:
# Try relative imports first # Try relative imports first
from .exceptions import ConfigurationError as ConfigError from .exceptions import ConfigurationError as ConfigError
except ImportError:
# Fall back to absolute imports if relative imports fail # except ImportError:
# from videoarchiver.config.exceptions import ConfigurationError as ConfigError # Fall back to absolute imports if relative imports fail
# from videoarchiver.config.exceptions import ConfigurationError as ConfigError
logger = logging.getLogger("ConfigValidation") logger = logging.getLogger("ConfigValidation")
@@ -72,7 +73,9 @@ class ValidationManager:
def _validate_concurrent_downloads(self, value: int) -> None: def _validate_concurrent_downloads(self, value: int) -> None:
"""Validate concurrent downloads setting""" """Validate concurrent downloads setting"""
if not isinstance(value, int) or not (1 <= value <= self.MAX_CONCURRENT_DOWNLOADS): if not isinstance(value, int) or not (
1 <= value <= self.MAX_CONCURRENT_DOWNLOADS
):
raise ConfigError( raise ConfigError(
f"Concurrent downloads must be between 1 and {self.MAX_CONCURRENT_DOWNLOADS}" f"Concurrent downloads must be between 1 and {self.MAX_CONCURRENT_DOWNLOADS}"
) )
@@ -87,9 +90,7 @@ class ValidationManager:
def _validate_max_retries(self, value: int) -> None: def _validate_max_retries(self, value: int) -> None:
"""Validate max retries setting""" """Validate max retries setting"""
if not isinstance(value, int) or not (0 <= value <= self.MAX_RETRIES): if not isinstance(value, int) or not (0 <= value <= self.MAX_RETRIES):
raise ConfigError( raise ConfigError(f"Max retries must be between 0 and {self.MAX_RETRIES}")
f"Max retries must be between 0 and {self.MAX_RETRIES}"
)
def _validate_retry_delay(self, value: int) -> None: def _validate_retry_delay(self, value: int) -> None:
"""Validate retry delay setting""" """Validate retry delay setting"""
@@ -124,7 +125,12 @@ class ValidationManager:
if setting.endswith("_channel") and value is not None: if setting.endswith("_channel") and value is not None:
if not isinstance(value, int): if not isinstance(value, int):
raise ConfigError(f"{setting} must be a channel ID (int) or None") raise ConfigError(f"{setting} must be a channel ID (int) or None")
elif setting in ["enabled", "delete_after_repost", "disable_update_check", "use_database"]: elif setting in [
"enabled",
"delete_after_repost",
"disable_update_check",
"use_database",
]:
self._validate_boolean(value) self._validate_boolean(value)
elif setting in ["monitored_channels", "allowed_roles", "enabled_sites"]: elif setting in ["monitored_channels", "allowed_roles", "enabled_sites"]:
self._validate_list(value) self._validate_list(value)

View File

@@ -6,20 +6,21 @@ from typing import Dict, Any, Optional, List, Union
import discord # type: ignore import discord # type: ignore
from redbot.core import Config # type: ignore from redbot.core import Config # type: ignore
try: # try:
# Try relative imports first # Try relative imports first
from .config.validation_manager import ValidationManager from .config.validation_manager import ValidationManager
from .config.settings_formatter import SettingsFormatter from .config.settings_formatter import SettingsFormatter
from .config.channel_manager import ChannelManager from .config.channel_manager import ChannelManager
from .config.role_manager import RoleManager from .config.role_manager import RoleManager
from .utils.exceptions import ConfigurationError as ConfigError from .utils.exceptions import ConfigurationError as ConfigError
except ImportError:
# Fall back to absolute imports if relative imports fail # except ImportError:
# from videoarchiver.config.validation_manager import ValidationManager # Fall back to absolute imports if relative imports fail
# from videoarchiver.config.settings_formatter import SettingsFormatter # from videoarchiver.config.validation_manager import ValidationManager
# from videoarchiver.config.channel_manager import ChannelManager # from videoarchiver.config.settings_formatter import SettingsFormatter
# from videoarchiver.config.role_manager import RoleManager # from videoarchiver.config.channel_manager import ChannelManager
# from videoarchiver.utils.exceptions import ConfigurationError as ConfigError # from videoarchiver.config.role_manager import RoleManager
# from videoarchiver.utils.exceptions import ConfigurationError as ConfigError
logger = logging.getLogger("VideoArchiver") logger = logging.getLogger("VideoArchiver")
@@ -80,12 +81,7 @@ class ConfigManager:
logger.error(f"Failed to get guild settings for {guild_id}: {e}") logger.error(f"Failed to get guild settings for {guild_id}: {e}")
raise ConfigError(f"Failed to get guild settings: {str(e)}") raise ConfigError(f"Failed to get guild settings: {str(e)}")
async def update_setting( async def update_setting(self, guild_id: int, setting: str, value: Any) -> None:
self,
guild_id: int,
setting: str,
value: Any
) -> None:
"""Update a specific setting for a guild""" """Update a specific setting for a guild"""
try: try:
if setting not in self.default_guild: if setting not in self.default_guild:
@@ -98,14 +94,12 @@ class ConfigManager:
await self.config.guild_from_id(guild_id).set_raw(setting, value=value) await self.config.guild_from_id(guild_id).set_raw(setting, value=value)
except Exception as e: except Exception as e:
logger.error(f"Failed to update setting {setting} for guild {guild_id}: {e}") logger.error(
f"Failed to update setting {setting} for guild {guild_id}: {e}"
)
raise ConfigError(f"Failed to update setting: {str(e)}") raise ConfigError(f"Failed to update setting: {str(e)}")
async def get_setting( async def get_setting(self, guild_id: int, setting: str) -> Any:
self,
guild_id: int,
setting: str
) -> Any:
"""Get a specific setting for a guild""" """Get a specific setting for a guild"""
try: try:
if setting not in self.default_guild: if setting not in self.default_guild:
@@ -118,11 +112,7 @@ class ConfigManager:
logger.error(f"Failed to get setting {setting} for guild {guild_id}: {e}") logger.error(f"Failed to get setting {setting} for guild {guild_id}: {e}")
raise ConfigError(f"Failed to get setting: {str(e)}") raise ConfigError(f"Failed to get setting: {str(e)}")
async def toggle_setting( async def toggle_setting(self, guild_id: int, setting: str) -> bool:
self,
guild_id: int,
setting: str
) -> bool:
"""Toggle a boolean setting for a guild""" """Toggle a boolean setting for a guild"""
try: try:
if setting not in self.default_guild: if setting not in self.default_guild:
@@ -137,22 +127,21 @@ class ConfigManager:
return not current return not current
except Exception as e: except Exception as e:
logger.error(f"Failed to toggle setting {setting} for guild {guild_id}: {e}") logger.error(
f"Failed to toggle setting {setting} for guild {guild_id}: {e}"
)
raise ConfigError(f"Failed to toggle setting: {str(e)}") raise ConfigError(f"Failed to toggle setting: {str(e)}")
async def add_to_list( async def add_to_list(self, guild_id: int, setting: str, value: Any) -> None:
self,
guild_id: int,
setting: str,
value: Any
) -> None:
"""Add a value to a list setting""" """Add a value to a list setting"""
try: try:
if setting not in self.default_guild: if setting not in self.default_guild:
raise ConfigError(f"Invalid setting: {setting}") raise ConfigError(f"Invalid setting: {setting}")
async with await self._get_guild_lock(guild_id): async with await self._get_guild_lock(guild_id):
async with self.config.guild_from_id(guild_id).get_attr(setting)() as items: async with self.config.guild_from_id(guild_id).get_attr(
setting
)() as items:
if not isinstance(items, list): if not isinstance(items, list):
raise ConfigError(f"Setting {setting} is not a list") raise ConfigError(f"Setting {setting} is not a list")
if value not in items: if value not in items:
@@ -162,26 +151,25 @@ class ConfigManager:
logger.error(f"Failed to add to list {setting} for guild {guild_id}: {e}") logger.error(f"Failed to add to list {setting} for guild {guild_id}: {e}")
raise ConfigError(f"Failed to add to list: {str(e)}") raise ConfigError(f"Failed to add to list: {str(e)}")
async def remove_from_list( async def remove_from_list(self, guild_id: int, setting: str, value: Any) -> None:
self,
guild_id: int,
setting: str,
value: Any
) -> None:
"""Remove a value from a list setting""" """Remove a value from a list setting"""
try: try:
if setting not in self.default_guild: if setting not in self.default_guild:
raise ConfigError(f"Invalid setting: {setting}") raise ConfigError(f"Invalid setting: {setting}")
async with await self._get_guild_lock(guild_id): async with await self._get_guild_lock(guild_id):
async with self.config.guild_from_id(guild_id).get_attr(setting)() as items: async with self.config.guild_from_id(guild_id).get_attr(
setting
)() as items:
if not isinstance(items, list): if not isinstance(items, list):
raise ConfigError(f"Setting {setting} is not a list") raise ConfigError(f"Setting {setting} is not a list")
if value in items: if value in items:
items.remove(value) items.remove(value)
except Exception as e: except Exception as e:
logger.error(f"Failed to remove from list {setting} for guild {guild_id}: {e}") logger.error(
f"Failed to remove from list {setting} for guild {guild_id}: {e}"
)
raise ConfigError(f"Failed to remove from list: {str(e)}") raise ConfigError(f"Failed to remove from list: {str(e)}")
async def format_settings_embed(self, guild: discord.Guild) -> discord.Embed: async def format_settings_embed(self, guild: discord.Guild) -> discord.Embed:
@@ -194,11 +182,15 @@ class ConfigManager:
raise ConfigError(f"Failed to format settings: {str(e)}") raise ConfigError(f"Failed to format settings: {str(e)}")
# Channel management delegated to channel_manager # Channel management delegated to channel_manager
async def get_channel(self, guild: discord.Guild, channel_type: str) -> Optional[discord.TextChannel]: async def get_channel(
self, guild: discord.Guild, channel_type: str
) -> Optional[discord.TextChannel]:
"""Get a channel by type""" """Get a channel by type"""
return await self.channel_manager.get_channel(guild, channel_type) return await self.channel_manager.get_channel(guild, channel_type)
async def get_monitored_channels(self, guild: discord.Guild) -> List[discord.TextChannel]: async def get_monitored_channels(
self, guild: discord.Guild
) -> List[discord.TextChannel]:
"""Get all monitored channels for a guild""" """Get all monitored channels for a guild"""
return await self.channel_manager.get_monitored_channels(guild) return await self.channel_manager.get_monitored_channels(guild)

View File

@@ -1,10 +1,11 @@
"""Core module for VideoArchiver cog""" """Core module for VideoArchiver cog"""
try: # try:
# Try relative imports first # Try relative imports first
from .base import VideoArchiver from .base import VideoArchiver
except ImportError:
# Fall back to absolute imports if relative imports fail # except ImportError:
# from videoarchiver.core.base import VideoArchiver # Fall back to absolute imports if relative imports fail
# from videoarchiver.core.base import VideoArchiver
__all__ = ["VideoArchiver"] __all__ = ["VideoArchiver"]

View File

@@ -12,40 +12,41 @@ import discord # type: ignore
from redbot.core.bot import Red # type: ignore from redbot.core.bot import Red # type: ignore
from redbot.core.commands import GroupCog, Context # type: ignore from redbot.core.commands import GroupCog, Context # type: ignore
try: # try:
# Try relative imports first # Try relative imports first
from .settings import Settings from .settings import Settings
from .lifecycle import LifecycleManager, LifecycleState from .lifecycle import LifecycleManager, LifecycleState
from .component_manager import ComponentManager, ComponentState from .component_manager import ComponentManager, ComponentState
from .error_handler import error_manager, handle_command_error from .error_handler import error_manager, handle_command_error
from .response_handler import ResponseManager from .response_handler import ResponseManager
from .commands.archiver_commands import setup_archiver_commands from .commands.archiver_commands import setup_archiver_commands
from .commands.database_commands import setup_database_commands from .commands.database_commands import setup_database_commands
from .commands.settings_commands import setup_settings_commands from .commands.settings_commands import setup_settings_commands
from .events import setup_events, EventManager from .events import setup_events, EventManager
from ..processor.core import VideoProcessor from ..processor.core import VideoProcessor
from ..queue.manager import EnhancedVideoQueueManager from ..queue.manager import EnhancedVideoQueueManager
from ..ffmpeg.ffmpeg_manager import FFmpegManager from ..ffmpeg.ffmpeg_manager import FFmpegManager
from ..database.video_archive_db import VideoArchiveDB from ..database.video_archive_db import VideoArchiveDB
from ..config_manager import ConfigManager from ..config_manager import ConfigManager
from ..utils.exceptions import CogError, ErrorContext, ErrorSeverity from ..utils.exceptions import CogError, ErrorContext, ErrorSeverity
except ImportError:
# Fall back to absolute imports if relative imports fail # except ImportError:
# from videoarchiver.core.settings import Settings # Fall back to absolute imports if relative imports fail
# from videoarchiver.core.lifecycle import LifecycleManager, LifecycleState # from videoarchiver.core.settings import Settings
# from videoarchiver.core.component_manager import ComponentManager, ComponentState # from videoarchiver.core.lifecycle import LifecycleManager, LifecycleState
# from videoarchiver.core.error_handler import error_manager, handle_command_error # from videoarchiver.core.component_manager import ComponentManager, ComponentState
# from videoarchiver.core.response_handler import ResponseManager # from videoarchiver.core.error_handler import error_manager, handle_command_error
# from videoarchiver.core.commands.archiver_commands import setup_archiver_commands # from videoarchiver.core.response_handler import ResponseManager
# from videoarchiver.core.commands.database_commands import setup_database_commands # from videoarchiver.core.commands.archiver_commands import setup_archiver_commands
# from videoarchiver.core.commands.settings_commands import setup_settings_commands # from videoarchiver.core.commands.database_commands import setup_database_commands
# from videoarchiver.core.events import setup_events, EventManager # from videoarchiver.core.commands.settings_commands import setup_settings_commands
# from videoarchiver.processor.core import VideoProcessor # from videoarchiver.core.events import setup_events, EventManager
# from videoarchiver.queue.manager import EnhancedVideoQueueManager # from videoarchiver.processor.core import VideoProcessor
# from videoarchiver.ffmpeg.ffmpeg_manager import FFmpegManager # from videoarchiver.queue.manager import EnhancedVideoQueueManager
# from videoarchiver.database.video_archive_db import VideoArchiveDB # from videoarchiver.ffmpeg.ffmpeg_manager import FFmpegManager
# from videoarchiver.config_manager import ConfigManager # from videoarchiver.database.video_archive_db import VideoArchiveDB
# from videoarchiver.utils.exceptions import CogError, ErrorContext, ErrorSeverity # from videoarchiver.config_manager import ConfigManager
# from videoarchiver.utils.exceptions import CogError, ErrorContext, ErrorSeverity
logger = logging.getLogger("VideoArchiver") logger = logging.getLogger("VideoArchiver")

View File

@@ -8,19 +8,19 @@ from enum import Enum, auto
from pathlib import Path from pathlib import Path
from typing import TYPE_CHECKING, Dict, Any, Optional, TypedDict, ClassVar from typing import TYPE_CHECKING, Dict, Any, Optional, TypedDict, ClassVar
try: #try:
# Try relative imports first # Try relative imports first
from ..utils.file_ops import cleanup_downloads from ..utils.file_ops import cleanup_downloads
from ..utils.exceptions import CleanupError, ErrorContext, ErrorSeverity from ..utils.exceptions import CleanupError, ErrorContext, ErrorSeverity
except ImportError: #except ImportError:
# Fall back to absolute imports if relative imports fail # Fall back to absolute imports if relative imports fail
# from videoarchiver.utils.file_ops import cleanup_downloads # from videoarchiver.utils.file_ops import cleanup_downloads
# from videoarchiver.utils.exceptions import CleanupError, ErrorContext, ErrorSeverity # from videoarchiver.utils.exceptions import CleanupError, ErrorContext, ErrorSeverity
if TYPE_CHECKING: if TYPE_CHECKING:
try: #try:
from .base import VideoArchiver from .base import VideoArchiver
except ImportError: #except ImportError:
# from videoarchiver.core.base import VideoArchiver # from videoarchiver.core.base import VideoArchiver
logger = logging.getLogger("VideoArchiver") logger = logging.getLogger("VideoArchiver")

View File

@@ -6,9 +6,9 @@
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
try: # try:
from .base import VideoArchiver from .base import VideoArchiver
except ImportError: # except ImportError:
# from videoarchiver.core.base import VideoArchiver # from videoarchiver.core.base import VideoArchiver
def setup_commands(cog: "VideoArchiver") -> None: def setup_commands(cog: "VideoArchiver") -> None:

View File

@@ -20,22 +20,23 @@ from datetime import datetime
from pathlib import Path from pathlib import Path
import importlib import importlib
try: # try:
# Try relative imports first # Try relative imports first
from ..utils.exceptions import ComponentError, ErrorContext, ErrorSeverity from ..utils.exceptions import ComponentError, ErrorContext, ErrorSeverity
from ..utils.path_manager import PathManager from ..utils.path_manager import PathManager
from ..config_manager import ConfigManager from ..config_manager import ConfigManager
from ..processor.core import VideoProcessor from ..processor.core import VideoProcessor
from ..queue.manager import EnhancedVideoQueueManager from ..queue.manager import EnhancedVideoQueueManager
from ..ffmpeg.ffmpeg_manager import FFmpegManager from ..ffmpeg.ffmpeg_manager import FFmpegManager
except ImportError:
# Fall back to absolute imports if relative imports fail # except ImportError:
# from videoarchiver.utils.exceptions import ComponentError, ErrorContext, ErrorSeverity # Fall back to absolute imports if relative imports fail
# from videoarchiver.utils.path_manager import PathManager # from videoarchiver.utils.exceptions import ComponentError, ErrorContext, ErrorSeverity
# from videoarchiver.config_manager import ConfigManager # from videoarchiver.utils.path_manager import PathManager
# from videoarchiver.processor.core import VideoProcessor # from videoarchiver.config_manager import ConfigManager
# from videoarchiver.queue.manager import EnhancedVideoQueueManager # from videoarchiver.processor.core import VideoProcessor
# from videoarchiver.ffmpeg.ffmpeg_manager import FFmpegManager # from videoarchiver.queue.manager import EnhancedVideoQueueManager
# from videoarchiver.ffmpeg.ffmpeg_manager import FFmpegManager
logger = logging.getLogger("VideoArchiver") logger = logging.getLogger("VideoArchiver")

View File

@@ -14,9 +14,9 @@ from redbot.core.commands import ( # type: ignore
CommandError CommandError
) )
try: #try:
# Try relative imports first # Try relative imports first
from ..utils.exceptions import ( from ..utils.exceptions import (
VideoArchiverError, VideoArchiverError,
ErrorSeverity, ErrorSeverity,
ErrorContext, ErrorContext,
@@ -34,29 +34,29 @@ try:
NetworkError, NetworkError,
ResourceExhaustedError, ResourceExhaustedError,
ConfigurationError ConfigurationError
) )
from ..core.response_handler import response_manager from ..core.response_handler import response_manager
except ImportError: # except ImportError:
# Fall back to absolute imports if relative imports fail # # Fall back to absolute imports if relative imports fail
# from videoarchiver.utils.exceptions import ( # # from videoarchiver.utils.exceptions import (
VideoArchiverError, # VideoArchiverError,
ErrorSeverity, # ErrorSeverity,
ErrorContext, # ErrorContext,
ProcessorError, # ProcessorError,
ValidationError, # ValidationError,
DisplayError, # DisplayError,
URLExtractionError, # URLExtractionError,
MessageHandlerError, # MessageHandlerError,
QueueHandlerError, # QueueHandlerError,
QueueProcessorError, # QueueProcessorError,
FFmpegError, # FFmpegError,
DatabaseError, # DatabaseError,
HealthCheckError, # HealthCheckError,
TrackingError, # TrackingError,
NetworkError, # NetworkError,
ResourceExhaustedError, # ResourceExhaustedError,
ConfigurationError # ConfigurationError
) # )
# from videoarchiver.core.response_handler import response_manager # from videoarchiver.core.response_handler import response_manager
logger = logging.getLogger("VideoArchiver") logger = logging.getLogger("VideoArchiver")

View File

@@ -9,15 +9,15 @@ from typing import TYPE_CHECKING, Dict, Any, Optional, TypedDict, ClassVar, List
import discord # type: ignore import discord # type: ignore
try: #try:
# Try relative imports first # Try relative imports first
from ..processor.constants import REACTIONS from ..processor.constants import REACTIONS
from ..processor.reactions import handle_archived_reaction from ..processor.reactions import handle_archived_reaction
from .guild import initialize_guild_components, cleanup_guild_components from .guild import initialize_guild_components, cleanup_guild_components
from .error_handler import ErrorManager from .error_handler import ErrorManager
from .response_handler import response_manager from .response_handler import response_manager
from ..utils.exceptions import EventError, ErrorContext, ErrorSeverity from ..utils.exceptions import EventError, ErrorContext, ErrorSeverity
except ImportError: #except ImportError:
# Fall back to absolute imports if relative imports fail # Fall back to absolute imports if relative imports fail
# from videoarchiver.processor.constants import REACTIONS # from videoarchiver.processor.constants import REACTIONS
# from videoarchiver.processor.reactions import handle_archived_reaction # from videoarchiver.processor.reactions import handle_archived_reaction
@@ -27,9 +27,9 @@ except ImportError:
# from videoarchiver.utils.exceptions import EventError, ErrorContext, ErrorSeverity # from videoarchiver.utils.exceptions import EventError, ErrorContext, ErrorSeverity
if TYPE_CHECKING: if TYPE_CHECKING:
try: #try:
from .base import VideoArchiver from .base import VideoArchiver
except ImportError: # except ImportError:
# from videoarchiver.core.base import VideoArchiver # from videoarchiver.core.base import VideoArchiver
logger = logging.getLogger("VideoArchiver") logger = logging.getLogger("VideoArchiver")

View File

@@ -4,24 +4,25 @@ import logging
from pathlib import Path from pathlib import Path
from typing import TYPE_CHECKING, Dict, Any, Optional from typing import TYPE_CHECKING, Dict, Any, Optional
try: # try:
# Try relative imports first # Try relative imports first
from ..utils.download_core import DownloadCore from ..utils.download_core import DownloadCore
from ..utils.message_manager import MessageManager from ..utils.message_manager import MessageManager
from ..utils.file_ops import cleanup_downloads from ..utils.file_ops import cleanup_downloads
from ..utils.exceptions import VideoArchiverError as ProcessingError from ..utils.exceptions import VideoArchiverError as ProcessingError
except ImportError:
# Fall back to absolute imports if relative imports fail # except ImportError:
# from videoarchiver.utils.download_core import DownloadCore # Fall back to absolute imports if relative imports fail
# from videoarchiver.utils.message_manager import MessageManager # from videoarchiver.utils.download_core import DownloadCore
# from videoarchiver.utils.file_ops import cleanup_downloads # from videoarchiver.utils.message_manager import MessageManager
# from videoarchiver.utils.exceptions import VideoArchiverError as ProcessingError # from videoarchiver.utils.file_ops import cleanup_downloads
# from videoarchiver.utils.exceptions import VideoArchiverError as ProcessingError
if TYPE_CHECKING: if TYPE_CHECKING:
try: # try:
from .base import VideoArchiver from .base import VideoArchiver
except ImportError: # except ImportError:
# from videoarchiver.core.base import VideoArchiver # from videoarchiver.core.base import VideoArchiver
logger = logging.getLogger("VideoArchiver") logger = logging.getLogger("VideoArchiver")

View File

@@ -4,20 +4,21 @@ from typing import TYPE_CHECKING, Optional, Dict, Any
import asyncio import asyncio
import logging import logging
try: # try:
# Try relative imports first # Try relative imports first
from ..utils.exceptions import ComponentError, ErrorContext, ErrorSeverity from ..utils.exceptions import ComponentError, ErrorContext, ErrorSeverity
from .lifecycle import LifecycleState from .lifecycle import LifecycleState
except ImportError:
# Fall back to absolute imports if relative imports fail # except ImportError:
# from videoarchiver.utils.exceptions import ComponentError, ErrorContext, ErrorSeverity # Fall back to absolute imports if relative imports fail
# from videoarchiver.core.lifecycle import LifecycleState # from videoarchiver.utils.exceptions import ComponentError, ErrorContext, ErrorSeverity
# from videoarchiver.core.lifecycle import LifecycleState
if TYPE_CHECKING: if TYPE_CHECKING:
try: # try:
from .base import VideoArchiver from .base import VideoArchiver
except ImportError: # except ImportError:
# from videoarchiver.core.base import VideoArchiver # from videoarchiver.core.base import VideoArchiver
logger = logging.getLogger("VideoArchiver") logger = logging.getLogger("VideoArchiver")
@@ -48,8 +49,8 @@ async def initialize_cog(cog: "VideoArchiver") -> None:
"Initialization", "Initialization",
"initialize_cog", "initialize_cog",
{"state": cog.lifecycle_manager.state_tracker.state.name}, {"state": cog.lifecycle_manager.state_tracker.state.name},
ErrorSeverity.HIGH ErrorSeverity.HIGH,
) ),
) )
@@ -103,8 +104,8 @@ def get_init_status(cog: "VideoArchiver") -> Dict[str, Any]:
"update_checker", "update_checker",
"ffmpeg_mgr", "ffmpeg_mgr",
"components", "components",
"db" "db",
] ]
), ),
"history": cog.lifecycle_manager.state_tracker.get_state_history() "history": cog.lifecycle_manager.state_tracker.get_state_history(),
} }

View File

@@ -7,26 +7,26 @@ from typing import Optional, Dict, Any, Set, List, Callable, TypedDict, ClassVar
from enum import Enum, auto from enum import Enum, auto
from datetime import datetime from datetime import datetime
try: # try:
# Try relative imports first # Try relative imports first
from .cleanup import cleanup_resources, force_cleanup_resources from .cleanup import cleanup_resources, force_cleanup_resources
from ..utils.exceptions import ( from ..utils.exceptions import (
VideoArchiverError, VideoArchiverError,
ErrorContext, ErrorContext,
ErrorSeverity, ErrorSeverity,
ComponentError, ComponentError,
CleanupError, CleanupError,
) )
except ImportError: #except ImportError:
# Fall back to absolute imports if relative imports fail # Fall back to absolute imports if relative imports fail
# from videoarchiver.core.cleanup import cleanup_resources, force_cleanup_resources # from videoarchiver.core.cleanup import cleanup_resources, force_cleanup_resources
# from videoarchiver.utils.exceptions import ( # from videoarchiver.utils.exceptions import (
VideoArchiverError, # VideoArchiverError,
ErrorContext, # ErrorContext,
ErrorSeverity, # ErrorSeverity,
ComponentError, # ComponentError,
CleanupError, # CleanupError,
) # )
logger = logging.getLogger("VideoArchiver") logger = logging.getLogger("VideoArchiver")

View File

@@ -7,12 +7,13 @@ from datetime import datetime
import discord # type: ignore import discord # type: ignore
from redbot.core.commands import Context # type: ignore from redbot.core.commands import Context # type: ignore
try: # try:
# Try relative imports first # 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 # except ImportError:
# from videoarchiver.utils.exceptions import ErrorSeverity # Fall back to absolute imports if relative imports fail
# from videoarchiver.utils.exceptions import ErrorSeverity
logger = logging.getLogger("VideoArchiver") logger = logging.getLogger("VideoArchiver")

View File

@@ -4,29 +4,35 @@ from typing import Dict, Any, List, Optional, Union, TypedDict, ClassVar
from dataclasses import dataclass, field from dataclasses import dataclass, field
from enum import Enum, auto from enum import Enum, auto
try: # try:
# Try relative imports first # 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 # except ImportError:
# from videoarchiver.utils.exceptions import ConfigurationError, ErrorContext, ErrorSeverity # Fall back to absolute imports if relative imports fail
# from videoarchiver.utils.exceptions import ConfigurationError, ErrorContext, ErrorSeverity
class VideoFormat(Enum): class VideoFormat(Enum):
"""Supported video formats""" """Supported video formats"""
MP4 = "mp4" MP4 = "mp4"
WEBM = "webm" WEBM = "webm"
MKV = "mkv" MKV = "mkv"
class VideoQuality(Enum): class VideoQuality(Enum):
"""Video quality presets""" """Video quality presets"""
LOW = "low" # 480p LOW = "low" # 480p
MEDIUM = "medium" # 720p MEDIUM = "medium" # 720p
HIGH = "high" # 1080p HIGH = "high" # 1080p
ULTRA = "ultra" # 4K ULTRA = "ultra" # 4K
class SettingCategory(Enum): class SettingCategory(Enum):
"""Setting categories""" """Setting categories"""
GENERAL = auto() GENERAL = auto()
CHANNELS = auto() CHANNELS = auto()
PERMISSIONS = auto() PERMISSIONS = auto()
@@ -35,15 +41,19 @@ class SettingCategory(Enum):
PERFORMANCE = auto() PERFORMANCE = auto()
FEATURES = auto() FEATURES = auto()
class ValidationResult(TypedDict): class ValidationResult(TypedDict):
"""Type definition for validation result""" """Type definition for validation result"""
valid: bool valid: bool
error: Optional[str] error: Optional[str]
details: Dict[str, Any] details: Dict[str, Any]
@dataclass @dataclass
class SettingDefinition: class SettingDefinition:
"""Defines a setting's properties""" """Defines a setting's properties"""
name: str name: str
category: SettingCategory category: SettingCategory
default_value: Any default_value: Any
@@ -66,8 +76,8 @@ class SettingDefinition:
"Settings", "Settings",
"definition_validation", "definition_validation",
{"setting": self.name}, {"setting": self.name},
ErrorSeverity.HIGH ErrorSeverity.HIGH,
) ),
) )
if self.min_value is not None and self.max_value is not None: if self.min_value is not None and self.max_value is not None:
@@ -78,10 +88,11 @@ class SettingDefinition:
"Settings", "Settings",
"definition_validation", "definition_validation",
{"setting": self.name}, {"setting": self.name},
ErrorSeverity.HIGH ErrorSeverity.HIGH,
) ),
) )
class Settings: class Settings:
"""Manages VideoArchiver settings""" """Manages VideoArchiver settings"""
@@ -92,7 +103,7 @@ class Settings:
category=SettingCategory.GENERAL, category=SettingCategory.GENERAL,
default_value=False, default_value=False,
description="Whether the archiver is enabled for this guild", description="Whether the archiver is enabled for this guild",
data_type=bool data_type=bool,
), ),
"archive_channel": SettingDefinition( "archive_channel": SettingDefinition(
name="archive_channel", name="archive_channel",
@@ -101,7 +112,7 @@ class Settings:
description="Channel where archived videos are posted", description="Channel where archived videos are posted",
data_type=int, data_type=int,
required=False, required=False,
error_message="Archive channel must be a valid channel ID" error_message="Archive channel must be a valid channel ID",
), ),
"log_channel": SettingDefinition( "log_channel": SettingDefinition(
name="log_channel", name="log_channel",
@@ -110,7 +121,7 @@ class Settings:
description="Channel for logging archiver actions", description="Channel for logging archiver actions",
data_type=int, data_type=int,
required=False, required=False,
error_message="Log channel must be a valid channel ID" error_message="Log channel must be a valid channel ID",
), ),
"enabled_channels": SettingDefinition( "enabled_channels": SettingDefinition(
name="enabled_channels", name="enabled_channels",
@@ -118,7 +129,7 @@ class Settings:
default_value=[], default_value=[],
description="Channels to monitor (empty means all channels)", 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" error_message="Enabled channels must be a list of valid channel IDs",
), ),
"allowed_roles": SettingDefinition( "allowed_roles": SettingDefinition(
name="allowed_roles", name="allowed_roles",
@@ -126,7 +137,7 @@ class Settings:
default_value=[], default_value=[],
description="Roles allowed to use archiver (empty means all roles)", 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" error_message="Allowed roles must be a list of valid role IDs",
), ),
"video_format": SettingDefinition( "video_format": SettingDefinition(
name="video_format", name="video_format",
@@ -135,7 +146,7 @@ class Settings:
description="Format for archived videos", description="Format for archived videos",
data_type=str, 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)}" error_message=f"Video format must be one of: {', '.join(f.value for f in VideoFormat)}",
), ),
"video_quality": SettingDefinition( "video_quality": SettingDefinition(
name="video_quality", name="video_quality",
@@ -144,7 +155,7 @@ class Settings:
description="Quality preset for archived videos", description="Quality preset for archived videos",
data_type=str, 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)}" error_message=f"Video quality must be one of: {', '.join(q.value for q in VideoQuality)}",
), ),
"max_file_size": SettingDefinition( "max_file_size": SettingDefinition(
name="max_file_size", name="max_file_size",
@@ -154,7 +165,7 @@ class Settings:
data_type=int, data_type=int,
min_value=1, min_value=1,
max_value=100, max_value=100,
error_message="Max file size must be between 1 and 100 MB" error_message="Max file size must be between 1 and 100 MB",
), ),
"message_duration": SettingDefinition( "message_duration": SettingDefinition(
name="message_duration", name="message_duration",
@@ -164,7 +175,7 @@ class Settings:
data_type=int, data_type=int,
min_value=5, min_value=5,
max_value=300, max_value=300,
error_message="Message duration must be between 5 and 300 seconds" error_message="Message duration must be between 5 and 300 seconds",
), ),
"message_template": SettingDefinition( "message_template": SettingDefinition(
name="message_template", name="message_template",
@@ -172,7 +183,7 @@ class Settings:
default_value="{author} archived a video from {channel}", default_value="{author} archived a video from {channel}",
description="Template for archive messages", description="Template for archive messages",
data_type=str, data_type=str,
error_message="Message template must contain {author} and {channel} placeholders" error_message="Message template must contain {author} and {channel} placeholders",
), ),
"concurrent_downloads": SettingDefinition( "concurrent_downloads": SettingDefinition(
name="concurrent_downloads", name="concurrent_downloads",
@@ -182,7 +193,7 @@ class Settings:
data_type=int, data_type=int,
min_value=1, min_value=1,
max_value=5, max_value=5,
error_message="Concurrent downloads must be between 1 and 5" error_message="Concurrent downloads must be between 1 and 5",
), ),
"enabled_sites": SettingDefinition( "enabled_sites": SettingDefinition(
name="enabled_sites", name="enabled_sites",
@@ -191,14 +202,14 @@ class Settings:
description="Sites to enable archiving for (None means all sites)", description="Sites to enable archiving for (None means all sites)",
data_type=list, data_type=list,
required=False, required=False,
error_message="Enabled sites must be a list of valid site identifiers" error_message="Enabled sites must be a list of valid site identifiers",
), ),
"use_database": SettingDefinition( "use_database": SettingDefinition(
name="use_database", name="use_database",
category=SettingCategory.FEATURES, category=SettingCategory.FEATURES,
default_value=False, default_value=False,
description="Enable database tracking of archived videos", description="Enable database tracking of archived videos",
data_type=bool data_type=bool,
), ),
} }
@@ -216,7 +227,9 @@ class Settings:
return cls.SETTINGS.get(setting) return cls.SETTINGS.get(setting)
@classmethod @classmethod
def get_settings_by_category(cls, category: SettingCategory) -> Dict[str, SettingDefinition]: def get_settings_by_category(
cls, category: SettingCategory
) -> Dict[str, SettingDefinition]:
""" """
Get all settings in a category. Get all settings in a category.
@@ -252,18 +265,15 @@ class Settings:
raise ConfigurationError( raise ConfigurationError(
f"Unknown setting: {setting}", f"Unknown setting: {setting}",
context=ErrorContext( context=ErrorContext(
"Settings", "Settings", "validation", {"setting": setting}, ErrorSeverity.HIGH
"validation", ),
{"setting": setting},
ErrorSeverity.HIGH
)
) )
details = { details = {
"setting": setting, "setting": setting,
"value": value, "value": value,
"type": type(value).__name__, "type": type(value).__name__,
"expected_type": definition.data_type.__name__ "expected_type": definition.data_type.__name__,
} }
# Check type # Check type
@@ -271,15 +281,13 @@ class Settings:
return ValidationResult( return ValidationResult(
valid=False, valid=False,
error=f"Invalid type: expected {definition.data_type.__name__}, got {type(value).__name__}", error=f"Invalid type: expected {definition.data_type.__name__}, got {type(value).__name__}",
details=details details=details,
) )
# Check required # Check required
if definition.required and value is None: if definition.required and value is None:
return ValidationResult( return ValidationResult(
valid=False, valid=False, error="Required setting cannot be None", details=details
error="Required setting cannot be None",
details=details
) )
# Check choices # Check choices
@@ -287,7 +295,7 @@ class Settings:
return ValidationResult( return ValidationResult(
valid=False, valid=False,
error=f"Value must be one of: {', '.join(map(str, definition.choices))}", error=f"Value must be one of: {', '.join(map(str, definition.choices))}",
details=details details=details,
) )
# Check numeric bounds # Check numeric bounds
@@ -296,13 +304,13 @@ class Settings:
return ValidationResult( return ValidationResult(
valid=False, valid=False,
error=f"Value must be at least {definition.min_value}", error=f"Value must be at least {definition.min_value}",
details=details details=details,
) )
if definition.max_value is not None and value > definition.max_value: if definition.max_value is not None and value > definition.max_value:
return ValidationResult( return ValidationResult(
valid=False, valid=False,
error=f"Value must be at most {definition.max_value}", error=f"Value must be at most {definition.max_value}",
details=details details=details,
) )
# Custom validation # Custom validation
@@ -313,20 +321,12 @@ class Settings:
return ValidationResult( return ValidationResult(
valid=False, valid=False,
error=definition.error_message or "Validation failed", error=definition.error_message or "Validation failed",
details=details details=details,
) )
except Exception as e: except Exception as e:
return ValidationResult( return ValidationResult(valid=False, error=str(e), details=details)
valid=False,
error=str(e),
details=details
)
return ValidationResult( return ValidationResult(valid=True, error=None, details=details)
valid=True,
error=None,
details=details
)
@property @property
def default_guild_settings(self) -> Dict[str, Any]: def default_guild_settings(self) -> Dict[str, Any]:
@@ -337,8 +337,7 @@ class Settings:
Dictionary of default settings Dictionary of default settings
""" """
return { return {
name: definition.default_value name: definition.default_value for name, definition in self.SETTINGS.items()
for name, definition in self.SETTINGS.items()
} }
@classmethod @classmethod
@@ -362,7 +361,7 @@ class Settings:
f"Description: {definition.description}", f"Description: {definition.description}",
f"Type: {definition.data_type.__name__}", f"Type: {definition.data_type.__name__}",
f"Required: {definition.required}", f"Required: {definition.required}",
f"Default: {definition.default_value}" f"Default: {definition.default_value}",
] ]
if definition.choices: if definition.choices:

View File

@@ -1,21 +1,22 @@
"""Database management package for video archiving""" """Database management package for video archiving"""
try: # try:
# Try relative imports first # Try relative imports first
from .connection_manager import DatabaseConnectionManager from .connection_manager import DatabaseConnectionManager
from .query_manager import DatabaseQueryManager from .query_manager import DatabaseQueryManager
from .schema_manager import DatabaseSchemaManager from .schema_manager import DatabaseSchemaManager
from .video_archive_db import VideoArchiveDB from .video_archive_db import VideoArchiveDB
except ImportError:
# Fall back to absolute imports if relative imports fail # except ImportError:
# from videoarchiver.database.connection_manager import DatabaseConnectionManager # Fall back to absolute imports if relative imports fail
# from videoarchiver.database.query_manager import DatabaseQueryManager # from videoarchiver.database.connection_manager import DatabaseConnectionManager
# from videoarchiver.database.schema_manager import DatabaseSchemaManager # from videoarchiver.database.query_manager import DatabaseQueryManager
# from videoarchiver.database.video_archive_db import VideoArchiveDB # from videoarchiver.database.schema_manager import DatabaseSchemaManager
# from videoarchiver.database.video_archive_db import VideoArchiveDB
__all__ = [ __all__ = [
'DatabaseConnectionManager', "DatabaseConnectionManager",
'DatabaseQueryManager', "DatabaseQueryManager",
'DatabaseSchemaManager', "DatabaseSchemaManager",
'VideoArchiveDB' "VideoArchiveDB",
] ]

View File

@@ -10,10 +10,10 @@ import threading
from queue import Queue, Empty from queue import Queue, Empty
from datetime import datetime from datetime import datetime
try: #try:
# Try relative imports first # Try relative imports first
from ..utils.exceptions import DatabaseError, ErrorContext, ErrorSeverity from ..utils.exceptions import DatabaseError, ErrorContext, ErrorSeverity
except ImportError: #except ImportError:
# Fall back to absolute imports if relative imports fail # Fall back to absolute imports if relative imports fail
# from videoarchiver.utils.exceptions import DatabaseError, ErrorContext, ErrorSeverity # from videoarchiver.utils.exceptions import DatabaseError, ErrorContext, ErrorSeverity

View File

@@ -7,10 +7,10 @@ from typing import List, Dict, Any, Optional, TypedDict, ClassVar, Union
from enum import Enum, auto from enum import Enum, auto
from datetime import datetime from datetime import datetime
try: #try:
# Try relative imports first # Try relative imports first
from ..utils.exceptions import DatabaseError, ErrorContext, ErrorSeverity from ..utils.exceptions import DatabaseError, ErrorContext, ErrorSeverity
except ImportError: #except ImportError:
# Fall back to absolute imports if relative imports fail # Fall back to absolute imports if relative imports fail
# from videoarchiver.utils.exceptions import DatabaseError, ErrorContext, ErrorSeverity # from videoarchiver.utils.exceptions import DatabaseError, ErrorContext, ErrorSeverity

View File

@@ -4,16 +4,17 @@ import logging
from pathlib import Path from pathlib import Path
from typing import Optional, Dict, Any, List from typing import Optional, Dict, Any, List
try: # try:
# Try relative imports first # Try relative imports first
from .schema_manager import DatabaseSchemaManager from .schema_manager import DatabaseSchemaManager
from .query_manager import DatabaseQueryManager from .query_manager import DatabaseQueryManager
from .connection_manager import DatabaseConnectionManager from .connection_manager import DatabaseConnectionManager
except ImportError:
# Fall back to absolute imports if relative imports fail # except ImportError:
# from videoarchiver.database.schema_manager import DatabaseSchemaManager # Fall back to absolute imports if relative imports fail
# from videoarchiver.database.query_manager import DatabaseQueryManager # from videoarchiver.database.schema_manager import DatabaseSchemaManager
# from videoarchiver.database.connection_manager import DatabaseConnectionManager # from videoarchiver.database.query_manager import DatabaseQueryManager
# from videoarchiver.database.connection_manager import DatabaseConnectionManager
logger = logging.getLogger("VideoArchiverDB") logger = logging.getLogger("VideoArchiverDB")

View File

@@ -18,14 +18,14 @@ logging.basicConfig(
logger = logging.getLogger("VideoArchiver") logger = logging.getLogger("VideoArchiver")
# Import components after logging is configured # Import components after logging is configured
try: #try:
# Try relative imports first # Try relative imports first
from .ffmpeg_manager import FFmpegManager from .ffmpeg_manager import FFmpegManager
from .video_analyzer import VideoAnalyzer from .video_analyzer import VideoAnalyzer
from .gpu_detector import GPUDetector from .gpu_detector import GPUDetector
from .encoder_params import EncoderParams from .encoder_params import EncoderParams
from .ffmpeg_downloader import FFmpegDownloader from .ffmpeg_downloader import FFmpegDownloader
from .exceptions import ( from .exceptions import (
FFmpegError, FFmpegError,
DownloadError, DownloadError,
VerificationError, VerificationError,
@@ -43,8 +43,8 @@ try:
QualityError, QualityError,
AudioError, AudioError,
BitrateError, BitrateError,
) )
except ImportError: #except ImportError:
# Fall back to absolute imports if relative imports fail # Fall back to absolute imports if relative imports fail
# from videoarchiver.ffmpeg.ffmpeg_manager import FFmpegManager # from videoarchiver.ffmpeg.ffmpeg_manager import FFmpegManager
# from videoarchiver.ffmpeg.video_analyzer import VideoAnalyzer # from videoarchiver.ffmpeg.video_analyzer import VideoAnalyzer
@@ -52,24 +52,24 @@ except ImportError:
# from videoarchiver.ffmpeg.encoder_params import EncoderParams # from videoarchiver.ffmpeg.encoder_params import EncoderParams
# from videoarchiver.ffmpeg.ffmpeg_downloader import FFmpegDownloader # from videoarchiver.ffmpeg.ffmpeg_downloader import FFmpegDownloader
# from videoarchiver.ffmpeg.exceptions import ( # from videoarchiver.ffmpeg.exceptions import (
FFmpegError, # FFmpegError,
DownloadError, # DownloadError,
VerificationError, # VerificationError,
EncodingError, # EncodingError,
AnalysisError, # AnalysisError,
GPUError, # GPUError,
HardwareAccelerationError, # HardwareAccelerationError,
FFmpegNotFoundError, # FFmpegNotFoundError,
FFprobeError, # FFprobeError,
CompressionError, # CompressionError,
FormatError, # FormatError,
PermissionError, # PermissionError,
TimeoutError, # TimeoutError,
ResourceError, # ResourceError,
QualityError, # QualityError,
AudioError, # AudioError,
BitrateError, # BitrateError,
) # )
class FFmpeg: class FFmpeg:

View File

@@ -5,26 +5,26 @@ import os
from pathlib import Path from pathlib import Path
from typing import Dict, Optional from typing import Dict, Optional
try: #try:
# Try relative imports first # Try relative imports first
from .exceptions import ( from .exceptions import (
FFmpegError, FFmpegError,
DownloadError, DownloadError,
VerificationError, VerificationError,
PermissionError, PermissionError,
FFmpegNotFoundError FFmpegNotFoundError
) )
from .ffmpeg_downloader import FFmpegDownloader from .ffmpeg_downloader import FFmpegDownloader
from .verification_manager import VerificationManager from .verification_manager import VerificationManager
except ImportError: #except ImportError:
# Fall back to absolute imports if relative imports fail # Fall back to absolute imports if relative imports fail
# from videoarchiver.ffmpeg.exceptions import ( # from videoarchiver.ffmpeg.exceptions import (
FFmpegError, # FFmpegError,
DownloadError, # DownloadError,
VerificationError, # VerificationError,
PermissionError, # PermissionError,
FFmpegNotFoundError # FFmpegNotFoundError
) # )
# from videoarchiver.ffmpeg.ffmpeg_downloader import FFmpegDownloader # from videoarchiver.ffmpeg.ffmpeg_downloader import FFmpegDownloader
# from videoarchiver.ffmpeg.verification_manager import VerificationManager # from videoarchiver.ffmpeg.verification_manager import VerificationManager

View File

@@ -4,10 +4,10 @@ import os
import logging import logging
from typing import Dict, Any from typing import Dict, Any
try: #try:
# Try relative imports first # Try relative imports first
from .exceptions import CompressionError, QualityError, BitrateError from .exceptions import CompressionError, QualityError, BitrateError
except ImportError: #except ImportError:
# Fall back to absolute imports if relative imports fail # Fall back to absolute imports if relative imports fail
# from videoarchiver.ffmpeg.exceptions import CompressionError, QualityError, BitrateError # from videoarchiver.ffmpeg.exceptions import CompressionError, QualityError, BitrateError

View File

@@ -16,12 +16,13 @@ from typing import Optional, Dict, List
import time import time
import lzma import lzma
try: # try:
# Try relative imports first # Try relative imports first
from .exceptions import DownloadError from .exceptions import DownloadError
except ImportError:
# Fall back to absolute imports if relative imports fail # except ImportError:
# from videoarchiver.ffmpeg.exceptions import DownloadError # Fall back to absolute imports if relative imports fail
# from videoarchiver.ffmpeg.exceptions import DownloadError
logger = logging.getLogger("VideoArchiver") logger = logging.getLogger("VideoArchiver")
@@ -249,14 +250,16 @@ class FFmpegDownloader:
binary_files = [ binary_files = [
f f
for f in zip_ref.namelist() for f in zip_ref.namelist()
if f.endswith(f"/bin/{binary_name}") or f.endswith(f"\\bin\\{binary_name}") if f.endswith(f"/bin/{binary_name}")
or f.endswith(f"\\bin\\{binary_name}")
] ]
if not binary_files: if not binary_files:
# Fallback to old structure # Fallback to old structure
binary_files = [ binary_files = [
f f
for f in zip_ref.namelist() for f in zip_ref.namelist()
if f.endswith(f"/{binary_name}") or f.endswith(f"\\{binary_name}") if f.endswith(f"/{binary_name}")
or f.endswith(f"\\{binary_name}")
] ]
if not binary_files: if not binary_files:
raise DownloadError(f"{binary_name} not found in archive") raise DownloadError(f"{binary_name} not found in archive")
@@ -271,10 +274,10 @@ class FFmpegDownloader:
"""Extract from tar archive (Linux/macOS)""" """Extract from tar archive (Linux/macOS)"""
try: try:
# First decompress the .xz file in chunks to prevent blocking # First decompress the .xz file in chunks to prevent blocking
decompressed_path = archive_path.with_suffix('') decompressed_path = archive_path.with_suffix("")
chunk_size = 1024 * 1024 # 1MB chunks chunk_size = 1024 * 1024 # 1MB chunks
with lzma.open(archive_path, 'rb') as compressed: with lzma.open(archive_path, "rb") as compressed:
with open(decompressed_path, 'wb') as decompressed: with open(decompressed_path, "wb") as decompressed:
while True: while True:
chunk = compressed.read(chunk_size) chunk = compressed.read(chunk_size)
if not chunk: if not chunk:
@@ -289,12 +292,16 @@ class FFmpegDownloader:
for binary_name in binary_names: for binary_name in binary_names:
# BtbN's builds have binaries in bin directory # BtbN's builds have binaries in bin directory
binary_files = [ binary_files = [
f for f in tar_ref.getnames() if f.endswith(f"/bin/{binary_name}") f
for f in tar_ref.getnames()
if f.endswith(f"/bin/{binary_name}")
] ]
if not binary_files: if not binary_files:
# Fallback to old structure # Fallback to old structure
binary_files = [ binary_files = [
f for f in tar_ref.getnames() if f.endswith(f"/{binary_name}") f
for f in tar_ref.getnames()
if f.endswith(f"/{binary_name}")
] ]
if not binary_files: if not binary_files:
raise DownloadError(f"{binary_name} not found in archive") raise DownloadError(f"{binary_name} not found in archive")
@@ -306,7 +313,9 @@ class FFmpegDownloader:
target_path = self.base_dir / binary_name target_path = self.base_dir / binary_name
# Copy file in chunks # Copy file in chunks
with open(extracted_path, 'rb') as src, open(target_path, 'wb') as dst: with open(extracted_path, "rb") as src, open(
target_path, "wb"
) as dst:
while True: while True:
chunk = src.read(chunk_size) chunk = src.read(chunk_size)
if not chunk: if not chunk:
@@ -350,7 +359,7 @@ class FFmpegDownloader:
timeout=5, timeout=5,
text=True, text=True,
check=False, # Don't raise on non-zero return code check=False, # Don't raise on non-zero return code
env={"PATH": os.environ.get("PATH", "")} # Ensure PATH is set env={"PATH": os.environ.get("PATH", "")}, # Ensure PATH is set
) )
except subprocess.TimeoutExpired: except subprocess.TimeoutExpired:
logger.error("FFmpeg verification timed out") logger.error("FFmpeg verification timed out")
@@ -368,7 +377,7 @@ class FFmpegDownloader:
timeout=5, timeout=5,
text=True, text=True,
check=False, # Don't raise on non-zero return code check=False, # Don't raise on non-zero return code
env={"PATH": os.environ.get("PATH", "")} # Ensure PATH is set env={"PATH": os.environ.get("PATH", "")}, # Ensure PATH is set
) )
except subprocess.TimeoutExpired: except subprocess.TimeoutExpired:
logger.error("FFprobe verification timed out") logger.error("FFprobe verification timed out")

View File

@@ -6,32 +6,29 @@ import multiprocessing
from pathlib import Path from pathlib import Path
from typing import Dict, Any, Optional from typing import Dict, Any, Optional
try: # try:
# Try relative imports first # Try relative imports first
from .exceptions import ( from .exceptions import FFmpegError, AnalysisError, FFmpegNotFoundError
FFmpegError, from .gpu_detector import GPUDetector
AnalysisError, from .video_analyzer import VideoAnalyzer
FFmpegNotFoundError from .encoder_params import EncoderParams
) from .process_manager import ProcessManager
from .gpu_detector import GPUDetector from .verification_manager import VerificationManager
from .video_analyzer import VideoAnalyzer from .binary_manager import BinaryManager
from .encoder_params import EncoderParams
from .process_manager import ProcessManager # except ImportError:
from .verification_manager import VerificationManager # Fall back to absolute imports if relative imports fail
from .binary_manager import BinaryManager # from videoarchiver.ffmpeg.exceptions import (
except ImportError: # FFmpegError,
# Fall back to absolute imports if relative imports fail # AnalysisError,
# from videoarchiver.ffmpeg.exceptions import ( # FFmpegNotFoundError
FFmpegError, # )
AnalysisError, # from videoarchiver.ffmpeg.gpu_detector import GPUDetector
FFmpegNotFoundError # from videoarchiver.ffmpeg.video_analyzer import VideoAnalyzer
) # from videoarchiver.ffmpeg.encoder_params import EncoderParams
# from videoarchiver.ffmpeg.gpu_detector import GPUDetector # from videoarchiver.ffmpeg.process_manager import ProcessManager
# from videoarchiver.ffmpeg.video_analyzer import VideoAnalyzer # from videoarchiver.ffmpeg.verification_manager import VerificationManager
# from videoarchiver.ffmpeg.encoder_params import EncoderParams # from videoarchiver.ffmpeg.binary_manager import BinaryManager
# from videoarchiver.ffmpeg.process_manager import ProcessManager
# from videoarchiver.ffmpeg.verification_manager import VerificationManager
# from videoarchiver.ffmpeg.binary_manager import BinaryManager
logger = logging.getLogger("VideoArchiver") logger = logging.getLogger("VideoArchiver")
@@ -53,7 +50,7 @@ class FFmpegManager:
base_dir=self.base_dir, base_dir=self.base_dir,
system=platform.system(), system=platform.system(),
machine=platform.machine(), machine=platform.machine(),
verification_manager=self.verification_manager verification_manager=self.verification_manager,
) )
# Initialize components # Initialize components
@@ -87,7 +84,9 @@ class FFmpegManager:
raise raise
raise AnalysisError(f"Failed to analyze video: {e}") raise AnalysisError(f"Failed to analyze video: {e}")
def get_compression_params(self, input_path: str, target_size_mb: int) -> Dict[str, str]: def get_compression_params(
self, input_path: str, target_size_mb: int
) -> Dict[str, str]:
"""Get optimal compression parameters for the given input file""" """Get optimal compression parameters for the given input file"""
try: try:
# Analyze video first # Analyze video first
@@ -113,7 +112,7 @@ class FFmpegManager:
"preset": "medium", "preset": "medium",
"crf": "23", "crf": "23",
"c:a": "aac", "c:a": "aac",
"b:a": "128k" "b:a": "128k",
} }
def get_ffmpeg_path(self) -> str: def get_ffmpeg_path(self) -> str:

View File

@@ -6,22 +6,23 @@ import subprocess
from pathlib import Path from pathlib import Path
from typing import Dict, List, Optional from typing import Dict, List, Optional
try: # try:
# Try relative imports first # Try relative imports first
from .exceptions import ( from .exceptions import (
TimeoutError, TimeoutError,
VerificationError, VerificationError,
EncodingError, EncodingError,
handle_ffmpeg_error handle_ffmpeg_error,
) )
except ImportError:
# Fall back to absolute imports if relative imports fail # except ImportError:
# from videoarchiver.ffmpeg.exceptions import ( # Fall back to absolute imports if relative imports fail
TimeoutError, # from videoarchiver.ffmpeg.exceptions import (
VerificationError, # TimeoutError,
EncodingError, # VerificationError,
handle_ffmpeg_error # EncodingError,
) # handle_ffmpeg_error
# )
logger = logging.getLogger("FFmpegVerification") logger = logging.getLogger("FFmpegVerification")
@@ -33,10 +34,7 @@ class VerificationManager:
self.process_manager = process_manager self.process_manager = process_manager
def verify_ffmpeg( def verify_ffmpeg(
self, self, ffmpeg_path: Path, ffprobe_path: Path, gpu_info: Dict[str, bool]
ffmpeg_path: Path,
ffprobe_path: Path,
gpu_info: Dict[str, bool]
) -> None: ) -> None:
"""Verify FFmpeg functionality with comprehensive checks """Verify FFmpeg functionality with comprehensive checks
@@ -72,8 +70,7 @@ class VerificationManager:
"""Verify FFmpeg version""" """Verify FFmpeg version"""
try: try:
result = self._execute_command( result = self._execute_command(
[str(ffmpeg_path), "-version"], [str(ffmpeg_path), "-version"], "FFmpeg version check"
"FFmpeg version check"
) )
logger.info(f"FFmpeg version: {result.stdout.split()[2]}") logger.info(f"FFmpeg version: {result.stdout.split()[2]}")
except Exception as e: except Exception as e:
@@ -83,23 +80,20 @@ class VerificationManager:
"""Verify FFprobe version""" """Verify FFprobe version"""
try: try:
result = self._execute_command( result = self._execute_command(
[str(ffprobe_path), "-version"], [str(ffprobe_path), "-version"], "FFprobe version check"
"FFprobe version check"
) )
logger.info(f"FFprobe version: {result.stdout.split()[2]}") logger.info(f"FFprobe version: {result.stdout.split()[2]}")
except Exception as e: except Exception as e:
raise VerificationError(f"FFprobe version check failed: {e}") raise VerificationError(f"FFprobe version check failed: {e}")
def _verify_ffmpeg_capabilities( def _verify_ffmpeg_capabilities(
self, self, ffmpeg_path: Path, gpu_info: Dict[str, bool]
ffmpeg_path: Path,
gpu_info: Dict[str, bool]
) -> None: ) -> None:
"""Verify FFmpeg capabilities and encoders""" """Verify FFmpeg capabilities and encoders"""
try: try:
result = self._execute_command( result = self._execute_command(
[str(ffmpeg_path), "-hide_banner", "-encoders"], [str(ffmpeg_path), "-hide_banner", "-encoders"],
"FFmpeg capabilities check" "FFmpeg capabilities check",
) )
# Verify required encoders # Verify required encoders
@@ -107,7 +101,8 @@ class VerificationManager:
available_encoders = result.stdout.lower() available_encoders = result.stdout.lower()
missing_encoders = [ missing_encoders = [
encoder for encoder in required_encoders encoder
for encoder in required_encoders
if encoder not in available_encoders if encoder not in available_encoders
] ]
@@ -122,17 +117,12 @@ class VerificationManager:
raise VerificationError(f"FFmpeg capabilities check failed: {e}") raise VerificationError(f"FFmpeg capabilities check failed: {e}")
def _execute_command( def _execute_command(
self, self, command: List[str], operation: str, timeout: int = 10
command: List[str],
operation: str,
timeout: int = 10
) -> subprocess.CompletedProcess: ) -> subprocess.CompletedProcess:
"""Execute a command with proper error handling""" """Execute a command with proper error handling"""
try: try:
result = self.process_manager.execute_command( result = self.process_manager.execute_command(
command, command, timeout=timeout, check=False
timeout=timeout,
check=False
) )
if result.returncode != 0: if result.returncode != 0:

View File

@@ -8,10 +8,17 @@ from typing import Any, ClassVar, Dict, List, Optional, Tuple, TYPE_CHECKING
import discord # type: ignore import discord # type: ignore
from discord.ext import commands # type: ignore from discord.ext import commands # type: ignore
# from videoarchiver.core.types import ComponentState, ProcessorState, ComponentStatus, IComponent, IConfigManager, IQueueManager from ..core.types import (
# from videoarchiver.processor.constants import REACTIONS ComponentState,
# from videoarchiver.utils.progress_tracker import ProgressTracker ProcessorState,
# from videoarchiver.utils.exceptions import ProcessorError ComponentStatus,
IComponent,
IConfigManager,
IQueueManager,
)
from .constants import REACTIONS
from ..utils.progress_tracker import ProgressTracker
from ..utils.exceptions import ProcessorError
logger = logging.getLogger("VideoArchiver") logger = logging.getLogger("VideoArchiver")
@@ -45,11 +52,13 @@ class OperationTracker:
) -> None: ) -> None:
"""End tracking an operation""" """End tracking an operation"""
if op_id in self.operations: if op_id in self.operations:
self.operations[op_id].update({ self.operations[op_id].update(
{
"end_time": datetime.utcnow(), "end_time": datetime.utcnow(),
"status": "success" if success else "error", "status": "success" if success else "error",
"error": error, "error": error,
}) }
)
# Move to history # Move to history
self.operation_history.append(self.operations.pop(op_id)) self.operation_history.append(self.operations.pop(op_id))
# Update counts # Update counts
@@ -60,7 +69,7 @@ class OperationTracker:
# Cleanup old history if needed # Cleanup old history if needed
if len(self.operation_history) > self.MAX_HISTORY: if len(self.operation_history) > self.MAX_HISTORY:
self.operation_history = self.operation_history[-self.MAX_HISTORY:] self.operation_history = self.operation_history[-self.MAX_HISTORY :]
def get_active_operations(self) -> Dict[str, Dict[str, Any]]: def get_active_operations(self) -> Dict[str, Dict[str, Any]]:
"""Get currently active operations""" """Get currently active operations"""
@@ -114,11 +123,13 @@ class HealthMonitor:
self.last_check = datetime.utcnow() self.last_check = datetime.utcnow()
# Check component health # Check component health
self.health_status.update({ self.health_status.update(
{
"queue_handler": self.processor.queue_handler.is_healthy(), "queue_handler": self.processor.queue_handler.is_healthy(),
"message_handler": self.processor.message_handler.is_healthy(), "message_handler": self.processor.message_handler.is_healthy(),
"progress_tracker": self.progress_tracker.is_healthy(), "progress_tracker": self.progress_tracker.is_healthy(),
}) }
)
# Check operation health # Check operation health
op_stats = self.processor.operation_tracker.get_operation_stats() op_stats = self.processor.operation_tracker.get_operation_stats()
@@ -168,8 +179,12 @@ class VideoProcessor(IComponent):
from .cleanup_manager import CleanupManager, CleanupStrategy from .cleanup_manager import CleanupManager, CleanupStrategy
# Initialize handlers # Initialize handlers
self.queue_handler: "QueueHandler" = QueueHandler(bot, config_manager, components) self.queue_handler: "QueueHandler" = QueueHandler(
self.message_handler: "MessageHandler" = MessageHandler(bot, config_manager, queue_manager) bot, config_manager, components
)
self.message_handler: "MessageHandler" = MessageHandler(
bot, config_manager, queue_manager
)
self.cleanup_manager: "CleanupManager" = CleanupManager( self.cleanup_manager: "CleanupManager" = CleanupManager(
self.queue_handler, ffmpeg_mgr, CleanupStrategy.NORMAL self.queue_handler, ffmpeg_mgr, CleanupStrategy.NORMAL
) )
@@ -243,9 +258,7 @@ class VideoProcessor(IComponent):
async def cleanup(self) -> None: async def cleanup(self) -> None:
"""Clean up resources and stop processing""" """Clean up resources and stop processing"""
op_id = self.operation_tracker.start_operation( op_id = self.operation_tracker.start_operation("cleanup", {"type": "normal"})
"cleanup", {"type": "normal"}
)
try: try:
self._state = ProcessorState.SHUTDOWN self._state = ProcessorState.SHUTDOWN
@@ -260,9 +273,7 @@ class VideoProcessor(IComponent):
async def force_cleanup(self) -> None: async def force_cleanup(self) -> None:
"""Force cleanup of resources""" """Force cleanup of resources"""
op_id = self.operation_tracker.start_operation( op_id = self.operation_tracker.start_operation("cleanup", {"type": "force"})
"cleanup", {"type": "force"}
)
try: try:
self._state = ProcessorState.SHUTDOWN self._state = ProcessorState.SHUTDOWN
@@ -321,5 +332,5 @@ class VideoProcessor(IComponent):
"operations": self.operation_tracker.get_operation_stats(), "operations": self.operation_tracker.get_operation_stats(),
"active_operations": self.operation_tracker.get_active_operations(), "active_operations": self.operation_tracker.get_active_operations(),
"health_status": self.health_monitor.health_status, "health_status": self.health_monitor.health_status,
} },
) )

View File

@@ -4,32 +4,44 @@ import asyncio
import logging import logging
from datetime import datetime, timedelta from datetime import datetime, timedelta
from enum import auto, Enum from enum import auto, Enum
from typing import Any, ClassVar, Dict, List, Optional, Set, Tuple, TypedDict, TYPE_CHECKING from typing import (
Any,
ClassVar,
Dict,
List,
Optional,
Set,
Tuple,
TypedDict,
TYPE_CHECKING,
)
import discord # type: ignore import discord # type: ignore
from discord.ext import commands # type: ignore from discord.ext import commands # type: ignore
try: # try:
# Try relative imports first # Try relative imports first
from ..config_manager import ConfigManager from ..config_manager import ConfigManager
from .constants import REACTIONS from .constants import REACTIONS
from .message_validator import MessageValidator, ValidationError from .message_validator import MessageValidator, ValidationError
from .url_extractor import URLExtractor, URLMetadata from .url_extractor import URLExtractor, URLMetadata
from ..queue.types import QueuePriority from ..queue.types import QueuePriority
from ..utils.exceptions import MessageHandlerError from ..utils.exceptions import MessageHandlerError
except ImportError:
# Fall back to absolute imports if relative imports fail # except ImportError:
# from videoarchiver.config_manager import ConfigManager # Fall back to absolute imports if relative imports fail
# from videoarchiver.processor.constants import REACTIONS # from videoarchiver.config_manager import ConfigManager
# from videoarchiver.processor.message_validator import MessageValidator, ValidationError # from videoarchiver.processor.constants import REACTIONS
# from videoarchiver.processor.url_extractor import URLExtractor, URLMetadata # from videoarchiver.processor.message_validator import MessageValidator, ValidationError
# from videoarchiver.queue.types import QueuePriority # from videoarchiver.processor.url_extractor import URLExtractor, URLMetadata
# from videoarchiver.utils.exceptions import MessageHandlerError # from videoarchiver.queue.types import QueuePriority
# from videoarchiver.utils.exceptions import MessageHandlerError
if TYPE_CHECKING: if TYPE_CHECKING:
try: # try:
from ..queue.manager import EnhancedVideoQueueManager from ..queue.manager import EnhancedVideoQueueManager
except ImportError:
# except ImportError:
# from videoarchiver.queue.manager import EnhancedVideoQueueManager # from videoarchiver.queue.manager import EnhancedVideoQueueManager
logger = logging.getLogger("VideoArchiver") logger = logging.getLogger("VideoArchiver")
@@ -337,7 +349,7 @@ class MessageHandler:
channel_id=message.channel.id, channel_id=message.channel.id,
guild_id=message.guild.id, guild_id=message.guild.id,
author_id=message.author.id, author_id=message.author.id,
priority=QueuePriority.NORMAL.value priority=QueuePriority.NORMAL.value,
) )
except Exception as e: except Exception as e:
raise MessageHandlerError(f"Queue processing failed: {str(e)}") raise MessageHandlerError(f"Queue processing failed: {str(e)}")

View File

@@ -7,10 +7,10 @@ from typing import Dict, Optional, Tuple, List, Any, Callable, Set, TypedDict, C
from datetime import datetime from datetime import datetime
import discord # type: ignore import discord # type: ignore
try: #try:
# Try relative imports first # Try relative imports first
from ..utils.exceptions import ValidationError from ..utils.exceptions import ValidationError
except ImportError: #except ImportError:
# Fall back to absolute imports if relative imports fail # Fall back to absolute imports if relative imports fail
# from videoarchiver.utils.exceptions import ValidationError # from videoarchiver.utils.exceptions import ValidationError

View File

@@ -8,27 +8,28 @@ from typing import Optional, Dict, Any, List, Tuple, Set, TypedDict, ClassVar, C
from datetime import datetime from datetime import datetime
import discord # type: ignore import discord # type: ignore
try: # try:
# Try relative imports first # Try relative imports first
from .. import utils from .. import utils
from ..database.video_archive_db import VideoArchiveDB from ..database.video_archive_db import VideoArchiveDB
from ..utils.download_manager import DownloadManager from ..utils.download_manager import DownloadManager
from ..utils.message_manager import MessageManager from ..utils.message_manager import MessageManager
from ..utils.exceptions import QueueHandlerError from ..utils.exceptions import QueueHandlerError
from ..queue.models import QueueItem from ..queue.models import QueueItem
from ..config_manager import ConfigManager from ..config_manager import ConfigManager
from . import progress_tracker # Import from processor package from . import progress_tracker # Import from processor package
from .constants import REACTIONS from .constants import REACTIONS
except ImportError:
# Fall back to absolute imports if relative imports fail # except ImportError:
# from videoarchiver.database.video_archive_db import VideoArchiveDB # Fall back to absolute imports if relative imports fail
# from videoarchiver.utils.download_manager import DownloadManager # from videoarchiver.database.video_archive_db import VideoArchiveDB
# from videoarchiver.utils.message_manager import MessageManager # from videoarchiver.utils.download_manager import DownloadManager
# from videoarchiver.utils.exceptions import QueueHandlerError # from videoarchiver.utils.message_manager import MessageManager
# from videoarchiver.queue.models import QueueItem # from videoarchiver.utils.exceptions import QueueHandlerError
# from videoarchiver.config_manager import ConfigManager # from videoarchiver.queue.models import QueueItem
# from videoarchiver.processor import progress_tracker # Import from processor package # from videoarchiver.config_manager import ConfigManager
# from videoarchiver.processor.constants import REACTIONS # from videoarchiver.processor import progress_tracker # Import from processor package
# from videoarchiver.processor.constants import REACTIONS
logger = logging.getLogger("VideoArchiver") logger = logging.getLogger("VideoArchiver")

View File

@@ -5,11 +5,11 @@ import asyncio
from typing import List, Optional, Dict, Any, Set, ClassVar from typing import List, Optional, Dict, Any, Set, ClassVar
from datetime import datetime from datetime import datetime
try: #try:
# Try relative imports first # Try relative imports first
from ..queue.types import QueuePriority, QueueMetrics, ProcessingMetrics from ..queue.types import QueuePriority, QueueMetrics, ProcessingMetrics
from ..queue.models import QueueItem from ..queue.models import QueueItem
except ImportError: #except ImportError:
# Fall back to absolute imports if relative imports fail # Fall back to absolute imports if relative imports fail
# from videoarchiver.queue.types import QueuePriority, QueueMetrics, ProcessingMetrics # from videoarchiver.queue.types import QueuePriority, QueueMetrics, ProcessingMetrics
# from videoarchiver.queue.models import QueueItem # from videoarchiver.queue.models import QueueItem

View File

@@ -7,24 +7,25 @@ from typing import List, Optional
import discord # type: ignore import discord # type: ignore
from urllib.parse import urlparse from urllib.parse import urlparse
try: # try:
# Try relative imports first # Try relative imports first
from ..processor.constants import ( from ..processor.constants import (
REACTIONS, REACTIONS,
ReactionType, ReactionType,
get_reaction, get_reaction,
get_progress_emoji, get_progress_emoji,
) )
from ..database.video_archive_db import VideoArchiveDB from ..database.video_archive_db import VideoArchiveDB
except ImportError:
# Fall back to absolute imports if relative imports fail # except ImportError:
# from videoarchiver.processor.constants import ( # Fall back to absolute imports if relative imports fail
REACTIONS, # from videoarchiver.processor.constants import (
ReactionType, # REACTIONS,
get_reaction, # ReactionType,
get_progress_emoji, # get_reaction,
) # get_progress_emoji,
# from videoarchiver.database.video_archive_db import VideoArchiveDB # )
# from videoarchiver.database.video_archive_db import VideoArchiveDB
logger = logging.getLogger("VideoArchiver") logger = logging.getLogger("VideoArchiver")

View File

@@ -18,12 +18,13 @@ from typing import (
) )
import discord # type: ignore import discord # type: ignore
try: # try:
# Try relative imports first # Try relative imports first
from ..utils.exceptions import DisplayError from ..utils.exceptions import DisplayError
except ImportError:
# Fall back to absolute imports if relative imports fail # except ImportError:
# from videoarchiver.utils.exceptions import DisplayError # Fall back to absolute imports if relative imports fail
# from videoarchiver.utils.exceptions import DisplayError
logger = logging.getLogger("VideoArchiver") logger = logging.getLogger("VideoArchiver")

View File

@@ -16,12 +16,13 @@ import tempfile
import os import os
import shutil import shutil
try: # try:
# Try relative imports first # Try relative imports first
from .utils.exceptions import UpdateError from .utils.exceptions import UpdateError
except ImportError:
# Fall back to absolute imports if relative imports fail # except ImportError:
# from videoarchiver.utils.exceptions import UpdateError # Fall back to absolute imports if relative imports fail
# from videoarchiver.utils.exceptions import UpdateError
logger = logging.getLogger("VideoArchiver") logger = logging.getLogger("VideoArchiver")