diff --git a/videoarchiver/__init__.py b/videoarchiver/__init__.py index 25aaa4f..35c3349 100644 --- a/videoarchiver/__init__.py +++ b/videoarchiver/__init__.py @@ -5,7 +5,7 @@ import asyncio import logging import importlib from typing import Optional -from redbot.core.bot import Red # type: ignore +from redbot.core.bot import Red # type: ignore # Force reload of all modules modules_to_reload = [ @@ -91,34 +91,34 @@ try: ) except ImportError: # Fall back to absolute imports if relative imports fail - from videoarchiver import utils - from videoarchiver import processor - from videoarchiver import queue - from videoarchiver import ffmpeg - from videoarchiver import database - from videoarchiver import config - from videoarchiver import core + # from videoarchiver import utils + # from videoarchiver import processor + # from videoarchiver import queue + # from videoarchiver import ffmpeg + # from videoarchiver import database + # from videoarchiver import config + # from videoarchiver import core # from videoarchiver.core.base import VideoArchiver # from videoarchiver.core.initialization import initialize_cog, init_callback # from videoarchiver.core.cleanup import cleanup_resources # from videoarchiver.utils.exceptions import ( - VideoArchiverError, - CommandError, - EventError, - CogError, - ErrorContext, - ErrorSeverity, - ProcessingError, - ) + # VideoArchiverError, + # CommandError, + # EventError, + # CogError, + # ErrorContext, + # ErrorSeverity, + # ProcessingError, + # ) -# Reload all modules -importlib.reload(utils) -importlib.reload(processor) -importlib.reload(queue) -importlib.reload(ffmpeg) -importlib.reload(database) -importlib.reload(config) -importlib.reload(core) + # Reload all modules + importlib.reload(utils) + importlib.reload(processor) + importlib.reload(queue) + importlib.reload(ffmpeg) + importlib.reload(database) + importlib.reload(config) + importlib.reload(core) # Import all submodules from .database import * diff --git a/videoarchiver/config/__init__.py b/videoarchiver/config/__init__.py index 396f4c9..537921c 100644 --- a/videoarchiver/config/__init__.py +++ b/videoarchiver/config/__init__.py @@ -1,49 +1,50 @@ """Configuration management module""" -try: - # Try relative imports first - from .exceptions import ( - ConfigurationError, - ValidationError, - PermissionError, - LoadError, - SaveError, - MigrationError, - SchemaError, - DiscordAPIError, - ) - from .channel_manager import ChannelManager - from .role_manager import RoleManager - from .settings_formatter import SettingsFormatter - from .validation_manager import ValidationManager -except ImportError: - # Fall back to absolute imports if relative imports fail - # from videoarchiver.config.exceptions import ( - ConfigurationError, - ValidationError, - PermissionError, - LoadError, - SaveError, - MigrationError, - SchemaError, - DiscordAPIError, - ) - # from videoarchiver.config.channel_manager import ChannelManager - # from videoarchiver.config.role_manager import RoleManager - # from videoarchiver.config.settings_formatter import SettingsFormatter - # from videoarchiver.config.validation_manager import ValidationManager +# try: +# Try relative imports first +from .exceptions import ( + ConfigurationError, + ValidationError, + PermissionError, + LoadError, + SaveError, + MigrationError, + SchemaError, + DiscordAPIError, +) +from .channel_manager import ChannelManager +from .role_manager import RoleManager +from .settings_formatter import SettingsFormatter +from .validation_manager import ValidationManager + +# except ImportError: +# Fall back to absolute imports if relative imports fail +# from videoarchiver.config.exceptions import ( +# ConfigurationError, +# ValidationError, +# PermissionError, +# LoadError, +# SaveError, +# MigrationError, +# SchemaError, +# DiscordAPIError, +# ) +# from videoarchiver.config.channel_manager import ChannelManager +# from videoarchiver.config.role_manager import RoleManager +# from videoarchiver.config.settings_formatter import SettingsFormatter +# from videoarchiver.config.validation_manager import ValidationManager __all__ = [ - 'ConfigurationError', - 'ValidationError', - 'PermissionError', - 'LoadError', - 'SaveError', - 'MigrationError', - 'SchemaError', - 'DiscordAPIError', - 'ChannelManager', - 'RoleManager', - 'SettingsFormatter', - 'ValidationManager', + "ConfigurationError", + "ValidationError", + "PermissionError", + "LoadError", + "SaveError", + "MigrationError", + "SchemaError", + "DiscordAPIError", + "ChannelManager", + "RoleManager", + "SettingsFormatter", + "ValidationManager", ] diff --git a/videoarchiver/config/channel_manager.py b/videoarchiver/config/channel_manager.py index b18ab30..bee17e7 100644 --- a/videoarchiver/config/channel_manager.py +++ b/videoarchiver/config/channel_manager.py @@ -2,20 +2,21 @@ import logging from typing import Dict, List, Optional, Tuple -import discord # type: ignore +import discord # type: ignore -try: - # Try relative imports first - from .exceptions import ( - ConfigurationError as ConfigError, - DiscordAPIError, - ) -except ImportError: - # Fall back to absolute imports if relative imports fail - # from videoarchiver.config.exceptions import ( - ConfigurationError as ConfigError, - DiscordAPIError, - ) +# try: +# Try relative imports first +from .exceptions import ( + ConfigurationError as ConfigError, + DiscordAPIError, +) + +# except ImportError: +# Fall back to absolute imports if relative imports fail +# from videoarchiver.config.exceptions import ( +# ConfigurationError as ConfigError, +# DiscordAPIError, +# ) logger = logging.getLogger("ChannelManager") diff --git a/videoarchiver/config/role_manager.py b/videoarchiver/config/role_manager.py index 6fc5164..29df9bc 100644 --- a/videoarchiver/config/role_manager.py +++ b/videoarchiver/config/role_manager.py @@ -2,14 +2,15 @@ import logging from typing import Dict, List, Set, Tuple, Optional, Any -import discord # type: ignore +import discord # type: ignore -try: - # Try relative imports first - from .exceptions import ConfigurationError as ConfigError -except ImportError: - # Fall back to absolute imports if relative imports fail - # from videoarchiver.config.exceptions import ConfigurationError as ConfigError +# try: +# Try relative imports first +from .exceptions import ConfigurationError as ConfigError + +# except ImportError: +# Fall back to absolute imports if relative imports fail +# from videoarchiver.config.exceptions import ConfigurationError as ConfigError logger = logging.getLogger("RoleManager") @@ -21,26 +22,24 @@ class RoleManager: self.config_manager = config_manager async def check_user_roles( - self, - member: discord.Member + self, member: discord.Member ) -> Tuple[bool, Optional[str]]: """Check if user has permission based on allowed roles - + Args: member: Discord member to check - + Returns: Tuple[bool, Optional[str]]: (Has permission, Reason if denied) - + Raises: ConfigError: If role check fails """ try: allowed_roles = await self.config_manager.get_setting( - member.guild.id, - "allowed_roles" + member.guild.id, "allowed_roles" ) - + # If no roles are set, allow all users if not allowed_roles: return True, None @@ -48,91 +47,73 @@ class RoleManager: # Check user roles user_roles = {role.id for role in member.roles} allowed_role_set = set(allowed_roles) - + if user_roles & allowed_role_set: # Intersection return True, None - + # Get role names for error message - missing_roles = await self._get_role_names( - member.guild, - allowed_role_set - ) + missing_roles = await self._get_role_names(member.guild, allowed_role_set) return False, f"Missing required roles: {', '.join(missing_roles)}" - + 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)}") - async def add_allowed_role( - self, - guild_id: int, - role_id: int - ) -> None: + async def add_allowed_role(self, guild_id: int, role_id: int) -> None: """Add a role to allowed roles - + Args: guild_id: Guild ID role_id: Role ID to add - + Raises: ConfigError: If role cannot be added """ try: - await self.config_manager.add_to_list( - guild_id, - "allowed_roles", - role_id - ) + await self.config_manager.add_to_list(guild_id, "allowed_roles", role_id) except Exception as e: logger.error(f"Failed to add allowed role {role_id}: {e}") raise ConfigError(f"Failed to add allowed role: {str(e)}") - async def remove_allowed_role( - self, - guild_id: int, - role_id: int - ) -> None: + async def remove_allowed_role(self, guild_id: int, role_id: int) -> None: """Remove a role from allowed roles - + Args: guild_id: Guild ID role_id: Role ID to remove - + Raises: ConfigError: If role cannot be removed """ try: await self.config_manager.remove_from_list( - guild_id, - "allowed_roles", - role_id + guild_id, "allowed_roles", role_id ) except Exception as e: logger.error(f"Failed to remove allowed role {role_id}: {e}") raise ConfigError(f"Failed to remove allowed role: {str(e)}") - async def get_allowed_roles( - self, - guild: discord.Guild - ) -> List[discord.Role]: + async def get_allowed_roles(self, guild: discord.Guild) -> List[discord.Role]: """Get all allowed roles for a guild - + Args: guild: Discord guild - + Returns: List[discord.Role]: List of allowed roles - + Raises: ConfigError: If roles cannot be retrieved """ try: settings = await self.config_manager.get_guild_settings(guild.id) role_ids = settings["allowed_roles"] - + roles = [] invalid_roles = [] - + for role_id in role_ids: role = guild.get_role(role_id) if role: @@ -140,55 +121,54 @@ class RoleManager: else: invalid_roles.append(role_id) logger.warning(f"Invalid role {role_id} in guild {guild.id}") - + # Clean up invalid roles if found if invalid_roles: await self._remove_invalid_roles(guild.id, invalid_roles) - + return roles - + except Exception as e: logger.error(f"Failed to get allowed roles for guild {guild.id}: {e}") raise ConfigError(f"Failed to get allowed roles: {str(e)}") async def verify_role_hierarchy( - self, - guild: discord.Guild, - role: discord.Role + self, guild: discord.Guild, role: discord.Role ) -> Tuple[bool, Optional[str]]: """Verify bot's role hierarchy position for managing a role - + Args: guild: Discord guild role: Role to check - + Returns: Tuple[bool, Optional[str]]: (Can manage role, Reason if not) """ try: bot_member = guild.me bot_top_role = bot_member.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 - + except Exception as e: logger.error(f"Error checking role hierarchy: {e}") return False, "Failed to check role hierarchy" async def _get_role_names( - self, - guild: discord.Guild, - role_ids: Set[int] + self, guild: discord.Guild, role_ids: Set[int] ) -> List[str]: """Get role names from role IDs - + Args: guild: Discord guild role_ids: Set of role IDs - + Returns: List[str]: List of role names """ @@ -199,13 +179,9 @@ class RoleManager: role_names.append(role.name) return role_names - async def _remove_invalid_roles( - self, - guild_id: int, - role_ids: List[int] - ) -> None: + async def _remove_invalid_roles(self, guild_id: int, role_ids: List[int]) -> None: """Remove invalid roles from allowed roles - + Args: guild_id: Guild ID role_ids: List of invalid role IDs to remove @@ -216,33 +192,30 @@ class RoleManager: except Exception as e: logger.error(f"Error removing invalid roles: {e}") - async def get_role_info( - self, - guild: discord.Guild - ) -> Dict[str, Any]: + async def get_role_info(self, guild: discord.Guild) -> Dict[str, Any]: """Get role configuration information - + Args: guild: Discord guild - + Returns: Dict[str, Any]: Dictionary containing role information """ try: allowed_roles = await self.get_allowed_roles(guild) bot_member = guild.me - + return { - 'allowed_roles': allowed_roles, - 'bot_top_role': bot_member.top_role, - 'bot_permissions': bot_member.guild_permissions, - 'role_count': len(allowed_roles) + "allowed_roles": allowed_roles, + "bot_top_role": bot_member.top_role, + "bot_permissions": bot_member.guild_permissions, + "role_count": len(allowed_roles), } except Exception as e: logger.error(f"Error getting role info: {e}") return { - 'allowed_roles': [], - 'bot_top_role': None, - 'bot_permissions': None, - 'role_count': 0 + "allowed_roles": [], + "bot_top_role": None, + "bot_permissions": None, + "role_count": 0, } diff --git a/videoarchiver/config/settings_formatter.py b/videoarchiver/config/settings_formatter.py index 24cc300..977d144 100644 --- a/videoarchiver/config/settings_formatter.py +++ b/videoarchiver/config/settings_formatter.py @@ -3,14 +3,15 @@ import logging from typing import Dict, Any, List from datetime import datetime -import discord # type: ignore +import discord # type: ignore -try: - # Try relative imports first - from .exceptions import ConfigurationError as ConfigError -except ImportError: - # Fall back to absolute imports if relative imports fail - # from videoarchiver.config.exceptions import ConfigurationError as ConfigError +# try: +# Try relative imports first +from .exceptions import ConfigurationError as ConfigError + +# except ImportError: +# Fall back to absolute imports if relative imports fail +# from videoarchiver.config.exceptions import ConfigurationError as ConfigError logger = logging.getLogger("SettingsFormatter") @@ -22,19 +23,17 @@ class SettingsFormatter: self.embed_color = discord.Color.blue() async def format_settings_embed( - self, - guild: discord.Guild, - settings: Dict[str, Any] + self, guild: discord.Guild, settings: Dict[str, Any] ) -> discord.Embed: """Format guild settings into a Discord embed - + Args: guild: Discord guild settings: Guild settings dictionary - + Returns: discord.Embed: Formatted settings embed - + Raises: ConfigError: If formatting fails """ @@ -42,7 +41,7 @@ class SettingsFormatter: embed = discord.Embed( title="Video Archiver Settings", color=self.embed_color, - timestamp=datetime.utcnow() + timestamp=datetime.utcnow(), ) # Add sections @@ -61,111 +60,98 @@ class SettingsFormatter: raise ConfigError(f"Failed to format settings: {str(e)}") async def _add_core_settings( - self, - embed: discord.Embed, - guild: discord.Guild, - settings: Dict[str, Any] + self, embed: discord.Embed, guild: discord.Guild, settings: Dict[str, Any] ) -> None: """Add core settings to embed""" embed.add_field( name="Core Settings", - value="\n".join([ - f"**Enabled:** {settings['enabled']}", - f"**Database Enabled:** {settings['use_database']}", - f"**Update Check Disabled:** {settings['disable_update_check']}" - ]), - inline=False + value="\n".join( + [ + f"**Enabled:** {settings['enabled']}", + f"**Database Enabled:** {settings['use_database']}", + f"**Update Check Disabled:** {settings['disable_update_check']}", + ] + ), + inline=False, ) async def _add_channel_settings( - self, - embed: discord.Embed, - guild: discord.Guild, - settings: Dict[str, Any] + self, embed: discord.Embed, guild: discord.Guild, settings: Dict[str, Any] ) -> None: """Add channel settings to embed""" # Get channels with error handling channels = await self._get_channel_mentions(guild, settings) - + embed.add_field( name="Channel Settings", - value="\n".join([ - f"**Archive Channel:** {channels['archive']}", - f"**Notification Channel:** {channels['notification']}", - f"**Log Channel:** {channels['log']}", - f"**Monitored Channels:**\n{channels['monitored']}" - ]), - inline=False + value="\n".join( + [ + f"**Archive Channel:** {channels['archive']}", + f"**Notification Channel:** {channels['notification']}", + f"**Log Channel:** {channels['log']}", + f"**Monitored Channels:**\n{channels['monitored']}", + ] + ), + inline=False, ) async def _add_permission_settings( - self, - embed: discord.Embed, - guild: discord.Guild, - settings: Dict[str, Any] + self, embed: discord.Embed, guild: discord.Guild, settings: Dict[str, Any] ) -> None: """Add permission settings to embed""" allowed_roles = await self._get_role_names(guild, settings["allowed_roles"]) - + embed.add_field( name="Permission Settings", value=f"**Allowed Roles:**\n{allowed_roles}", - inline=False + inline=False, ) async def _add_video_settings( - self, - embed: discord.Embed, - settings: Dict[str, Any] + self, embed: discord.Embed, settings: Dict[str, Any] ) -> None: """Add video settings to embed""" embed.add_field( name="Video Settings", - value="\n".join([ - f"**Format:** {settings['video_format']}", - f"**Max Quality:** {settings['video_quality']}p", - f"**Max File Size:** {settings['max_file_size']}MB" - ]), - inline=False + value="\n".join( + [ + f"**Format:** {settings['video_format']}", + f"**Max Quality:** {settings['video_quality']}p", + f"**Max File Size:** {settings['max_file_size']}MB", + ] + ), + inline=False, ) async def _add_operation_settings( - self, - embed: discord.Embed, - settings: Dict[str, Any] + self, embed: discord.Embed, settings: Dict[str, Any] ) -> None: """Add operation settings to embed""" embed.add_field( name="Operation Settings", - value="\n".join([ - f"**Delete After Repost:** {settings['delete_after_repost']}", - f"**Message Duration:** {settings['message_duration']} hours", - f"**Concurrent Downloads:** {settings['concurrent_downloads']}", - f"**Max Retries:** {settings['max_retries']}", - f"**Retry Delay:** {settings['retry_delay']}s" - ]), - inline=False + value="\n".join( + [ + f"**Delete After Repost:** {settings['delete_after_repost']}", + f"**Message Duration:** {settings['message_duration']} hours", + f"**Concurrent Downloads:** {settings['concurrent_downloads']}", + f"**Max Retries:** {settings['max_retries']}", + f"**Retry Delay:** {settings['retry_delay']}s", + ] + ), + inline=False, ) async def _add_site_settings( - self, - embed: discord.Embed, - settings: Dict[str, Any] + self, embed: discord.Embed, settings: Dict[str, Any] ) -> None: """Add site settings to embed""" enabled_sites = settings["enabled_sites"] sites_text = ", ".join(enabled_sites) if enabled_sites else "All sites" - - embed.add_field( - name="Enabled Sites", - value=sites_text, - inline=False - ) + + embed.add_field(name="Enabled Sites", value=sites_text, inline=False) async def _get_channel_mentions( - self, - guild: discord.Guild, - settings: Dict[str, Any] + self, guild: discord.Guild, settings: Dict[str, Any] ) -> Dict[str, str]: """Get channel mentions with error handling""" try: @@ -173,7 +159,7 @@ class SettingsFormatter: archive_channel = guild.get_channel(settings["archive_channel"]) notification_channel = guild.get_channel(settings["notification_channel"]) log_channel = guild.get_channel(settings["log_channel"]) - + # Get monitored channels monitored_channels = [] for channel_id in settings["monitored_channels"]: @@ -183,9 +169,17 @@ class SettingsFormatter: return { "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", - "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: @@ -194,14 +188,10 @@ class SettingsFormatter: "archive": "Error", "notification": "Error", "log": "Error", - "monitored": "Error getting channels" + "monitored": "Error getting channels", } - async def _get_role_names( - self, - guild: discord.Guild, - role_ids: List[int] - ) -> str: + async def _get_role_names(self, guild: discord.Guild, role_ids: List[int]) -> str: """Get role names with error handling""" try: role_names = [] @@ -209,8 +199,10 @@ class SettingsFormatter: role = guild.get_role(role_id) if role: 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: logger.error(f"Error getting role names: {e}") diff --git a/videoarchiver/config/validation_manager.py b/videoarchiver/config/validation_manager.py index 1e88cd9..d3e2c96 100644 --- a/videoarchiver/config/validation_manager.py +++ b/videoarchiver/config/validation_manager.py @@ -3,12 +3,13 @@ import logging from typing import Any, Dict, List, Union -try: - # Try relative imports first - from .exceptions import ConfigurationError as ConfigError -except ImportError: - # Fall back to absolute imports if relative imports fail - # from videoarchiver.config.exceptions import ConfigurationError as ConfigError +# try: +# Try relative imports first +from .exceptions import ConfigurationError as ConfigError + +# except ImportError: +# Fall back to absolute imports if relative imports fail +# from videoarchiver.config.exceptions import ConfigurationError as ConfigError logger = logging.getLogger("ConfigValidation") @@ -27,11 +28,11 @@ class ValidationManager: def validate_setting(self, setting: str, value: Any) -> None: """Validate a setting value against constraints - + Args: setting: Name of the setting to validate value: Value to validate - + Raises: ConfigError: If validation fails """ @@ -72,7 +73,9 @@ class ValidationManager: def _validate_concurrent_downloads(self, value: int) -> None: """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( 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: """Validate max retries setting""" if not isinstance(value, int) or not (0 <= value <= self.MAX_RETRIES): - raise ConfigError( - f"Max retries must be between 0 and {self.MAX_RETRIES}" - ) + raise ConfigError(f"Max retries must be between 0 and {self.MAX_RETRIES}") def _validate_retry_delay(self, value: int) -> None: """Validate retry delay setting""" @@ -124,17 +125,22 @@ class ValidationManager: if setting.endswith("_channel") and value is not None: if not isinstance(value, int): 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) elif setting in ["monitored_channels", "allowed_roles", "enabled_sites"]: self._validate_list(value) def validate_all_settings(self, settings: Dict[str, Any]) -> None: """Validate all settings in a configuration dictionary - + Args: settings: Dictionary of settings to validate - + Raises: ConfigError: If any validation fails """ diff --git a/videoarchiver/config_manager.py b/videoarchiver/config_manager.py index 3a0473a..4bf65a1 100644 --- a/videoarchiver/config_manager.py +++ b/videoarchiver/config_manager.py @@ -3,23 +3,24 @@ import logging import asyncio from typing import Dict, Any, Optional, List, Union -import discord # type: ignore -from redbot.core import Config # type: ignore +import discord # type: ignore +from redbot.core import Config # type: ignore -try: - # Try relative imports first - from .config.validation_manager import ValidationManager - from .config.settings_formatter import SettingsFormatter - from .config.channel_manager import ChannelManager - from .config.role_manager import RoleManager - from .utils.exceptions import ConfigurationError as ConfigError -except ImportError: - # Fall back to absolute imports if relative imports fail - # from videoarchiver.config.validation_manager import ValidationManager - # from videoarchiver.config.settings_formatter import SettingsFormatter - # from videoarchiver.config.channel_manager import ChannelManager - # from videoarchiver.config.role_manager import RoleManager - # from videoarchiver.utils.exceptions import ConfigurationError as ConfigError +# try: +# Try relative imports first +from .config.validation_manager import ValidationManager +from .config.settings_formatter import SettingsFormatter +from .config.channel_manager import ChannelManager +from .config.role_manager import RoleManager +from .utils.exceptions import ConfigurationError as ConfigError + +# except ImportError: +# Fall back to absolute imports if relative imports fail +# from videoarchiver.config.validation_manager import ValidationManager +# from videoarchiver.config.settings_formatter import SettingsFormatter +# from videoarchiver.config.channel_manager import ChannelManager +# from videoarchiver.config.role_manager import RoleManager +# from videoarchiver.utils.exceptions import ConfigurationError as ConfigError logger = logging.getLogger("VideoArchiver") @@ -55,13 +56,13 @@ class ConfigManager: """Initialize configuration managers""" self.config = bot_config self.config.register_guild(**self.default_guild) - + # Initialize managers self.validation_manager = ValidationManager() self.settings_formatter = SettingsFormatter() self.channel_manager = ChannelManager(self) self.role_manager = RoleManager(self) - + # Thread safety self._config_locks: Dict[int, asyncio.Lock] = {} @@ -80,108 +81,95 @@ class ConfigManager: logger.error(f"Failed to get guild settings for {guild_id}: {e}") raise ConfigError(f"Failed to get guild settings: {str(e)}") - async def update_setting( - self, - guild_id: int, - setting: str, - value: Any - ) -> None: + async def update_setting(self, guild_id: int, setting: str, value: Any) -> None: """Update a specific setting for a guild""" try: if setting not in self.default_guild: raise ConfigError(f"Invalid setting: {setting}") - + # Validate setting self.validation_manager.validate_setting(setting, value) - + async with await self._get_guild_lock(guild_id): await self.config.guild_from_id(guild_id).set_raw(setting, value=value) - + 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)}") - async def get_setting( - self, - guild_id: int, - setting: str - ) -> Any: + async def get_setting(self, guild_id: int, setting: str) -> Any: """Get a specific setting for a guild""" try: if setting not in self.default_guild: raise ConfigError(f"Invalid setting: {setting}") - + async with await self._get_guild_lock(guild_id): return await self.config.guild_from_id(guild_id).get_raw(setting) - + except Exception as e: logger.error(f"Failed to get setting {setting} for guild {guild_id}: {e}") raise ConfigError(f"Failed to get setting: {str(e)}") - async def toggle_setting( - self, - guild_id: int, - setting: str - ) -> bool: + async def toggle_setting(self, guild_id: int, setting: str) -> bool: """Toggle a boolean setting for a guild""" try: if setting not in self.default_guild: raise ConfigError(f"Invalid setting: {setting}") - + async with await self._get_guild_lock(guild_id): current = await self.get_setting(guild_id, setting) if not isinstance(current, bool): raise ConfigError(f"Setting {setting} is not a boolean") - + await self.update_setting(guild_id, setting, not current) return not current - + 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)}") - async def add_to_list( - self, - guild_id: int, - setting: str, - value: Any - ) -> None: + async def add_to_list(self, guild_id: int, setting: str, value: Any) -> None: """Add a value to a list setting""" try: if setting not in self.default_guild: raise ConfigError(f"Invalid setting: {setting}") - + 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): raise ConfigError(f"Setting {setting} is not a list") if value not in items: items.append(value) - + except Exception as 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)}") - async def remove_from_list( - self, - guild_id: int, - setting: str, - value: Any - ) -> None: + async def remove_from_list(self, guild_id: int, setting: str, value: Any) -> None: """Remove a value from a list setting""" try: if setting not in self.default_guild: raise ConfigError(f"Invalid setting: {setting}") - + 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): raise ConfigError(f"Setting {setting} is not a list") if value in items: items.remove(value) - + 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)}") 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)}") # 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""" 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""" return await self.channel_manager.get_monitored_channels(guild) diff --git a/videoarchiver/core/__init__.py b/videoarchiver/core/__init__.py index b45d77f..01dd7cd 100644 --- a/videoarchiver/core/__init__.py +++ b/videoarchiver/core/__init__.py @@ -1,10 +1,11 @@ """Core module for VideoArchiver cog""" -try: - # Try relative imports first - from .base import VideoArchiver -except ImportError: - # Fall back to absolute imports if relative imports fail - # from videoarchiver.core.base import VideoArchiver +# try: +# Try relative imports first +from .base import VideoArchiver + +# except ImportError: +# Fall back to absolute imports if relative imports fail +# from videoarchiver.core.base import VideoArchiver __all__ = ["VideoArchiver"] diff --git a/videoarchiver/core/base.py b/videoarchiver/core/base.py index 7996b9f..ae2e3d1 100644 --- a/videoarchiver/core/base.py +++ b/videoarchiver/core/base.py @@ -12,40 +12,41 @@ import discord # type: ignore from redbot.core.bot import Red # type: ignore from redbot.core.commands import GroupCog, Context # type: ignore -try: - # Try relative imports first - from .settings import Settings - from .lifecycle import LifecycleManager, LifecycleState - from .component_manager import ComponentManager, ComponentState - from .error_handler import error_manager, handle_command_error - from .response_handler import ResponseManager - from .commands.archiver_commands import setup_archiver_commands - from .commands.database_commands import setup_database_commands - from .commands.settings_commands import setup_settings_commands - from .events import setup_events, EventManager - from ..processor.core import VideoProcessor - from ..queue.manager import EnhancedVideoQueueManager - from ..ffmpeg.ffmpeg_manager import FFmpegManager - from ..database.video_archive_db import VideoArchiveDB - from ..config_manager import ConfigManager - from ..utils.exceptions import CogError, ErrorContext, ErrorSeverity -except ImportError: - # Fall back to absolute imports if relative imports fail - # from videoarchiver.core.settings import Settings - # from videoarchiver.core.lifecycle import LifecycleManager, LifecycleState - # from videoarchiver.core.component_manager import ComponentManager, ComponentState - # from videoarchiver.core.error_handler import error_manager, handle_command_error - # from videoarchiver.core.response_handler import ResponseManager - # from videoarchiver.core.commands.archiver_commands import setup_archiver_commands - # from videoarchiver.core.commands.database_commands import setup_database_commands - # from videoarchiver.core.commands.settings_commands import setup_settings_commands - # from videoarchiver.core.events import setup_events, EventManager - # from videoarchiver.processor.core import VideoProcessor - # from videoarchiver.queue.manager import EnhancedVideoQueueManager - # from videoarchiver.ffmpeg.ffmpeg_manager import FFmpegManager - # from videoarchiver.database.video_archive_db import VideoArchiveDB - # from videoarchiver.config_manager import ConfigManager - # from videoarchiver.utils.exceptions import CogError, ErrorContext, ErrorSeverity +# try: +# Try relative imports first +from .settings import Settings +from .lifecycle import LifecycleManager, LifecycleState +from .component_manager import ComponentManager, ComponentState +from .error_handler import error_manager, handle_command_error +from .response_handler import ResponseManager +from .commands.archiver_commands import setup_archiver_commands +from .commands.database_commands import setup_database_commands +from .commands.settings_commands import setup_settings_commands +from .events import setup_events, EventManager +from ..processor.core import VideoProcessor +from ..queue.manager import EnhancedVideoQueueManager +from ..ffmpeg.ffmpeg_manager import FFmpegManager +from ..database.video_archive_db import VideoArchiveDB +from ..config_manager import ConfigManager +from ..utils.exceptions import CogError, ErrorContext, ErrorSeverity + +# except ImportError: +# Fall back to absolute imports if relative imports fail +# from videoarchiver.core.settings import Settings +# from videoarchiver.core.lifecycle import LifecycleManager, LifecycleState +# from videoarchiver.core.component_manager import ComponentManager, ComponentState +# from videoarchiver.core.error_handler import error_manager, handle_command_error +# from videoarchiver.core.response_handler import ResponseManager +# from videoarchiver.core.commands.archiver_commands import setup_archiver_commands +# from videoarchiver.core.commands.database_commands import setup_database_commands +# from videoarchiver.core.commands.settings_commands import setup_settings_commands +# from videoarchiver.core.events import setup_events, EventManager +# from videoarchiver.processor.core import VideoProcessor +# from videoarchiver.queue.manager import EnhancedVideoQueueManager +# from videoarchiver.ffmpeg.ffmpeg_manager import FFmpegManager +# from videoarchiver.database.video_archive_db import VideoArchiveDB +# from videoarchiver.config_manager import ConfigManager +# from videoarchiver.utils.exceptions import CogError, ErrorContext, ErrorSeverity logger = logging.getLogger("VideoArchiver") diff --git a/videoarchiver/core/cleanup.py b/videoarchiver/core/cleanup.py index b46cd12..1c09bba 100644 --- a/videoarchiver/core/cleanup.py +++ b/videoarchiver/core/cleanup.py @@ -8,19 +8,19 @@ from enum import Enum, auto from pathlib import Path from typing import TYPE_CHECKING, Dict, Any, Optional, TypedDict, ClassVar -try: +#try: # Try relative imports first - from ..utils.file_ops import cleanup_downloads - from ..utils.exceptions import CleanupError, ErrorContext, ErrorSeverity -except ImportError: +from ..utils.file_ops import cleanup_downloads +from ..utils.exceptions import CleanupError, ErrorContext, ErrorSeverity +#except ImportError: # Fall back to absolute imports if relative imports fail # from videoarchiver.utils.file_ops import cleanup_downloads # from videoarchiver.utils.exceptions import CleanupError, ErrorContext, ErrorSeverity if TYPE_CHECKING: - try: - from .base import VideoArchiver - except ImportError: + #try: + from .base import VideoArchiver + #except ImportError: # from videoarchiver.core.base import VideoArchiver logger = logging.getLogger("VideoArchiver") diff --git a/videoarchiver/core/commands.py b/videoarchiver/core/commands.py index 167b99b..518a508 100644 --- a/videoarchiver/core/commands.py +++ b/videoarchiver/core/commands.py @@ -6,9 +6,9 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: - try: - from .base import VideoArchiver - except ImportError: + # try: + from .base import VideoArchiver + # except ImportError: # from videoarchiver.core.base import VideoArchiver def setup_commands(cog: "VideoArchiver") -> None: diff --git a/videoarchiver/core/component_manager.py b/videoarchiver/core/component_manager.py index a9a7995..d3940b6 100644 --- a/videoarchiver/core/component_manager.py +++ b/videoarchiver/core/component_manager.py @@ -20,22 +20,23 @@ from datetime import datetime from pathlib import Path import importlib -try: - # Try relative imports first - from ..utils.exceptions import ComponentError, ErrorContext, ErrorSeverity - from ..utils.path_manager import PathManager - from ..config_manager import ConfigManager - from ..processor.core import VideoProcessor - from ..queue.manager import EnhancedVideoQueueManager - from ..ffmpeg.ffmpeg_manager import FFmpegManager -except ImportError: - # Fall back to absolute imports if relative imports fail - # from videoarchiver.utils.exceptions import ComponentError, ErrorContext, ErrorSeverity - # from videoarchiver.utils.path_manager import PathManager - # from videoarchiver.config_manager import ConfigManager - # from videoarchiver.processor.core import VideoProcessor - # from videoarchiver.queue.manager import EnhancedVideoQueueManager - # from videoarchiver.ffmpeg.ffmpeg_manager import FFmpegManager +# try: +# Try relative imports first +from ..utils.exceptions import ComponentError, ErrorContext, ErrorSeverity +from ..utils.path_manager import PathManager +from ..config_manager import ConfigManager +from ..processor.core import VideoProcessor +from ..queue.manager import EnhancedVideoQueueManager +from ..ffmpeg.ffmpeg_manager import FFmpegManager + +# except ImportError: +# Fall back to absolute imports if relative imports fail +# from videoarchiver.utils.exceptions import ComponentError, ErrorContext, ErrorSeverity +# from videoarchiver.utils.path_manager import PathManager +# from videoarchiver.config_manager import ConfigManager +# from videoarchiver.processor.core import VideoProcessor +# from videoarchiver.queue.manager import EnhancedVideoQueueManager +# from videoarchiver.ffmpeg.ffmpeg_manager import FFmpegManager logger = logging.getLogger("VideoArchiver") diff --git a/videoarchiver/core/error_handler.py b/videoarchiver/core/error_handler.py index 37e0dff..689222e 100644 --- a/videoarchiver/core/error_handler.py +++ b/videoarchiver/core/error_handler.py @@ -14,49 +14,49 @@ from redbot.core.commands import ( # type: ignore CommandError ) -try: +#try: # Try relative imports first - from ..utils.exceptions import ( - VideoArchiverError, - ErrorSeverity, - ErrorContext, - ProcessorError, - ValidationError, - DisplayError, - URLExtractionError, - MessageHandlerError, - QueueHandlerError, - QueueProcessorError, - FFmpegError, - DatabaseError, - HealthCheckError, - TrackingError, - NetworkError, - ResourceExhaustedError, - ConfigurationError - ) - from ..core.response_handler import response_manager -except ImportError: - # Fall back to absolute imports if relative imports fail - # from videoarchiver.utils.exceptions import ( - VideoArchiverError, - ErrorSeverity, - ErrorContext, - ProcessorError, - ValidationError, - DisplayError, - URLExtractionError, - MessageHandlerError, - QueueHandlerError, - QueueProcessorError, - FFmpegError, - DatabaseError, - HealthCheckError, - TrackingError, - NetworkError, - ResourceExhaustedError, - ConfigurationError - ) +from ..utils.exceptions import ( + VideoArchiverError, + ErrorSeverity, + ErrorContext, + ProcessorError, + ValidationError, + DisplayError, + URLExtractionError, + MessageHandlerError, + QueueHandlerError, + QueueProcessorError, + FFmpegError, + DatabaseError, + HealthCheckError, + TrackingError, + NetworkError, + ResourceExhaustedError, + ConfigurationError +) +from ..core.response_handler import response_manager +# except ImportError: +# # Fall back to absolute imports if relative imports fail +# # from videoarchiver.utils.exceptions import ( +# VideoArchiverError, +# ErrorSeverity, +# ErrorContext, +# ProcessorError, +# ValidationError, +# DisplayError, +# URLExtractionError, +# MessageHandlerError, +# QueueHandlerError, +# QueueProcessorError, +# FFmpegError, +# DatabaseError, +# HealthCheckError, +# TrackingError, +# NetworkError, +# ResourceExhaustedError, +# ConfigurationError +# ) # from videoarchiver.core.response_handler import response_manager logger = logging.getLogger("VideoArchiver") diff --git a/videoarchiver/core/events.py b/videoarchiver/core/events.py index 8a18910..44f892b 100644 --- a/videoarchiver/core/events.py +++ b/videoarchiver/core/events.py @@ -9,15 +9,15 @@ from typing import TYPE_CHECKING, Dict, Any, Optional, TypedDict, ClassVar, List import discord # type: ignore -try: +#try: # Try relative imports first - from ..processor.constants import REACTIONS - from ..processor.reactions import handle_archived_reaction - from .guild import initialize_guild_components, cleanup_guild_components - from .error_handler import ErrorManager - from .response_handler import response_manager - from ..utils.exceptions import EventError, ErrorContext, ErrorSeverity -except ImportError: +from ..processor.constants import REACTIONS +from ..processor.reactions import handle_archived_reaction +from .guild import initialize_guild_components, cleanup_guild_components +from .error_handler import ErrorManager +from .response_handler import response_manager +from ..utils.exceptions import EventError, ErrorContext, ErrorSeverity +#except ImportError: # Fall back to absolute imports if relative imports fail # from videoarchiver.processor.constants import REACTIONS # from videoarchiver.processor.reactions import handle_archived_reaction @@ -27,9 +27,9 @@ except ImportError: # from videoarchiver.utils.exceptions import EventError, ErrorContext, ErrorSeverity if TYPE_CHECKING: - try: - from .base import VideoArchiver - except ImportError: + #try: + from .base import VideoArchiver + # except ImportError: # from videoarchiver.core.base import VideoArchiver logger = logging.getLogger("VideoArchiver") diff --git a/videoarchiver/core/guild.py b/videoarchiver/core/guild.py index bf2e446..39e3909 100644 --- a/videoarchiver/core/guild.py +++ b/videoarchiver/core/guild.py @@ -4,24 +4,25 @@ import logging from pathlib import Path from typing import TYPE_CHECKING, Dict, Any, Optional -try: - # Try relative imports first - from ..utils.download_core import DownloadCore - from ..utils.message_manager import MessageManager - from ..utils.file_ops import cleanup_downloads - from ..utils.exceptions import VideoArchiverError as ProcessingError -except ImportError: - # Fall back to absolute imports if relative imports fail - # from videoarchiver.utils.download_core import DownloadCore - # from videoarchiver.utils.message_manager import MessageManager - # from videoarchiver.utils.file_ops import cleanup_downloads - # from videoarchiver.utils.exceptions import VideoArchiverError as ProcessingError +# try: +# Try relative imports first +from ..utils.download_core import DownloadCore +from ..utils.message_manager import MessageManager +from ..utils.file_ops import cleanup_downloads +from ..utils.exceptions import VideoArchiverError as ProcessingError + +# except ImportError: +# Fall back to absolute imports if relative imports fail +# from videoarchiver.utils.download_core import DownloadCore +# from videoarchiver.utils.message_manager import MessageManager +# from videoarchiver.utils.file_ops import cleanup_downloads +# from videoarchiver.utils.exceptions import VideoArchiverError as ProcessingError if TYPE_CHECKING: - try: - from .base import VideoArchiver - except ImportError: - # from videoarchiver.core.base import VideoArchiver + # try: + from .base import VideoArchiver +# except ImportError: +# from videoarchiver.core.base import VideoArchiver logger = logging.getLogger("VideoArchiver") diff --git a/videoarchiver/core/initialization.py b/videoarchiver/core/initialization.py index d3b810f..7fa6359 100644 --- a/videoarchiver/core/initialization.py +++ b/videoarchiver/core/initialization.py @@ -4,20 +4,21 @@ from typing import TYPE_CHECKING, Optional, Dict, Any import asyncio import logging -try: - # Try relative imports first - from ..utils.exceptions import ComponentError, ErrorContext, ErrorSeverity - from .lifecycle import LifecycleState -except ImportError: - # Fall back to absolute imports if relative imports fail - # from videoarchiver.utils.exceptions import ComponentError, ErrorContext, ErrorSeverity - # from videoarchiver.core.lifecycle import LifecycleState +# try: +# Try relative imports first +from ..utils.exceptions import ComponentError, ErrorContext, ErrorSeverity +from .lifecycle import LifecycleState + +# except ImportError: +# Fall back to absolute imports if relative imports fail +# from videoarchiver.utils.exceptions import ComponentError, ErrorContext, ErrorSeverity +# from videoarchiver.core.lifecycle import LifecycleState if TYPE_CHECKING: - try: - from .base import VideoArchiver - except ImportError: - # from videoarchiver.core.base import VideoArchiver + # try: + from .base import VideoArchiver +# except ImportError: +# from videoarchiver.core.base import VideoArchiver logger = logging.getLogger("VideoArchiver") @@ -25,13 +26,13 @@ logger = logging.getLogger("VideoArchiver") async def initialize_cog(cog: "VideoArchiver") -> None: """ Initialize all components with proper error handling. - + This is a re-export of lifecycle_manager.initialize_cog with additional error context and logging. - + Args: cog: VideoArchiver cog instance - + Raises: ComponentError: If initialization fails """ @@ -48,18 +49,18 @@ async def initialize_cog(cog: "VideoArchiver") -> None: "Initialization", "initialize_cog", {"state": cog.lifecycle_manager.state_tracker.state.name}, - ErrorSeverity.HIGH - ) + ErrorSeverity.HIGH, + ), ) def init_callback(cog: "VideoArchiver", task: asyncio.Task) -> None: """ Handle initialization task completion. - + This is a re-export of lifecycle_manager.init_callback with additional error context and logging. - + Args: cog: VideoArchiver cog instance task: Initialization task @@ -67,7 +68,7 @@ def init_callback(cog: "VideoArchiver", task: asyncio.Task) -> None: try: logger.debug("Processing initialization task completion...") cog.lifecycle_manager.init_callback(task) - + # Log final state state = cog.lifecycle_manager.state_tracker.state if state == LifecycleState.READY: @@ -76,7 +77,7 @@ def init_callback(cog: "VideoArchiver", task: asyncio.Task) -> None: logger.error("Initialization failed") else: logger.warning(f"Unexpected state after initialization: {state.name}") - + except Exception as e: logger.error(f"Error in initialization callback: {str(e)}", exc_info=True) # We don't raise here since this is a callback @@ -85,10 +86,10 @@ def init_callback(cog: "VideoArchiver", task: asyncio.Task) -> None: def get_init_status(cog: "VideoArchiver") -> Dict[str, Any]: """ Get initialization status information. - + Args: cog: VideoArchiver cog instance - + Returns: Dictionary containing initialization status """ @@ -103,8 +104,8 @@ def get_init_status(cog: "VideoArchiver") -> Dict[str, Any]: "update_checker", "ffmpeg_mgr", "components", - "db" + "db", ] ), - "history": cog.lifecycle_manager.state_tracker.get_state_history() + "history": cog.lifecycle_manager.state_tracker.get_state_history(), } diff --git a/videoarchiver/core/lifecycle.py b/videoarchiver/core/lifecycle.py index 90d31c3..234fe05 100644 --- a/videoarchiver/core/lifecycle.py +++ b/videoarchiver/core/lifecycle.py @@ -7,26 +7,26 @@ from typing import Optional, Dict, Any, Set, List, Callable, TypedDict, ClassVar from enum import Enum, auto from datetime import datetime -try: +# try: # Try relative imports first - from .cleanup import cleanup_resources, force_cleanup_resources - from ..utils.exceptions import ( - VideoArchiverError, - ErrorContext, - ErrorSeverity, - ComponentError, - CleanupError, - ) -except ImportError: +from .cleanup import cleanup_resources, force_cleanup_resources +from ..utils.exceptions import ( + VideoArchiverError, + ErrorContext, + ErrorSeverity, + ComponentError, + CleanupError, +) +#except ImportError: # Fall back to absolute imports if relative imports fail # from videoarchiver.core.cleanup import cleanup_resources, force_cleanup_resources # from videoarchiver.utils.exceptions import ( - VideoArchiverError, - ErrorContext, - ErrorSeverity, - ComponentError, - CleanupError, - ) + # VideoArchiverError, + # ErrorContext, + # ErrorSeverity, + # ComponentError, + # CleanupError, + # ) logger = logging.getLogger("VideoArchiver") diff --git a/videoarchiver/core/response_handler.py b/videoarchiver/core/response_handler.py index 7fefadb..3e8e2bc 100644 --- a/videoarchiver/core/response_handler.py +++ b/videoarchiver/core/response_handler.py @@ -7,12 +7,13 @@ from datetime import datetime import discord # type: ignore from redbot.core.commands import Context # type: ignore -try: - # Try relative imports first - from ..utils.exceptions import ErrorSeverity -except ImportError: - # Fall back to absolute imports if relative imports fail - # from videoarchiver.utils.exceptions import ErrorSeverity +# try: +# Try relative imports first +from ..utils.exceptions import ErrorSeverity + +# except ImportError: +# Fall back to absolute imports if relative imports fail +# from videoarchiver.utils.exceptions import ErrorSeverity logger = logging.getLogger("VideoArchiver") diff --git a/videoarchiver/core/settings.py b/videoarchiver/core/settings.py index ba05b2f..590523e 100644 --- a/videoarchiver/core/settings.py +++ b/videoarchiver/core/settings.py @@ -4,29 +4,35 @@ from typing import Dict, Any, List, Optional, Union, TypedDict, ClassVar from dataclasses import dataclass, field from enum import Enum, auto -try: - # Try relative imports first - from ..utils.exceptions import ConfigurationError, ErrorContext, ErrorSeverity -except ImportError: - # Fall back to absolute imports if relative imports fail - # from videoarchiver.utils.exceptions import ConfigurationError, ErrorContext, ErrorSeverity +# try: +# Try relative imports first +from ..utils.exceptions import ConfigurationError, ErrorContext, ErrorSeverity + +# except ImportError: +# Fall back to absolute imports if relative imports fail +# from videoarchiver.utils.exceptions import ConfigurationError, ErrorContext, ErrorSeverity class VideoFormat(Enum): """Supported video formats""" + MP4 = "mp4" WEBM = "webm" MKV = "mkv" + class VideoQuality(Enum): """Video quality presets""" - LOW = "low" # 480p + + LOW = "low" # 480p MEDIUM = "medium" # 720p - HIGH = "high" # 1080p + HIGH = "high" # 1080p ULTRA = "ultra" # 4K + class SettingCategory(Enum): """Setting categories""" + GENERAL = auto() CHANNELS = auto() PERMISSIONS = auto() @@ -35,15 +41,19 @@ class SettingCategory(Enum): PERFORMANCE = auto() FEATURES = auto() + class ValidationResult(TypedDict): """Type definition for validation result""" + valid: bool error: Optional[str] details: Dict[str, Any] + @dataclass class SettingDefinition: """Defines a setting's properties""" + name: str category: SettingCategory default_value: Any @@ -66,8 +76,8 @@ class SettingDefinition: "Settings", "definition_validation", {"setting": self.name}, - ErrorSeverity.HIGH - ) + ErrorSeverity.HIGH, + ), ) if self.min_value is not None and self.max_value is not None: @@ -78,10 +88,11 @@ class SettingDefinition: "Settings", "definition_validation", {"setting": self.name}, - ErrorSeverity.HIGH - ) + ErrorSeverity.HIGH, + ), ) + class Settings: """Manages VideoArchiver settings""" @@ -92,7 +103,7 @@ class Settings: category=SettingCategory.GENERAL, default_value=False, description="Whether the archiver is enabled for this guild", - data_type=bool + data_type=bool, ), "archive_channel": SettingDefinition( name="archive_channel", @@ -101,7 +112,7 @@ class Settings: description="Channel where archived videos are posted", data_type=int, 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( name="log_channel", @@ -110,7 +121,7 @@ class Settings: description="Channel for logging archiver actions", data_type=int, 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( name="enabled_channels", @@ -118,7 +129,7 @@ class Settings: default_value=[], description="Channels to monitor (empty means all channels)", data_type=list, - error_message="Enabled channels must be a list of valid channel IDs" + error_message="Enabled channels must be a list of valid channel IDs", ), "allowed_roles": SettingDefinition( name="allowed_roles", @@ -126,7 +137,7 @@ class Settings: default_value=[], description="Roles allowed to use archiver (empty means all roles)", data_type=list, - error_message="Allowed roles must be a list of valid role IDs" + error_message="Allowed roles must be a list of valid role IDs", ), "video_format": SettingDefinition( name="video_format", @@ -135,7 +146,7 @@ class Settings: description="Format for archived videos", data_type=str, choices=[format.value for format in VideoFormat], - error_message=f"Video format must be one of: {', '.join(f.value for f in VideoFormat)}" + error_message=f"Video format must be one of: {', '.join(f.value for f in VideoFormat)}", ), "video_quality": SettingDefinition( name="video_quality", @@ -144,7 +155,7 @@ class Settings: description="Quality preset for archived videos", data_type=str, choices=[quality.value for quality in VideoQuality], - error_message=f"Video quality must be one of: {', '.join(q.value for q in VideoQuality)}" + error_message=f"Video quality must be one of: {', '.join(q.value for q in VideoQuality)}", ), "max_file_size": SettingDefinition( name="max_file_size", @@ -154,7 +165,7 @@ class Settings: data_type=int, min_value=1, 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( name="message_duration", @@ -164,7 +175,7 @@ class Settings: data_type=int, min_value=5, 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( name="message_template", @@ -172,7 +183,7 @@ class Settings: default_value="{author} archived a video from {channel}", description="Template for archive messages", data_type=str, - error_message="Message template must contain {author} and {channel} placeholders" + error_message="Message template must contain {author} and {channel} placeholders", ), "concurrent_downloads": SettingDefinition( name="concurrent_downloads", @@ -182,7 +193,7 @@ class Settings: data_type=int, min_value=1, 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( name="enabled_sites", @@ -191,14 +202,14 @@ class Settings: description="Sites to enable archiving for (None means all sites)", data_type=list, required=False, - error_message="Enabled sites must be a list of valid site identifiers" + error_message="Enabled sites must be a list of valid site identifiers", ), "use_database": SettingDefinition( name="use_database", category=SettingCategory.FEATURES, default_value=False, description="Enable database tracking of archived videos", - data_type=bool + data_type=bool, ), } @@ -206,23 +217,25 @@ class Settings: def get_setting_definition(cls, setting: str) -> Optional[SettingDefinition]: """ Get definition for a setting. - + Args: setting: Setting name - + Returns: Setting definition or None if not found """ return cls.SETTINGS.get(setting) @classmethod - def get_settings_by_category(cls, category: SettingCategory) -> Dict[str, SettingDefinition]: + def get_settings_by_category( + cls, category: SettingCategory + ) -> Dict[str, SettingDefinition]: """ Get all settings in a category. - + Args: category: Setting category - + Returns: Dictionary of settings in the category """ @@ -236,14 +249,14 @@ class Settings: def validate_setting(cls, setting: str, value: Any) -> ValidationResult: """ Validate a setting value. - + Args: setting: Setting name value: Value to validate - + Returns: Validation result dictionary - + Raises: ConfigurationError: If setting definition is not found """ @@ -252,18 +265,15 @@ class Settings: raise ConfigurationError( f"Unknown setting: {setting}", context=ErrorContext( - "Settings", - "validation", - {"setting": setting}, - ErrorSeverity.HIGH - ) + "Settings", "validation", {"setting": setting}, ErrorSeverity.HIGH + ), ) details = { "setting": setting, "value": value, "type": type(value).__name__, - "expected_type": definition.data_type.__name__ + "expected_type": definition.data_type.__name__, } # Check type @@ -271,15 +281,13 @@ class Settings: return ValidationResult( valid=False, error=f"Invalid type: expected {definition.data_type.__name__}, got {type(value).__name__}", - details=details + details=details, ) # Check required if definition.required and value is None: return ValidationResult( - valid=False, - error="Required setting cannot be None", - details=details + valid=False, error="Required setting cannot be None", details=details ) # Check choices @@ -287,7 +295,7 @@ class Settings: return ValidationResult( valid=False, error=f"Value must be one of: {', '.join(map(str, definition.choices))}", - details=details + details=details, ) # Check numeric bounds @@ -296,13 +304,13 @@ class Settings: return ValidationResult( valid=False, 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: return ValidationResult( valid=False, error=f"Value must be at most {definition.max_value}", - details=details + details=details, ) # Custom validation @@ -313,42 +321,33 @@ class Settings: return ValidationResult( valid=False, error=definition.error_message or "Validation failed", - details=details + details=details, ) except Exception as e: - return ValidationResult( - valid=False, - error=str(e), - details=details - ) + return ValidationResult(valid=False, error=str(e), details=details) - return ValidationResult( - valid=True, - error=None, - details=details - ) + return ValidationResult(valid=True, error=None, details=details) @property def default_guild_settings(self) -> Dict[str, Any]: """ Default settings for guild configuration. - + Returns: Dictionary of default settings """ return { - name: definition.default_value - for name, definition in self.SETTINGS.items() + name: definition.default_value for name, definition in self.SETTINGS.items() } @classmethod def get_setting_help(cls, setting: str) -> Optional[str]: """ Get help text for a setting. - + Args: setting: Setting name - + Returns: Help text or None if setting not found """ @@ -362,7 +361,7 @@ class Settings: f"Description: {definition.description}", f"Type: {definition.data_type.__name__}", f"Required: {definition.required}", - f"Default: {definition.default_value}" + f"Default: {definition.default_value}", ] if definition.choices: diff --git a/videoarchiver/database/__init__.py b/videoarchiver/database/__init__.py index e4cb070..2f44a29 100644 --- a/videoarchiver/database/__init__.py +++ b/videoarchiver/database/__init__.py @@ -1,21 +1,22 @@ """Database management package for video archiving""" -try: - # Try relative imports first - from .connection_manager import DatabaseConnectionManager - from .query_manager import DatabaseQueryManager - from .schema_manager import DatabaseSchemaManager - from .video_archive_db import VideoArchiveDB -except ImportError: - # Fall back to absolute imports if relative imports fail - # from videoarchiver.database.connection_manager import DatabaseConnectionManager - # from videoarchiver.database.query_manager import DatabaseQueryManager - # from videoarchiver.database.schema_manager import DatabaseSchemaManager - # from videoarchiver.database.video_archive_db import VideoArchiveDB +# try: +# Try relative imports first +from .connection_manager import DatabaseConnectionManager +from .query_manager import DatabaseQueryManager +from .schema_manager import DatabaseSchemaManager +from .video_archive_db import VideoArchiveDB + +# except ImportError: +# Fall back to absolute imports if relative imports fail +# from videoarchiver.database.connection_manager import DatabaseConnectionManager +# from videoarchiver.database.query_manager import DatabaseQueryManager +# from videoarchiver.database.schema_manager import DatabaseSchemaManager +# from videoarchiver.database.video_archive_db import VideoArchiveDB __all__ = [ - 'DatabaseConnectionManager', - 'DatabaseQueryManager', - 'DatabaseSchemaManager', - 'VideoArchiveDB' + "DatabaseConnectionManager", + "DatabaseQueryManager", + "DatabaseSchemaManager", + "VideoArchiveDB", ] diff --git a/videoarchiver/database/connection_manager.py b/videoarchiver/database/connection_manager.py index 7b11ba4..84143b1 100644 --- a/videoarchiver/database/connection_manager.py +++ b/videoarchiver/database/connection_manager.py @@ -10,10 +10,10 @@ import threading from queue import Queue, Empty from datetime import datetime -try: +#try: # Try relative imports first - from ..utils.exceptions import DatabaseError, ErrorContext, ErrorSeverity -except ImportError: +from ..utils.exceptions import DatabaseError, ErrorContext, ErrorSeverity +#except ImportError: # Fall back to absolute imports if relative imports fail # from videoarchiver.utils.exceptions import DatabaseError, ErrorContext, ErrorSeverity diff --git a/videoarchiver/database/schema_manager.py b/videoarchiver/database/schema_manager.py index 3989c7d..09d7ac5 100644 --- a/videoarchiver/database/schema_manager.py +++ b/videoarchiver/database/schema_manager.py @@ -7,10 +7,10 @@ from typing import List, Dict, Any, Optional, TypedDict, ClassVar, Union from enum import Enum, auto from datetime import datetime -try: +#try: # Try relative imports first - from ..utils.exceptions import DatabaseError, ErrorContext, ErrorSeverity -except ImportError: +from ..utils.exceptions import DatabaseError, ErrorContext, ErrorSeverity +#except ImportError: # Fall back to absolute imports if relative imports fail # from videoarchiver.utils.exceptions import DatabaseError, ErrorContext, ErrorSeverity diff --git a/videoarchiver/database/video_archive_db.py b/videoarchiver/database/video_archive_db.py index ba4e38b..92ef704 100644 --- a/videoarchiver/database/video_archive_db.py +++ b/videoarchiver/database/video_archive_db.py @@ -4,16 +4,17 @@ import logging from pathlib import Path from typing import Optional, Dict, Any, List -try: - # Try relative imports first - from .schema_manager import DatabaseSchemaManager - from .query_manager import DatabaseQueryManager - from .connection_manager import DatabaseConnectionManager -except ImportError: - # Fall back to absolute imports if relative imports fail - # from videoarchiver.database.schema_manager import DatabaseSchemaManager - # from videoarchiver.database.query_manager import DatabaseQueryManager - # from videoarchiver.database.connection_manager import DatabaseConnectionManager +# try: +# Try relative imports first +from .schema_manager import DatabaseSchemaManager +from .query_manager import DatabaseQueryManager +from .connection_manager import DatabaseConnectionManager + +# except ImportError: +# Fall back to absolute imports if relative imports fail +# from videoarchiver.database.schema_manager import DatabaseSchemaManager +# from videoarchiver.database.query_manager import DatabaseQueryManager +# from videoarchiver.database.connection_manager import DatabaseConnectionManager logger = logging.getLogger("VideoArchiverDB") diff --git a/videoarchiver/ffmpeg/__init__.py b/videoarchiver/ffmpeg/__init__.py index a7ccd4c..e73895b 100644 --- a/videoarchiver/ffmpeg/__init__.py +++ b/videoarchiver/ffmpeg/__init__.py @@ -18,33 +18,33 @@ logging.basicConfig( logger = logging.getLogger("VideoArchiver") # Import components after logging is configured -try: +#try: # Try relative imports first - from .ffmpeg_manager import FFmpegManager - from .video_analyzer import VideoAnalyzer - from .gpu_detector import GPUDetector - from .encoder_params import EncoderParams - from .ffmpeg_downloader import FFmpegDownloader - from .exceptions import ( - FFmpegError, - DownloadError, - VerificationError, - EncodingError, - AnalysisError, - GPUError, - HardwareAccelerationError, - FFmpegNotFoundError, - FFprobeError, - CompressionError, - FormatError, - PermissionError, - TimeoutError, - ResourceError, - QualityError, - AudioError, - BitrateError, - ) -except ImportError: +from .ffmpeg_manager import FFmpegManager +from .video_analyzer import VideoAnalyzer +from .gpu_detector import GPUDetector +from .encoder_params import EncoderParams +from .ffmpeg_downloader import FFmpegDownloader +from .exceptions import ( + FFmpegError, + DownloadError, + VerificationError, + EncodingError, + AnalysisError, + GPUError, + HardwareAccelerationError, + FFmpegNotFoundError, + FFprobeError, + CompressionError, + FormatError, + PermissionError, + TimeoutError, + ResourceError, + QualityError, + AudioError, + BitrateError, +) +#except ImportError: # Fall back to absolute imports if relative imports fail # from videoarchiver.ffmpeg.ffmpeg_manager import FFmpegManager # from videoarchiver.ffmpeg.video_analyzer import VideoAnalyzer @@ -52,24 +52,24 @@ except ImportError: # from videoarchiver.ffmpeg.encoder_params import EncoderParams # from videoarchiver.ffmpeg.ffmpeg_downloader import FFmpegDownloader # from videoarchiver.ffmpeg.exceptions import ( - FFmpegError, - DownloadError, - VerificationError, - EncodingError, - AnalysisError, - GPUError, - HardwareAccelerationError, - FFmpegNotFoundError, - FFprobeError, - CompressionError, - FormatError, - PermissionError, - TimeoutError, - ResourceError, - QualityError, - AudioError, - BitrateError, - ) + # FFmpegError, + # DownloadError, + # VerificationError, + # EncodingError, + # AnalysisError, + # GPUError, + # HardwareAccelerationError, + # FFmpegNotFoundError, + # FFprobeError, + # CompressionError, + # FormatError, + # PermissionError, + # TimeoutError, + # ResourceError, + # QualityError, + # AudioError, + # BitrateError, + # ) class FFmpeg: diff --git a/videoarchiver/ffmpeg/binary_manager.py b/videoarchiver/ffmpeg/binary_manager.py index a18af29..292e594 100644 --- a/videoarchiver/ffmpeg/binary_manager.py +++ b/videoarchiver/ffmpeg/binary_manager.py @@ -5,26 +5,26 @@ import os from pathlib import Path from typing import Dict, Optional -try: +#try: # Try relative imports first - from .exceptions import ( - FFmpegError, - DownloadError, - VerificationError, - PermissionError, - FFmpegNotFoundError - ) - from .ffmpeg_downloader import FFmpegDownloader - from .verification_manager import VerificationManager -except ImportError: +from .exceptions import ( + FFmpegError, + DownloadError, + VerificationError, + PermissionError, + FFmpegNotFoundError +) +from .ffmpeg_downloader import FFmpegDownloader +from .verification_manager import VerificationManager +#except ImportError: # Fall back to absolute imports if relative imports fail # from videoarchiver.ffmpeg.exceptions import ( - FFmpegError, - DownloadError, - VerificationError, - PermissionError, - FFmpegNotFoundError - ) + # FFmpegError, + # DownloadError, + # VerificationError, + # PermissionError, + # FFmpegNotFoundError + # ) # from videoarchiver.ffmpeg.ffmpeg_downloader import FFmpegDownloader # from videoarchiver.ffmpeg.verification_manager import VerificationManager diff --git a/videoarchiver/ffmpeg/encoder_params.py b/videoarchiver/ffmpeg/encoder_params.py index a865192..fc2ebbb 100644 --- a/videoarchiver/ffmpeg/encoder_params.py +++ b/videoarchiver/ffmpeg/encoder_params.py @@ -4,10 +4,10 @@ import os import logging from typing import Dict, Any -try: +#try: # Try relative imports first - from .exceptions import CompressionError, QualityError, BitrateError -except ImportError: +from .exceptions import CompressionError, QualityError, BitrateError +#except ImportError: # Fall back to absolute imports if relative imports fail # from videoarchiver.ffmpeg.exceptions import CompressionError, QualityError, BitrateError diff --git a/videoarchiver/ffmpeg/ffmpeg_downloader.py b/videoarchiver/ffmpeg/ffmpeg_downloader.py index 16cc38c..55670db 100644 --- a/videoarchiver/ffmpeg/ffmpeg_downloader.py +++ b/videoarchiver/ffmpeg/ffmpeg_downloader.py @@ -16,12 +16,13 @@ from typing import Optional, Dict, List import time import lzma -try: - # Try relative imports first - from .exceptions import DownloadError -except ImportError: - # Fall back to absolute imports if relative imports fail - # from videoarchiver.ffmpeg.exceptions import DownloadError +# try: +# Try relative imports first +from .exceptions import DownloadError + +# except ImportError: +# Fall back to absolute imports if relative imports fail +# from videoarchiver.ffmpeg.exceptions import DownloadError logger = logging.getLogger("VideoArchiver") @@ -249,14 +250,16 @@ class FFmpegDownloader: binary_files = [ f 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: # Fallback to old structure binary_files = [ f 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: raise DownloadError(f"{binary_name} not found in archive") @@ -271,10 +274,10 @@ class FFmpegDownloader: """Extract from tar archive (Linux/macOS)""" try: # 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 - with lzma.open(archive_path, 'rb') as compressed: - with open(decompressed_path, 'wb') as decompressed: + with lzma.open(archive_path, "rb") as compressed: + with open(decompressed_path, "wb") as decompressed: while True: chunk = compressed.read(chunk_size) if not chunk: @@ -289,12 +292,16 @@ class FFmpegDownloader: for binary_name in binary_names: # BtbN's builds have binaries in bin directory 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: # Fallback to old structure 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: raise DownloadError(f"{binary_name} not found in archive") @@ -304,9 +311,11 @@ class FFmpegDownloader: tar_ref.extract(member, temp_dir) extracted_path = Path(temp_dir) / binary_files[0] target_path = self.base_dir / binary_name - + # 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: chunk = src.read(chunk_size) if not chunk: @@ -314,7 +323,7 @@ class FFmpegDownloader: dst.write(chunk) # Allow other tasks to run time.sleep(0) - + logger.info(f"Extracted {binary_name} to {target_path}") # Clean up decompressed file @@ -350,7 +359,7 @@ class FFmpegDownloader: timeout=5, text=True, 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: logger.error("FFmpeg verification timed out") @@ -368,7 +377,7 @@ class FFmpegDownloader: timeout=5, text=True, 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: logger.error("FFprobe verification timed out") diff --git a/videoarchiver/ffmpeg/ffmpeg_manager.py b/videoarchiver/ffmpeg/ffmpeg_manager.py index 06ed405..87945ba 100644 --- a/videoarchiver/ffmpeg/ffmpeg_manager.py +++ b/videoarchiver/ffmpeg/ffmpeg_manager.py @@ -6,32 +6,29 @@ import multiprocessing from pathlib import Path from typing import Dict, Any, Optional -try: - # Try relative imports first - from .exceptions import ( - FFmpegError, - AnalysisError, - FFmpegNotFoundError - ) - from .gpu_detector import GPUDetector - from .video_analyzer import VideoAnalyzer - from .encoder_params import EncoderParams - from .process_manager import ProcessManager - from .verification_manager import VerificationManager - from .binary_manager import BinaryManager -except ImportError: - # Fall back to absolute imports if relative imports fail - # from videoarchiver.ffmpeg.exceptions import ( - FFmpegError, - AnalysisError, - FFmpegNotFoundError - ) - # from videoarchiver.ffmpeg.gpu_detector import GPUDetector - # from videoarchiver.ffmpeg.video_analyzer import VideoAnalyzer - # from videoarchiver.ffmpeg.encoder_params import EncoderParams - # from videoarchiver.ffmpeg.process_manager import ProcessManager - # from videoarchiver.ffmpeg.verification_manager import VerificationManager - # from videoarchiver.ffmpeg.binary_manager import BinaryManager +# try: +# Try relative imports first +from .exceptions import FFmpegError, AnalysisError, FFmpegNotFoundError +from .gpu_detector import GPUDetector +from .video_analyzer import VideoAnalyzer +from .encoder_params import EncoderParams +from .process_manager import ProcessManager +from .verification_manager import VerificationManager +from .binary_manager import BinaryManager + +# except ImportError: +# Fall back to absolute imports if relative imports fail +# from videoarchiver.ffmpeg.exceptions import ( +# FFmpegError, +# AnalysisError, +# FFmpegNotFoundError +# ) +# from videoarchiver.ffmpeg.gpu_detector import GPUDetector +# from videoarchiver.ffmpeg.video_analyzer import VideoAnalyzer +# from videoarchiver.ffmpeg.encoder_params import EncoderParams +# 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") @@ -45,7 +42,7 @@ class FFmpegManager: module_dir = Path(__file__).parent.parent self.base_dir = module_dir / "bin" logger.info(f"FFmpeg base directory: {self.base_dir}") - + # Initialize managers self.process_manager = ProcessManager() self.verification_manager = VerificationManager(self.process_manager) @@ -53,15 +50,15 @@ class FFmpegManager: base_dir=self.base_dir, system=platform.system(), machine=platform.machine(), - verification_manager=self.verification_manager + verification_manager=self.verification_manager, ) - + # Initialize components self.gpu_detector = GPUDetector(self.get_ffmpeg_path) self.video_analyzer = VideoAnalyzer(self.get_ffmpeg_path) self._gpu_info = self.gpu_detector.detect_gpu() self._cpu_cores = multiprocessing.cpu_count() - + # Initialize encoder params self.encoder_params = EncoderParams(self._cpu_cores, self._gpu_info) @@ -87,22 +84,24 @@ class FFmpegManager: raise 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""" try: # Analyze video first video_info = self.analyze_video(input_path) if not video_info: raise AnalysisError("Failed to analyze video") - + # Convert target size to bytes target_size_bytes = target_size_mb * 1024 * 1024 - + # Get encoding parameters params = self.encoder_params.get_params(video_info, target_size_bytes) logger.info(f"Generated compression parameters: {params}") return params - + except Exception as e: logger.error(f"Failed to get compression parameters: {e}") if isinstance(e, AnalysisError): @@ -113,7 +112,7 @@ class FFmpegManager: "preset": "medium", "crf": "23", "c:a": "aac", - "b:a": "128k" + "b:a": "128k", } def get_ffmpeg_path(self) -> str: diff --git a/videoarchiver/ffmpeg/verification_manager.py b/videoarchiver/ffmpeg/verification_manager.py index 33130db..4be7169 100644 --- a/videoarchiver/ffmpeg/verification_manager.py +++ b/videoarchiver/ffmpeg/verification_manager.py @@ -6,22 +6,23 @@ import subprocess from pathlib import Path from typing import Dict, List, Optional -try: - # Try relative imports first - from .exceptions import ( - TimeoutError, - VerificationError, - EncodingError, - handle_ffmpeg_error - ) -except ImportError: - # Fall back to absolute imports if relative imports fail - # from videoarchiver.ffmpeg.exceptions import ( - TimeoutError, - VerificationError, - EncodingError, - handle_ffmpeg_error - ) +# try: +# Try relative imports first +from .exceptions import ( + TimeoutError, + VerificationError, + EncodingError, + handle_ffmpeg_error, +) + +# except ImportError: +# Fall back to absolute imports if relative imports fail +# from videoarchiver.ffmpeg.exceptions import ( +# TimeoutError, +# VerificationError, +# EncodingError, +# handle_ffmpeg_error +# ) logger = logging.getLogger("FFmpegVerification") @@ -33,18 +34,15 @@ class VerificationManager: self.process_manager = process_manager def verify_ffmpeg( - self, - ffmpeg_path: Path, - ffprobe_path: Path, - gpu_info: Dict[str, bool] + self, ffmpeg_path: Path, ffprobe_path: Path, gpu_info: Dict[str, bool] ) -> None: """Verify FFmpeg functionality with comprehensive checks - + Args: ffmpeg_path: Path to FFmpeg binary ffprobe_path: Path to FFprobe binary gpu_info: Dictionary of GPU availability - + Raises: VerificationError: If verification fails TimeoutError: If verification times out @@ -53,13 +51,13 @@ class VerificationManager: try: # Check FFmpeg version self._verify_ffmpeg_version(ffmpeg_path) - + # Check FFprobe version self._verify_ffprobe_version(ffprobe_path) - + # Check FFmpeg capabilities self._verify_ffmpeg_capabilities(ffmpeg_path, gpu_info) - + logger.info("FFmpeg verification completed successfully") except Exception as e: @@ -72,8 +70,7 @@ class VerificationManager: """Verify FFmpeg version""" try: result = self._execute_command( - [str(ffmpeg_path), "-version"], - "FFmpeg version check" + [str(ffmpeg_path), "-version"], "FFmpeg version check" ) logger.info(f"FFmpeg version: {result.stdout.split()[2]}") except Exception as e: @@ -83,34 +80,32 @@ class VerificationManager: """Verify FFprobe version""" try: result = self._execute_command( - [str(ffprobe_path), "-version"], - "FFprobe version check" + [str(ffprobe_path), "-version"], "FFprobe version check" ) logger.info(f"FFprobe version: {result.stdout.split()[2]}") except Exception as e: raise VerificationError(f"FFprobe version check failed: {e}") def _verify_ffmpeg_capabilities( - self, - ffmpeg_path: Path, - gpu_info: Dict[str, bool] + self, ffmpeg_path: Path, gpu_info: Dict[str, bool] ) -> None: """Verify FFmpeg capabilities and encoders""" try: result = self._execute_command( [str(ffmpeg_path), "-hide_banner", "-encoders"], - "FFmpeg capabilities check" + "FFmpeg capabilities check", ) # Verify required encoders required_encoders = self._get_required_encoders(gpu_info) available_encoders = result.stdout.lower() - + missing_encoders = [ - encoder for encoder in required_encoders + encoder + for encoder in required_encoders if encoder not in available_encoders ] - + if missing_encoders: logger.warning(f"Missing encoders: {', '.join(missing_encoders)}") if "libx264" in missing_encoders: @@ -122,17 +117,12 @@ class VerificationManager: raise VerificationError(f"FFmpeg capabilities check failed: {e}") def _execute_command( - self, - command: List[str], - operation: str, - timeout: int = 10 + self, command: List[str], operation: str, timeout: int = 10 ) -> subprocess.CompletedProcess: """Execute a command with proper error handling""" try: result = self.process_manager.execute_command( - command, - timeout=timeout, - check=False + command, timeout=timeout, check=False ) if result.returncode != 0: @@ -152,14 +142,14 @@ class VerificationManager: def _get_required_encoders(self, gpu_info: Dict[str, bool]) -> List[str]: """Get list of required encoders based on GPU availability""" required_encoders = ["libx264"] - + if gpu_info["nvidia"]: required_encoders.append("h264_nvenc") elif gpu_info["amd"]: required_encoders.append("h264_amf") elif gpu_info["intel"]: required_encoders.append("h264_qsv") - + return required_encoders def verify_binary_permissions(self, binary_path: Path) -> None: diff --git a/videoarchiver/processor/core.py b/videoarchiver/processor/core.py index 94791a2..dd277e9 100644 --- a/videoarchiver/processor/core.py +++ b/videoarchiver/processor/core.py @@ -8,10 +8,17 @@ from typing import Any, ClassVar, Dict, List, Optional, Tuple, TYPE_CHECKING import discord # type: ignore from discord.ext import commands # type: ignore -# from videoarchiver.core.types import ComponentState, ProcessorState, ComponentStatus, IComponent, IConfigManager, IQueueManager -# from videoarchiver.processor.constants import REACTIONS -# from videoarchiver.utils.progress_tracker import ProgressTracker -# from videoarchiver.utils.exceptions import ProcessorError +from ..core.types import ( + ComponentState, + ProcessorState, + ComponentStatus, + IComponent, + IConfigManager, + IQueueManager, +) +from .constants import REACTIONS +from ..utils.progress_tracker import ProgressTracker +from ..utils.exceptions import ProcessorError logger = logging.getLogger("VideoArchiver") @@ -45,11 +52,13 @@ class OperationTracker: ) -> None: """End tracking an operation""" if op_id in self.operations: - self.operations[op_id].update({ - "end_time": datetime.utcnow(), - "status": "success" if success else "error", - "error": error, - }) + self.operations[op_id].update( + { + "end_time": datetime.utcnow(), + "status": "success" if success else "error", + "error": error, + } + ) # Move to history self.operation_history.append(self.operations.pop(op_id)) # Update counts @@ -60,7 +69,7 @@ class OperationTracker: # Cleanup old history if needed 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]]: """Get currently active operations""" @@ -114,11 +123,13 @@ class HealthMonitor: self.last_check = datetime.utcnow() # Check component health - self.health_status.update({ - "queue_handler": self.processor.queue_handler.is_healthy(), - "message_handler": self.processor.message_handler.is_healthy(), - "progress_tracker": self.progress_tracker.is_healthy(), - }) + self.health_status.update( + { + "queue_handler": self.processor.queue_handler.is_healthy(), + "message_handler": self.processor.message_handler.is_healthy(), + "progress_tracker": self.progress_tracker.is_healthy(), + } + ) # Check operation health op_stats = self.processor.operation_tracker.get_operation_stats() @@ -168,8 +179,12 @@ class VideoProcessor(IComponent): from .cleanup_manager import CleanupManager, CleanupStrategy # Initialize handlers - self.queue_handler: "QueueHandler" = QueueHandler(bot, config_manager, components) - self.message_handler: "MessageHandler" = MessageHandler(bot, config_manager, queue_manager) + self.queue_handler: "QueueHandler" = QueueHandler( + bot, config_manager, components + ) + self.message_handler: "MessageHandler" = MessageHandler( + bot, config_manager, queue_manager + ) self.cleanup_manager: "CleanupManager" = CleanupManager( self.queue_handler, ffmpeg_mgr, CleanupStrategy.NORMAL ) @@ -243,9 +258,7 @@ class VideoProcessor(IComponent): async def cleanup(self) -> None: """Clean up resources and stop processing""" - op_id = self.operation_tracker.start_operation( - "cleanup", {"type": "normal"} - ) + op_id = self.operation_tracker.start_operation("cleanup", {"type": "normal"}) try: self._state = ProcessorState.SHUTDOWN @@ -260,9 +273,7 @@ class VideoProcessor(IComponent): async def force_cleanup(self) -> None: """Force cleanup of resources""" - op_id = self.operation_tracker.start_operation( - "cleanup", {"type": "force"} - ) + op_id = self.operation_tracker.start_operation("cleanup", {"type": "force"}) try: self._state = ProcessorState.SHUTDOWN @@ -321,5 +332,5 @@ class VideoProcessor(IComponent): "operations": self.operation_tracker.get_operation_stats(), "active_operations": self.operation_tracker.get_active_operations(), "health_status": self.health_monitor.health_status, - } + }, ) diff --git a/videoarchiver/processor/message_handler.py b/videoarchiver/processor/message_handler.py index 281e6d7..7ba392d 100644 --- a/videoarchiver/processor/message_handler.py +++ b/videoarchiver/processor/message_handler.py @@ -4,33 +4,45 @@ import asyncio import logging from datetime import datetime, timedelta 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 from discord.ext import commands # type: ignore -try: - # Try relative imports first - from ..config_manager import ConfigManager - from .constants import REACTIONS - from .message_validator import MessageValidator, ValidationError - from .url_extractor import URLExtractor, URLMetadata - from ..queue.types import QueuePriority - from ..utils.exceptions import MessageHandlerError -except ImportError: - # Fall back to absolute imports if relative imports fail - # from videoarchiver.config_manager import ConfigManager - # from videoarchiver.processor.constants import REACTIONS - # from videoarchiver.processor.message_validator import MessageValidator, ValidationError - # from videoarchiver.processor.url_extractor import URLExtractor, URLMetadata - # from videoarchiver.queue.types import QueuePriority - # from videoarchiver.utils.exceptions import MessageHandlerError +# try: +# Try relative imports first +from ..config_manager import ConfigManager +from .constants import REACTIONS +from .message_validator import MessageValidator, ValidationError +from .url_extractor import URLExtractor, URLMetadata +from ..queue.types import QueuePriority +from ..utils.exceptions import MessageHandlerError + +# except ImportError: +# Fall back to absolute imports if relative imports fail +# from videoarchiver.config_manager import ConfigManager +# from videoarchiver.processor.constants import REACTIONS +# from videoarchiver.processor.message_validator import MessageValidator, ValidationError +# from videoarchiver.processor.url_extractor import URLExtractor, URLMetadata +# from videoarchiver.queue.types import QueuePriority +# from videoarchiver.utils.exceptions import MessageHandlerError if TYPE_CHECKING: - try: - from ..queue.manager import EnhancedVideoQueueManager - except ImportError: - # from videoarchiver.queue.manager import EnhancedVideoQueueManager + # try: + from ..queue.manager import EnhancedVideoQueueManager + + # except ImportError: + # from videoarchiver.queue.manager import EnhancedVideoQueueManager logger = logging.getLogger("VideoArchiver") @@ -337,7 +349,7 @@ class MessageHandler: channel_id=message.channel.id, guild_id=message.guild.id, author_id=message.author.id, - priority=QueuePriority.NORMAL.value + priority=QueuePriority.NORMAL.value, ) except Exception as e: raise MessageHandlerError(f"Queue processing failed: {str(e)}") diff --git a/videoarchiver/processor/message_validator.py b/videoarchiver/processor/message_validator.py index 3e0f0b9..85e279f 100644 --- a/videoarchiver/processor/message_validator.py +++ b/videoarchiver/processor/message_validator.py @@ -7,10 +7,10 @@ from typing import Dict, Optional, Tuple, List, Any, Callable, Set, TypedDict, C from datetime import datetime import discord # type: ignore -try: +#try: # Try relative imports first - from ..utils.exceptions import ValidationError -except ImportError: +from ..utils.exceptions import ValidationError +#except ImportError: # Fall back to absolute imports if relative imports fail # from videoarchiver.utils.exceptions import ValidationError diff --git a/videoarchiver/processor/queue_handler.py b/videoarchiver/processor/queue_handler.py index 9b5235e..9f76713 100644 --- a/videoarchiver/processor/queue_handler.py +++ b/videoarchiver/processor/queue_handler.py @@ -8,27 +8,28 @@ from typing import Optional, Dict, Any, List, Tuple, Set, TypedDict, ClassVar, C from datetime import datetime import discord # type: ignore -try: - # Try relative imports first - from .. import utils - from ..database.video_archive_db import VideoArchiveDB - from ..utils.download_manager import DownloadManager - from ..utils.message_manager import MessageManager - from ..utils.exceptions import QueueHandlerError - from ..queue.models import QueueItem - from ..config_manager import ConfigManager - from . import progress_tracker # Import from processor package - from .constants import REACTIONS -except ImportError: - # Fall back to absolute imports if relative imports fail - # from videoarchiver.database.video_archive_db import VideoArchiveDB - # from videoarchiver.utils.download_manager import DownloadManager - # from videoarchiver.utils.message_manager import MessageManager - # from videoarchiver.utils.exceptions import QueueHandlerError - # from videoarchiver.queue.models import QueueItem - # from videoarchiver.config_manager import ConfigManager - # from videoarchiver.processor import progress_tracker # Import from processor package - # from videoarchiver.processor.constants import REACTIONS +# try: +# Try relative imports first +from .. import utils +from ..database.video_archive_db import VideoArchiveDB +from ..utils.download_manager import DownloadManager +from ..utils.message_manager import MessageManager +from ..utils.exceptions import QueueHandlerError +from ..queue.models import QueueItem +from ..config_manager import ConfigManager +from . import progress_tracker # Import from processor package +from .constants import REACTIONS + +# except ImportError: +# Fall back to absolute imports if relative imports fail +# from videoarchiver.database.video_archive_db import VideoArchiveDB +# from videoarchiver.utils.download_manager import DownloadManager +# from videoarchiver.utils.message_manager import MessageManager +# from videoarchiver.utils.exceptions import QueueHandlerError +# from videoarchiver.queue.models import QueueItem +# from videoarchiver.config_manager import ConfigManager +# from videoarchiver.processor import progress_tracker # Import from processor package +# from videoarchiver.processor.constants import REACTIONS logger = logging.getLogger("VideoArchiver") diff --git a/videoarchiver/processor/queue_processor.py b/videoarchiver/processor/queue_processor.py index 01bbfcd..a79caaa 100644 --- a/videoarchiver/processor/queue_processor.py +++ b/videoarchiver/processor/queue_processor.py @@ -5,11 +5,11 @@ import asyncio from typing import List, Optional, Dict, Any, Set, ClassVar from datetime import datetime -try: +#try: # Try relative imports first - from ..queue.types import QueuePriority, QueueMetrics, ProcessingMetrics - from ..queue.models import QueueItem -except ImportError: +from ..queue.types import QueuePriority, QueueMetrics, ProcessingMetrics +from ..queue.models import QueueItem +#except ImportError: # Fall back to absolute imports if relative imports fail # from videoarchiver.queue.types import QueuePriority, QueueMetrics, ProcessingMetrics # from videoarchiver.queue.models import QueueItem diff --git a/videoarchiver/processor/reactions.py b/videoarchiver/processor/reactions.py index 483c0a9..f040b0c 100644 --- a/videoarchiver/processor/reactions.py +++ b/videoarchiver/processor/reactions.py @@ -7,24 +7,25 @@ from typing import List, Optional import discord # type: ignore from urllib.parse import urlparse -try: - # Try relative imports first - from ..processor.constants import ( - REACTIONS, - ReactionType, - get_reaction, - get_progress_emoji, - ) - from ..database.video_archive_db import VideoArchiveDB -except ImportError: - # Fall back to absolute imports if relative imports fail - # from videoarchiver.processor.constants import ( - REACTIONS, - ReactionType, - get_reaction, - get_progress_emoji, - ) - # from videoarchiver.database.video_archive_db import VideoArchiveDB +# try: +# Try relative imports first +from ..processor.constants import ( + REACTIONS, + ReactionType, + get_reaction, + get_progress_emoji, +) +from ..database.video_archive_db import VideoArchiveDB + +# except ImportError: +# Fall back to absolute imports if relative imports fail +# from videoarchiver.processor.constants import ( +# REACTIONS, +# ReactionType, +# get_reaction, +# get_progress_emoji, +# ) +# from videoarchiver.database.video_archive_db import VideoArchiveDB logger = logging.getLogger("VideoArchiver") diff --git a/videoarchiver/processor/status_display.py b/videoarchiver/processor/status_display.py index 3a9703e..3b942b3 100644 --- a/videoarchiver/processor/status_display.py +++ b/videoarchiver/processor/status_display.py @@ -18,12 +18,13 @@ from typing import ( ) import discord # type: ignore -try: - # Try relative imports first - from ..utils.exceptions import DisplayError -except ImportError: - # Fall back to absolute imports if relative imports fail - # from videoarchiver.utils.exceptions import DisplayError +# try: +# Try relative imports first +from ..utils.exceptions import DisplayError + +# except ImportError: +# Fall back to absolute imports if relative imports fail +# from videoarchiver.utils.exceptions import DisplayError logger = logging.getLogger("VideoArchiver") diff --git a/videoarchiver/update_checker.py b/videoarchiver/update_checker.py index be25a03..d8cc352 100644 --- a/videoarchiver/update_checker.py +++ b/videoarchiver/update_checker.py @@ -5,7 +5,7 @@ from importlib.metadata import version as get_package_version from datetime import datetime, timedelta import aiohttp from packaging import version -import discord # type: ignore +import discord # type: ignore from typing import Optional, Tuple, Dict, Any import asyncio import sys @@ -16,12 +16,13 @@ import tempfile import os import shutil -try: - # Try relative imports first - from .utils.exceptions import UpdateError -except ImportError: - # Fall back to absolute imports if relative imports fail - # from videoarchiver.utils.exceptions import UpdateError +# try: +# Try relative imports first +from .utils.exceptions import UpdateError + +# except ImportError: +# Fall back to absolute imports if relative imports fail +# from videoarchiver.utils.exceptions import UpdateError logger = logging.getLogger("VideoArchiver")