fixed imports again

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

View File

@@ -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 *

View File

@@ -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",
]

View File

@@ -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")

View File

@@ -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,
}

View File

@@ -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}")

View File

@@ -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
"""

View File

@@ -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)

View File

@@ -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"]

View File

@@ -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")

View File

@@ -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")

View File

@@ -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:

View File

@@ -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")

View File

@@ -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")

View File

@@ -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")

View File

@@ -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")

View File

@@ -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(),
}

View File

@@ -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")

View File

@@ -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")

View File

@@ -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:

View File

@@ -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",
]

View File

@@ -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

View File

@@ -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

View File

@@ -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")

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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")

View File

@@ -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:

View File

@@ -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:

View File

@@ -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,
}
},
)

View File

@@ -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)}")

View File

@@ -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

View File

@@ -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")

View File

@@ -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

View File

@@ -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")

View File

@@ -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")

View File

@@ -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")