mirror of
https://github.com/unshackle-dl/unshackle.git
synced 2025-10-23 15:11:08 +00:00
feat: Add update check interval configuration and implement rate limiting for update checks
This commit is contained in:
@@ -79,6 +79,7 @@ class Config:
|
|||||||
self.tag: str = kwargs.get("tag") or ""
|
self.tag: str = kwargs.get("tag") or ""
|
||||||
self.tmdb_api_key: str = kwargs.get("tmdb_api_key") or ""
|
self.tmdb_api_key: str = kwargs.get("tmdb_api_key") or ""
|
||||||
self.update_checks: bool = kwargs.get("update_checks", True)
|
self.update_checks: bool = kwargs.get("update_checks", True)
|
||||||
|
self.update_check_interval: int = kwargs.get("update_check_interval", 24)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_yaml(cls, path: Path) -> Config:
|
def from_yaml(cls, path: Path) -> Config:
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
@@ -11,6 +14,66 @@ class UpdateChecker:
|
|||||||
|
|
||||||
REPO_URL = "https://api.github.com/repos/unshackle-dl/unshackle/releases/latest"
|
REPO_URL = "https://api.github.com/repos/unshackle-dl/unshackle/releases/latest"
|
||||||
TIMEOUT = 5
|
TIMEOUT = 5
|
||||||
|
DEFAULT_CHECK_INTERVAL = 24 * 60 * 60 # 24 hours in seconds
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _get_cache_file(cls) -> Path:
|
||||||
|
"""Get the path to the update check cache file."""
|
||||||
|
from unshackle.core.config import config
|
||||||
|
|
||||||
|
return config.directories.cache / "update_check.json"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _should_check_for_updates(cls, check_interval: int = DEFAULT_CHECK_INTERVAL) -> bool:
|
||||||
|
"""
|
||||||
|
Check if enough time has passed since the last update check.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
check_interval: Time in seconds between checks (default: 24 hours)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True if we should check for updates, False otherwise
|
||||||
|
"""
|
||||||
|
cache_file = cls._get_cache_file()
|
||||||
|
|
||||||
|
if not cache_file.exists():
|
||||||
|
return True
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(cache_file, "r") as f:
|
||||||
|
cache_data = json.load(f)
|
||||||
|
|
||||||
|
last_check = cache_data.get("last_check", 0)
|
||||||
|
current_time = time.time()
|
||||||
|
|
||||||
|
return (current_time - last_check) >= check_interval
|
||||||
|
|
||||||
|
except (json.JSONDecodeError, KeyError, OSError):
|
||||||
|
# If cache is corrupted or unreadable, allow check
|
||||||
|
return True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _update_cache(cls, latest_version: Optional[str] = None) -> None:
|
||||||
|
"""
|
||||||
|
Update the cache file with the current timestamp and latest version.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
latest_version: The latest version found, if any
|
||||||
|
"""
|
||||||
|
cache_file = cls._get_cache_file()
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Ensure cache directory exists
|
||||||
|
cache_file.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
cache_data = {"last_check": time.time(), "latest_version": latest_version}
|
||||||
|
|
||||||
|
with open(cache_file, "w") as f:
|
||||||
|
json.dump(cache_data, f)
|
||||||
|
|
||||||
|
except (OSError, json.JSONEncodeError):
|
||||||
|
# Silently fail if we can't write cache
|
||||||
|
pass
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _compare_versions(current: str, latest: str) -> bool:
|
def _compare_versions(current: str, latest: str) -> bool:
|
||||||
@@ -75,32 +138,51 @@ class UpdateChecker:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def check_for_updates_sync(cls, current_version: str) -> Optional[str]:
|
def check_for_updates_sync(cls, current_version: str, check_interval: Optional[int] = None) -> Optional[str]:
|
||||||
"""
|
"""
|
||||||
Synchronous version of update check.
|
Synchronous version of update check with rate limiting.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
current_version: The current version string (e.g., "1.1.0")
|
current_version: The current version string (e.g., "1.1.0")
|
||||||
|
check_interval: Time in seconds between checks (default: from config)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The latest version string if an update is available, None otherwise
|
The latest version string if an update is available, None otherwise
|
||||||
"""
|
"""
|
||||||
|
# Use config value if not specified
|
||||||
|
if check_interval is None:
|
||||||
|
from unshackle.core.config import config
|
||||||
|
|
||||||
|
check_interval = config.update_check_interval * 60 * 60 # Convert hours to seconds
|
||||||
|
|
||||||
|
# Check if we should skip this check due to rate limiting
|
||||||
|
if not cls._should_check_for_updates(check_interval):
|
||||||
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response = requests.get(cls.REPO_URL, timeout=cls.TIMEOUT)
|
response = requests.get(cls.REPO_URL, timeout=cls.TIMEOUT)
|
||||||
|
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
|
# Update cache even on failure to prevent rapid retries
|
||||||
|
cls._update_cache()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
data = response.json()
|
data = response.json()
|
||||||
latest_version = data.get("tag_name", "").lstrip("v")
|
latest_version = data.get("tag_name", "").lstrip("v")
|
||||||
|
|
||||||
if not latest_version:
|
if not latest_version:
|
||||||
|
cls._update_cache()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
# Update cache with the latest version info
|
||||||
|
cls._update_cache(latest_version)
|
||||||
|
|
||||||
if cls._compare_versions(current_version, latest_version):
|
if cls._compare_versions(current_version, latest_version):
|
||||||
return latest_version
|
return latest_version
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
|
# Update cache even on exception to prevent rapid retries
|
||||||
|
cls._update_cache()
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ set_terminal_bg: false
|
|||||||
# Check for updates from GitHub repository on startup (default: true)
|
# Check for updates from GitHub repository on startup (default: true)
|
||||||
update_checks: true
|
update_checks: true
|
||||||
|
|
||||||
|
# How often to check for updates, in hours (default: 24)
|
||||||
|
update_check_interval: 24
|
||||||
|
|
||||||
# Muxing configuration
|
# Muxing configuration
|
||||||
muxing:
|
muxing:
|
||||||
set_title: false
|
set_title: false
|
||||||
|
|||||||
Reference in New Issue
Block a user