Refactor Utility Functions with Caching and Error Handling for yt-dlp/ffmpeg Paths
This commit is contained in:
+506
-27
@@ -1,6 +1,7 @@
|
|||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
@@ -9,6 +10,328 @@ import pkg_resources
|
|||||||
from packaging import version
|
from packaging import version
|
||||||
import requests
|
import requests
|
||||||
from ytsage_ffmpeg import check_ffmpeg_installed, get_ffmpeg_install_path
|
from ytsage_ffmpeg import check_ffmpeg_installed, get_ffmpeg_install_path
|
||||||
|
from ytsage_yt_dlp import get_yt_dlp_path # Import the new function to avoid import errors
|
||||||
|
|
||||||
|
# Cache for version information to avoid delays
|
||||||
|
_version_cache = {
|
||||||
|
'ytdlp': {'version': None, 'path': None, 'last_check': 0, 'path_mtime': 0},
|
||||||
|
'ffmpeg': {'version': None, 'path': None, 'last_check': 0, 'path_mtime': 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Cache expiry time in seconds (5 minutes)
|
||||||
|
CACHE_EXPIRY = 300
|
||||||
|
|
||||||
|
def get_file_mtime(filepath):
|
||||||
|
"""Get file modification time safely."""
|
||||||
|
try:
|
||||||
|
if filepath and os.path.exists(filepath):
|
||||||
|
return os.path.getmtime(filepath)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def should_refresh_cache(tool_name, current_path):
|
||||||
|
"""Determine if cache should be refreshed for a tool."""
|
||||||
|
cache = _version_cache.get(tool_name, {})
|
||||||
|
current_time = time.time()
|
||||||
|
|
||||||
|
# Always refresh if no cached data
|
||||||
|
if not cache.get('version'):
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Refresh if path changed
|
||||||
|
if cache.get('path') != current_path:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Refresh if file was modified
|
||||||
|
current_mtime = get_file_mtime(current_path)
|
||||||
|
if current_mtime > cache.get('path_mtime', 0):
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Refresh if cache expired
|
||||||
|
if current_time - cache.get('last_check', 0) > CACHE_EXPIRY:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def update_version_cache(tool_name, version_info, path, force_save=False):
|
||||||
|
"""Update the version cache and optionally save to config."""
|
||||||
|
current_time = time.time()
|
||||||
|
current_mtime = get_file_mtime(path)
|
||||||
|
|
||||||
|
_version_cache[tool_name] = {
|
||||||
|
'version': version_info,
|
||||||
|
'path': path,
|
||||||
|
'last_check': current_time,
|
||||||
|
'path_mtime': current_mtime
|
||||||
|
}
|
||||||
|
|
||||||
|
# Save to persistent config
|
||||||
|
if force_save:
|
||||||
|
save_version_cache_to_config()
|
||||||
|
|
||||||
|
def load_version_cache_from_config():
|
||||||
|
"""Load cached version info from config file."""
|
||||||
|
try:
|
||||||
|
config = load_config()
|
||||||
|
cached_versions = config.get('cached_versions', {})
|
||||||
|
|
||||||
|
for tool_name, cache_data in cached_versions.items():
|
||||||
|
if tool_name in _version_cache:
|
||||||
|
_version_cache[tool_name].update(cache_data)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error loading version cache: {e}")
|
||||||
|
|
||||||
|
def save_version_cache_to_config():
|
||||||
|
"""Save version cache to config file."""
|
||||||
|
try:
|
||||||
|
config = load_config()
|
||||||
|
config['cached_versions'] = _version_cache.copy()
|
||||||
|
save_config(config)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error saving version cache: {e}")
|
||||||
|
|
||||||
|
def get_ytdlp_version_cached():
|
||||||
|
"""Get yt-dlp version with caching support."""
|
||||||
|
try:
|
||||||
|
current_path = get_yt_dlp_path()
|
||||||
|
|
||||||
|
# Check if we need to refresh cache
|
||||||
|
if not should_refresh_cache('ytdlp', current_path):
|
||||||
|
cached_version = _version_cache['ytdlp'].get('version')
|
||||||
|
if cached_version:
|
||||||
|
return cached_version
|
||||||
|
|
||||||
|
# Get fresh version info
|
||||||
|
version_info = get_ytdlp_version_direct(current_path)
|
||||||
|
|
||||||
|
# Update cache
|
||||||
|
update_version_cache('ytdlp', version_info, current_path)
|
||||||
|
|
||||||
|
return version_info
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error getting cached yt-dlp version: {e}")
|
||||||
|
return "Error getting version"
|
||||||
|
|
||||||
|
def get_ffmpeg_version_cached():
|
||||||
|
"""Get FFmpeg version with caching support."""
|
||||||
|
try:
|
||||||
|
# Try to find ffmpeg path
|
||||||
|
current_path = "ffmpeg" # Default to system PATH
|
||||||
|
|
||||||
|
# Check if we need to refresh cache
|
||||||
|
if not should_refresh_cache('ffmpeg', current_path):
|
||||||
|
cached_version = _version_cache['ffmpeg'].get('version')
|
||||||
|
if cached_version:
|
||||||
|
return cached_version
|
||||||
|
|
||||||
|
# Get fresh version info
|
||||||
|
version_info = get_ffmpeg_version_direct()
|
||||||
|
|
||||||
|
# Update cache
|
||||||
|
update_version_cache('ffmpeg', version_info, current_path)
|
||||||
|
|
||||||
|
return version_info
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error getting cached FFmpeg version: {e}")
|
||||||
|
return "Error getting version"
|
||||||
|
|
||||||
|
def refresh_version_cache(force=False):
|
||||||
|
"""Manually refresh version cache for both tools."""
|
||||||
|
try:
|
||||||
|
# Refresh yt-dlp
|
||||||
|
current_path = get_yt_dlp_path()
|
||||||
|
version_info = get_ytdlp_version_direct(current_path)
|
||||||
|
update_version_cache('ytdlp', version_info, current_path, force_save=True)
|
||||||
|
|
||||||
|
# Refresh FFmpeg
|
||||||
|
version_info = get_ffmpeg_version_direct()
|
||||||
|
update_version_cache('ffmpeg', version_info, "ffmpeg", force_save=True)
|
||||||
|
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error refreshing version cache: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_ytdlp_version():
|
||||||
|
"""Get the version of yt-dlp (uses cached version for performance)."""
|
||||||
|
return get_ytdlp_version_cached()
|
||||||
|
|
||||||
|
def get_ffmpeg_version():
|
||||||
|
"""Get the version of FFmpeg (uses cached version for performance)."""
|
||||||
|
return get_ffmpeg_version_cached()
|
||||||
|
|
||||||
|
def get_ytdlp_version_direct(yt_dlp_path=None):
|
||||||
|
"""Get yt-dlp version directly without caching."""
|
||||||
|
try:
|
||||||
|
if yt_dlp_path is None:
|
||||||
|
yt_dlp_path = get_yt_dlp_path()
|
||||||
|
|
||||||
|
if not yt_dlp_path or yt_dlp_path == "yt-dlp":
|
||||||
|
return "Not found"
|
||||||
|
|
||||||
|
# Create startupinfo to hide console on Windows
|
||||||
|
startupinfo = None
|
||||||
|
if sys.platform == 'win32' and hasattr(subprocess, 'STARTUPINFO'):
|
||||||
|
startupinfo = subprocess.STARTUPINFO()
|
||||||
|
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
||||||
|
startupinfo.wShowWindow = 0 # SW_HIDE
|
||||||
|
|
||||||
|
result = subprocess.run(
|
||||||
|
[yt_dlp_path, '--version'],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=10,
|
||||||
|
startupinfo=startupinfo
|
||||||
|
)
|
||||||
|
|
||||||
|
if result.returncode == 0:
|
||||||
|
return result.stdout.strip()
|
||||||
|
else:
|
||||||
|
return "Error getting version"
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error getting yt-dlp version: {e}")
|
||||||
|
return "Error getting version"
|
||||||
|
|
||||||
|
def get_ffmpeg_version_direct():
|
||||||
|
"""Get FFmpeg version directly without caching."""
|
||||||
|
try:
|
||||||
|
# Create startupinfo to hide console on Windows
|
||||||
|
startupinfo = None
|
||||||
|
if sys.platform == 'win32' and hasattr(subprocess, 'STARTUPINFO'):
|
||||||
|
startupinfo = subprocess.STARTUPINFO()
|
||||||
|
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
||||||
|
startupinfo.wShowWindow = 0 # SW_HIDE
|
||||||
|
|
||||||
|
result = subprocess.run(
|
||||||
|
['ffmpeg', '-version'],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=10,
|
||||||
|
startupinfo=startupinfo
|
||||||
|
)
|
||||||
|
|
||||||
|
if result.returncode == 0:
|
||||||
|
# Parse the first line to get version info
|
||||||
|
lines = result.stdout.split('\n')
|
||||||
|
if lines:
|
||||||
|
first_line = lines[0]
|
||||||
|
# Extract version from something like "ffmpeg version 4.4.2 Copyright..."
|
||||||
|
if 'version' in first_line:
|
||||||
|
parts = first_line.split()
|
||||||
|
for i, part in enumerate(parts):
|
||||||
|
if part == 'version' and i + 1 < len(parts):
|
||||||
|
return parts[i + 1]
|
||||||
|
return first_line.strip()
|
||||||
|
return "Unknown version"
|
||||||
|
else:
|
||||||
|
return "Not found"
|
||||||
|
except FileNotFoundError:
|
||||||
|
# If ffmpeg is not in PATH, try the installation directory
|
||||||
|
try:
|
||||||
|
ffmpeg_path = get_ffmpeg_install_path()
|
||||||
|
if sys.platform == 'win32':
|
||||||
|
ffmpeg_exe = os.path.join(ffmpeg_path, 'ffmpeg.exe')
|
||||||
|
else:
|
||||||
|
ffmpeg_exe = os.path.join(ffmpeg_path, 'ffmpeg')
|
||||||
|
|
||||||
|
if os.path.exists(ffmpeg_exe):
|
||||||
|
result = subprocess.run(
|
||||||
|
[ffmpeg_exe, '-version'],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=10,
|
||||||
|
startupinfo=startupinfo
|
||||||
|
)
|
||||||
|
|
||||||
|
if result.returncode == 0:
|
||||||
|
lines = result.stdout.split('\n')
|
||||||
|
if lines:
|
||||||
|
first_line = lines[0]
|
||||||
|
if 'version' in first_line:
|
||||||
|
parts = first_line.split()
|
||||||
|
for i, part in enumerate(parts):
|
||||||
|
if part == 'version' and i + 1 < len(parts):
|
||||||
|
return parts[i + 1]
|
||||||
|
return first_line.strip()
|
||||||
|
return "Unknown version"
|
||||||
|
return "Not found"
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error getting FFmpeg version from install path: {e}")
|
||||||
|
return "Not found"
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error getting FFmpeg version: {e}")
|
||||||
|
return "Error getting version"
|
||||||
|
|
||||||
|
def get_app_data_dir():
|
||||||
|
"""Get the OS-specific application data directory."""
|
||||||
|
if sys.platform == 'win32':
|
||||||
|
# Windows: %LOCALAPPDATA%\YTSage\data\
|
||||||
|
return Path(os.environ.get('LOCALAPPDATA', '')) / 'YTSage' / 'data'
|
||||||
|
elif sys.platform == 'darwin':
|
||||||
|
# macOS: ~/Library/Application Support/YTSage/data/
|
||||||
|
return Path.home() / 'Library' / 'Application Support' / 'YTSage' / 'data'
|
||||||
|
else:
|
||||||
|
# Linux: ~/.local/share/YTSage/data/
|
||||||
|
return Path.home() / '.local' / 'share' / 'YTSage' / 'data'
|
||||||
|
|
||||||
|
def get_config_file_path():
|
||||||
|
"""Get the path to the main configuration file."""
|
||||||
|
return get_app_data_dir() / 'ytsage_config.json'
|
||||||
|
|
||||||
|
def ensure_app_data_dir():
|
||||||
|
"""Ensure the application data directory exists."""
|
||||||
|
data_dir = get_app_data_dir()
|
||||||
|
data_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
return data_dir
|
||||||
|
|
||||||
|
def load_config():
|
||||||
|
"""Load the application configuration from file."""
|
||||||
|
config_file = get_config_file_path()
|
||||||
|
default_config = {
|
||||||
|
'download_path': str(Path.home() / 'Downloads'),
|
||||||
|
'speed_limit_value': None,
|
||||||
|
'speed_limit_unit_index': 0,
|
||||||
|
'cookie_file_path': None,
|
||||||
|
'last_used_cookie_file': None,
|
||||||
|
'auto_update_ytdlp': True, # Enable auto-update by default
|
||||||
|
'auto_update_frequency': 'daily', # daily, weekly, or startup
|
||||||
|
'last_update_check': 0, # timestamp of last check
|
||||||
|
'cached_versions': {
|
||||||
|
'ytdlp': {'version': None, 'path': None, 'last_check': 0, 'path_mtime': 0},
|
||||||
|
'ffmpeg': {'version': None, 'path': None, 'last_check': 0, 'path_mtime': 0}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
if config_file.exists():
|
||||||
|
with open(config_file, 'r', encoding='utf-8') as f:
|
||||||
|
config = json.load(f)
|
||||||
|
# Merge with defaults to ensure all keys exist
|
||||||
|
for key, value in default_config.items():
|
||||||
|
if key not in config:
|
||||||
|
config[key] = value
|
||||||
|
return config
|
||||||
|
except (json.JSONDecodeError, UnicodeError, Exception) as e:
|
||||||
|
print(f"Error reading config file: {e}")
|
||||||
|
# If config file is corrupted, create a new one with defaults
|
||||||
|
save_config(default_config)
|
||||||
|
|
||||||
|
return default_config
|
||||||
|
|
||||||
|
def save_config(config):
|
||||||
|
"""Save the application configuration to file."""
|
||||||
|
config_file = get_config_file_path()
|
||||||
|
try:
|
||||||
|
# Ensure the config directory exists
|
||||||
|
ensure_app_data_dir()
|
||||||
|
|
||||||
|
with open(config_file, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(config, f, ensure_ascii=False, indent=2)
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error saving config: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
def check_ffmpeg():
|
def check_ffmpeg():
|
||||||
"""Check if FFmpeg is installed and accessible with enhanced error handling."""
|
"""Check if FFmpeg is installed and accessible with enhanced error handling."""
|
||||||
@@ -52,29 +375,9 @@ def check_ffmpeg():
|
|||||||
print(f"Error checking FFmpeg: {e}")
|
print(f"Error checking FFmpeg: {e}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_yt_dlp_path():
|
|
||||||
"""Get the yt-dlp command or path, prioritizing the system PATH."""
|
|
||||||
try:
|
|
||||||
# Use shutil.which to find yt-dlp in the system's PATH
|
|
||||||
yt_dlp_executable = shutil.which('yt-dlp')
|
|
||||||
|
|
||||||
if yt_dlp_executable:
|
|
||||||
print(f"Found yt-dlp executable in PATH: {yt_dlp_executable}")
|
|
||||||
return yt_dlp_executable
|
|
||||||
else:
|
|
||||||
# If not found in PATH, assume 'yt-dlp' is the command name
|
|
||||||
print("yt-dlp not found in PATH. Will attempt to use 'yt-dlp' as the command.")
|
|
||||||
return 'yt-dlp'
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error finding yt-dlp path: {e}")
|
|
||||||
# Fallback to the command name on any error
|
|
||||||
print("An error occurred during yt-dlp path detection. Falling back to command 'yt-dlp'.")
|
|
||||||
return 'yt-dlp'
|
|
||||||
|
|
||||||
def load_saved_path(main_window_instance):
|
def load_saved_path(main_window_instance):
|
||||||
"""Load saved download path with enhanced error handling."""
|
"""Load saved download path with enhanced error handling."""
|
||||||
config_file = main_window_instance.config_file
|
config_file = get_config_file_path()
|
||||||
try:
|
try:
|
||||||
if config_file.exists():
|
if config_file.exists():
|
||||||
try:
|
try:
|
||||||
@@ -106,7 +409,7 @@ def load_saved_path(main_window_instance):
|
|||||||
|
|
||||||
def save_path(main_window_instance, path):
|
def save_path(main_window_instance, path):
|
||||||
"""Save download path with enhanced error handling."""
|
"""Save download path with enhanced error handling."""
|
||||||
config_file = main_window_instance.config_file
|
config_file = get_config_file_path()
|
||||||
try:
|
try:
|
||||||
# Verify the path is valid and writable
|
# Verify the path is valid and writable
|
||||||
if not os.path.exists(path):
|
if not os.path.exists(path):
|
||||||
@@ -120,10 +423,8 @@ def save_path(main_window_instance, path):
|
|||||||
print("Path is not writable")
|
print("Path is not writable")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Create config directory if it doesn't exist
|
# Ensure the config directory exists
|
||||||
config_dir = config_file.parent
|
ensure_app_data_dir()
|
||||||
if not config_dir.exists():
|
|
||||||
config_dir.mkdir(parents=True, exist_ok=True)
|
|
||||||
|
|
||||||
# Save the config
|
# Save the config
|
||||||
config = {'download_path': path}
|
config = {'download_path': path}
|
||||||
@@ -138,6 +439,70 @@ def save_path(main_window_instance, path):
|
|||||||
def update_yt_dlp():
|
def update_yt_dlp():
|
||||||
"""Check for yt-dlp updates and update if a newer version is available."""
|
"""Check for yt-dlp updates and update if a newer version is available."""
|
||||||
try:
|
try:
|
||||||
|
# Get the yt-dlp path
|
||||||
|
yt_dlp_path = get_yt_dlp_path()
|
||||||
|
|
||||||
|
# Create startupinfo to hide console on Windows
|
||||||
|
startupinfo = None
|
||||||
|
if sys.platform == 'win32' and hasattr(subprocess, 'STARTUPINFO'):
|
||||||
|
startupinfo = subprocess.STARTUPINFO()
|
||||||
|
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
||||||
|
startupinfo.wShowWindow = 0 # SW_HIDE
|
||||||
|
|
||||||
|
# For binaries downloaded with our app, use direct binary update approach
|
||||||
|
if os.path.dirname(yt_dlp_path) in [
|
||||||
|
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'YTSage', 'bin'),
|
||||||
|
os.path.expanduser(os.path.join('~', 'Library', 'Application Support', 'YTSage', 'bin')),
|
||||||
|
os.path.expanduser(os.path.join('~', '.local', 'share', 'YTSage', 'bin'))
|
||||||
|
]:
|
||||||
|
# We're using a binary installed by our app, update directly
|
||||||
|
print(f"Updating yt-dlp binary at {yt_dlp_path}")
|
||||||
|
|
||||||
|
# Determine the URL based on OS
|
||||||
|
if sys.platform == 'win32':
|
||||||
|
url = "https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp.exe"
|
||||||
|
elif sys.platform == 'darwin':
|
||||||
|
url = "https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_macos"
|
||||||
|
else:
|
||||||
|
url = "https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp"
|
||||||
|
|
||||||
|
# Download the latest version
|
||||||
|
try:
|
||||||
|
response = requests.get(url, stream=True)
|
||||||
|
if response.status_code == 200:
|
||||||
|
# Create a temporary file
|
||||||
|
temp_file = f"{yt_dlp_path}.new"
|
||||||
|
|
||||||
|
with open(temp_file, 'wb') as f:
|
||||||
|
for chunk in response.iter_content(chunk_size=8192):
|
||||||
|
f.write(chunk)
|
||||||
|
|
||||||
|
# Make executable on Unix systems
|
||||||
|
if sys.platform != 'win32':
|
||||||
|
os.chmod(temp_file, 0o755)
|
||||||
|
|
||||||
|
# Replace the old file with the new one
|
||||||
|
try:
|
||||||
|
# On Windows, we need to remove the old file first
|
||||||
|
if sys.platform == 'win32' and os.path.exists(yt_dlp_path):
|
||||||
|
os.remove(yt_dlp_path)
|
||||||
|
|
||||||
|
os.rename(temp_file, yt_dlp_path)
|
||||||
|
print("yt-dlp binary successfully updated")
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error replacing yt-dlp binary: {e}")
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
print(f"Failed to download latest yt-dlp: HTTP {response.status_code}")
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error downloading yt-dlp update: {e}")
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
# We're using a system-installed yt-dlp, use pip to update
|
||||||
|
print("Using pip to update yt-dlp")
|
||||||
|
|
||||||
# Get current version
|
# Get current version
|
||||||
try:
|
try:
|
||||||
current_version = pkg_resources.get_distribution("yt-dlp").version
|
current_version = pkg_resources.get_distribution("yt-dlp").version
|
||||||
@@ -161,7 +526,8 @@ def update_yt_dlp():
|
|||||||
[sys.executable, "-m", "pip", "install", "--upgrade", "yt-dlp"],
|
[sys.executable, "-m", "pip", "install", "--upgrade", "yt-dlp"],
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
check=False
|
check=False,
|
||||||
|
startupinfo=startupinfo
|
||||||
)
|
)
|
||||||
if update_result.returncode == 0:
|
if update_result.returncode == 0:
|
||||||
print("yt-dlp successfully updated")
|
print("yt-dlp successfully updated")
|
||||||
@@ -179,3 +545,116 @@ def update_yt_dlp():
|
|||||||
print(f"Unexpected error during yt-dlp update: {e}")
|
print(f"Unexpected error during yt-dlp update: {e}")
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def should_check_for_auto_update():
|
||||||
|
"""Check if auto-update should be performed based on user settings."""
|
||||||
|
try:
|
||||||
|
config = load_config()
|
||||||
|
|
||||||
|
# Check if auto-update is enabled
|
||||||
|
if not config.get('auto_update_ytdlp', False):
|
||||||
|
return False
|
||||||
|
|
||||||
|
frequency = config.get('auto_update_frequency', 'daily')
|
||||||
|
last_check = config.get('last_update_check', 0)
|
||||||
|
current_time = time.time()
|
||||||
|
|
||||||
|
# Calculate time since last check
|
||||||
|
time_diff = current_time - last_check
|
||||||
|
|
||||||
|
if frequency == 'startup':
|
||||||
|
# Always check on startup if we haven't checked in the last hour
|
||||||
|
return time_diff > 3600 # 1 hour
|
||||||
|
elif frequency == 'daily':
|
||||||
|
return time_diff > 86400 # 24 hours
|
||||||
|
elif frequency == 'weekly':
|
||||||
|
return time_diff > 604800 # 7 days
|
||||||
|
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error checking auto-update schedule: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def check_and_update_ytdlp_auto():
|
||||||
|
"""Perform automatic yt-dlp update check and update if needed."""
|
||||||
|
try:
|
||||||
|
print("Performing automatic yt-dlp update check...")
|
||||||
|
|
||||||
|
# Get current version
|
||||||
|
current_version = get_ytdlp_version()
|
||||||
|
if "Error" in current_version:
|
||||||
|
print("Could not determine current yt-dlp version, skipping auto-update")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Get latest version from PyPI
|
||||||
|
try:
|
||||||
|
response = requests.get("https://pypi.org/pypi/yt-dlp/json", timeout=10)
|
||||||
|
response.raise_for_status()
|
||||||
|
latest_version = response.json()["info"]["version"]
|
||||||
|
|
||||||
|
# Clean up version strings
|
||||||
|
current_version = current_version.replace('_', '.')
|
||||||
|
latest_version = latest_version.replace('_', '.')
|
||||||
|
|
||||||
|
print(f"Current yt-dlp version: {current_version}")
|
||||||
|
print(f"Latest yt-dlp version: {latest_version}")
|
||||||
|
|
||||||
|
# Compare versions
|
||||||
|
from packaging import version as version_parser
|
||||||
|
if version_parser.parse(latest_version) > version_parser.parse(current_version):
|
||||||
|
print(f"Auto-updating yt-dlp from {current_version} to {latest_version}...")
|
||||||
|
|
||||||
|
# Perform the update
|
||||||
|
if update_yt_dlp():
|
||||||
|
print("Auto-update completed successfully!")
|
||||||
|
# Update the last check timestamp
|
||||||
|
config = load_config()
|
||||||
|
config['last_update_check'] = time.time()
|
||||||
|
save_config(config)
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
print("Auto-update failed")
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
print("yt-dlp is already up to date")
|
||||||
|
# Still update the timestamp even if no update was needed
|
||||||
|
config = load_config()
|
||||||
|
config['last_update_check'] = time.time()
|
||||||
|
save_config(config)
|
||||||
|
return True
|
||||||
|
|
||||||
|
except requests.RequestException as e:
|
||||||
|
print(f"Network error during auto-update check: {e}")
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error during auto-update check: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Critical error in auto-update: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def get_auto_update_settings():
|
||||||
|
"""Get current auto-update settings from config."""
|
||||||
|
config = load_config()
|
||||||
|
return {
|
||||||
|
'enabled': config.get('auto_update_ytdlp', True),
|
||||||
|
'frequency': config.get('auto_update_frequency', 'daily'),
|
||||||
|
'last_check': config.get('last_update_check', 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def update_auto_update_settings(enabled, frequency):
|
||||||
|
"""Update auto-update settings in config."""
|
||||||
|
try:
|
||||||
|
config = load_config()
|
||||||
|
config['auto_update_ytdlp'] = enabled
|
||||||
|
config['auto_update_frequency'] = frequency
|
||||||
|
save_config(config)
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error updating auto-update settings: {e}")
|
||||||
|
return False
|
||||||
Reference in New Issue
Block a user