Improve VideoArchiver cog reliability

- Add proper error handling for pkg_resources import
- Add setuptools to requirements for pkg_resources
- Add last_update_check tracking to prevent excessive checks
- Add timeout to GitHub API requests
- Improve error handling in update checker
- Add update check status to settings display
- Fix potential race conditions in update checker
This commit is contained in:
pacnpal
2024-11-14 19:58:34 +00:00
parent 6c2e277211
commit a8cbe110a4
2 changed files with 93 additions and 17 deletions

23
videoarchiver/info.json Normal file
View File

@@ -0,0 +1,23 @@
{
"name": "VideoArchiver",
"author": ["PacNPal"],
"description": "A powerful Discord video archiver cog that automatically downloads and reposts videos from monitored channels. Features include:\n- GPU-accelerated video compression (NVIDIA, AMD, Intel)\n- Multi-core CPU utilization\n- Concurrent multi-video processing\n- Intelligent quality preservation\n- Support for multiple video sites\n- Customizable archive messages\n- Automatic cleanup\n- Automatic yt-dlp updates",
"short": "Archive videos from Discord channels with GPU-accelerated compression",
"tags": [
"video",
"archive",
"download",
"compression",
"media"
],
"requirements": [
"yt-dlp>=2024.11.4",
"ffmpeg-python>=0.2.0",
"requests>=2.32.3",
"setuptools>=65.5.1"
],
"min_bot_version": "3.5.0",
"hidden": false,
"disabled": false,
"type": "COG"
}

View File

@@ -13,8 +13,14 @@ import asyncio
import subprocess import subprocess
from typing import Optional, List, Set, Dict from typing import Optional, List, Set, Dict
import sys import sys
import pkg_resources
import requests import requests
from datetime import datetime, timedelta
try:
import pkg_resources
PKG_RESOURCES_AVAILABLE = True
except ImportError:
PKG_RESOURCES_AVAILABLE = False
# Import local utils # Import local utils
from .utils import VideoDownloader, secure_delete_file, cleanup_downloads, MessageManager from .utils import VideoDownloader, secure_delete_file, cleanup_downloads, MessageManager
@@ -44,7 +50,8 @@ class VideoArchiver(commands.Cog):
"message_template": "Archived video from {author}\nOriginal: {original_message}", "message_template": "Archived video from {author}\nOriginal: {original_message}",
"enabled_sites": [], "enabled_sites": [],
"concurrent_downloads": 3, "concurrent_downloads": 3,
"disable_update_check": False "disable_update_check": False,
"last_update_check": None
} }
def __init__(self, bot: Red): def __init__(self, bot: Red):
@@ -81,15 +88,41 @@ class VideoArchiver(commands.Cog):
await self.bot.wait_until_ready() await self.bot.wait_until_ready()
while True: while True:
try: try:
# Only check once per day per guild
all_guilds = await self.config.all_guilds()
current_time = datetime.utcnow()
for guild_id, settings in all_guilds.items():
if settings.get('disable_update_check', False):
continue
last_check = settings.get('last_update_check')
if last_check:
last_check = datetime.fromisoformat(last_check)
if current_time - last_check < timedelta(days=1):
continue
try:
if not PKG_RESOURCES_AVAILABLE:
logger.warning("pkg_resources not available, skipping update check")
continue
current_version = pkg_resources.get_distribution('yt-dlp').version current_version = pkg_resources.get_distribution('yt-dlp').version
response = requests.get('https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest')
# Use a timeout for the request
response = requests.get(
'https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest',
timeout=10
)
response.raise_for_status()
latest_version = response.json()['tag_name'].lstrip('v') latest_version = response.json()['tag_name'].lstrip('v')
# Update last check time
await self.config.guild_from_id(guild_id).last_update_check.set(
current_time.isoformat()
)
if current_version != latest_version: if current_version != latest_version:
# Notify bot owner if any guild has update checks enabled
all_guilds = await self.config.all_guilds()
for guild_id, settings in all_guilds.items():
if not settings.get('disable_update_check', False):
owner = self.bot.get_user(self.bot.owner_id) owner = self.bot.get_user(self.bot.owner_id)
if owner: if owner:
await owner.send( await owner.send(
@@ -98,13 +131,17 @@ class VideoArchiver(commands.Cog):
f"Latest: {latest_version}\n" f"Latest: {latest_version}\n"
f"Use `[p]videoarchiver updateytdlp` to update." f"Use `[p]videoarchiver updateytdlp` to update."
) )
break
except requests.RequestException as e:
logger.error(f"Failed to check for updates (network error): {str(e)}")
except Exception as e: except Exception as e:
logger.error(f"Failed to check for updates: {str(e)}") logger.error(f"Failed to check for updates: {str(e)}")
# Check once per day except Exception as e:
await asyncio.sleep(86400) logger.error(f"Error in update check task: {str(e)}")
# Wait for 6 hours before checking again
await asyncio.sleep(21600) # 6 hours in seconds
async def initialize_guild_components(self, guild_id: int): async def initialize_guild_components(self, guild_id: int):
"""Initialize or update components for a guild""" """Initialize or update components for a guild"""
@@ -200,6 +237,15 @@ class VideoArchiver(commands.Cog):
embed.add_field(name="Message Duration", value=f"{settings['message_duration']} hours", inline=True) embed.add_field(name="Message Duration", value=f"{settings['message_duration']} hours", inline=True)
embed.add_field(name="Concurrent Downloads", value=str(settings["concurrent_downloads"]), inline=True) embed.add_field(name="Concurrent Downloads", value=str(settings["concurrent_downloads"]), inline=True)
embed.add_field(name="Update Check Disabled", value=str(settings["disable_update_check"]), inline=True) embed.add_field(name="Update Check Disabled", value=str(settings["disable_update_check"]), inline=True)
if settings.get("last_update_check"):
last_check = datetime.fromisoformat(settings["last_update_check"])
embed.add_field(
name="Last Update Check",
value=last_check.strftime("%Y-%m-%d %H:%M:%S UTC"),
inline=True
)
embed.add_field( embed.add_field(
name="Enabled Sites", name="Enabled Sites",
value=", ".join(settings["enabled_sites"]) if settings["enabled_sites"] else "All sites", value=", ".join(settings["enabled_sites"]) if settings["enabled_sites"] else "All sites",
@@ -237,10 +283,17 @@ class VideoArchiver(commands.Cog):
stdout, stderr = await process.communicate() stdout, stderr = await process.communicate()
if process.returncode == 0: if process.returncode == 0:
if PKG_RESOURCES_AVAILABLE:
try:
new_version = pkg_resources.get_distribution('yt-dlp').version new_version = pkg_resources.get_distribution('yt-dlp').version
await ctx.send(f"✅ Successfully updated yt-dlp to version {new_version}") await ctx.send(f"✅ Successfully updated yt-dlp to version {new_version}")
except Exception:
await ctx.send("✅ Successfully updated yt-dlp")
else: else:
await ctx.send(f"❌ Failed to update yt-dlp: {stderr.decode()}") await ctx.send("✅ Successfully updated yt-dlp")
else:
error_msg = stderr.decode() if stderr else "Unknown error"
await ctx.send(f"❌ Failed to update yt-dlp: {error_msg}")
except Exception as e: except Exception as e:
await ctx.send(f"❌ Error updating yt-dlp: {str(e)}") await ctx.send(f"❌ Error updating yt-dlp: {str(e)}")