mirror of
https://github.com/pacnpal/Pac-cogs.git
synced 2025-12-20 10:51:05 -05:00
fixed imports again
This commit is contained in:
@@ -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 *
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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}")
|
||||
|
||||
@@ -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
|
||||
"""
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@@ -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)}")
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user