mirror of
https://github.com/unshackle-dl/unshackle.git
synced 2025-10-23 15:11:08 +00:00
feat: Update version to 1.1.1 and add update checking functionality
This commit is contained in:
35
CHANGELOG.md
35
CHANGELOG.md
@@ -7,6 +7,41 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [1.1.1] - 2025-07-30
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- **Update Checker**: Automatic GitHub release version checking on startup
|
||||||
|
- Configurable update notifications via `update_checks` setting in unshackle.yaml
|
||||||
|
- Non-blocking HTTP requests with 5-second timeout for performance
|
||||||
|
- Smart semantic version comparison supporting all version formats (x.y.z, x.y, x)
|
||||||
|
- Graceful error handling for network issues and API failures
|
||||||
|
- User-friendly update notifications with current → latest version display
|
||||||
|
- Direct links to GitHub releases page for easy updates
|
||||||
|
- **HDR10+ Support**: Enhanced HDR10+ metadata processing for hybrid tracks
|
||||||
|
- HDR10+ tool binary support (`hdr10plus_tool`) added to binaries module
|
||||||
|
- HDR10+ to Dolby Vision conversion capabilities in hybrid processing
|
||||||
|
- Enhanced metadata extraction for HDR10+ content
|
||||||
|
- **Duration Fix Handling**: Added duration correction for video and hybrid tracks
|
||||||
|
- **Temporary Directory Management**: Automatic creation of temp directories for attachment downloads
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Enhanced configuration system with new `update_checks` boolean option (defaults to true)
|
||||||
|
- Updated sample unshackle.yaml with update checker configuration documentation
|
||||||
|
- Improved console styling consistency using `bright_black` for dimmed text
|
||||||
|
- **Environment Dependency Check**: Complete overhaul with detailed categorization and status summary
|
||||||
|
- Organized dependencies by category (Core, HDR, Download, Subtitle, Player, Network)
|
||||||
|
- Enhanced status reporting with compact summary display
|
||||||
|
- Improved tool requirement tracking and missing dependency alerts
|
||||||
|
- **Hybrid Track Processing**: Significant improvements to HDR10+ and Dolby Vision handling
|
||||||
|
- Enhanced metadata extraction and processing workflows
|
||||||
|
- Better integration with HDR processing tools
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- **Docker Workflow**: Removed Docker build and publish GitHub Actions workflow for manual builds
|
||||||
|
|
||||||
## [1.1.0] - 2025-07-29
|
## [1.1.0] - 2025-07-29
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "unshackle"
|
name = "unshackle"
|
||||||
version = "1.1.0"
|
version = "1.1.1"
|
||||||
description = "Modular Movie, TV, and Music Archival Software."
|
description = "Modular Movie, TV, and Music Archival Software."
|
||||||
authors = [{ name = "unshackle team" }]
|
authors = [{ name = "unshackle team" }]
|
||||||
requires-python = ">=3.10,<3.13"
|
requires-python = ">=3.10,<3.13"
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ def check() -> None:
|
|||||||
"name": "N_m3u8DL-RE",
|
"name": "N_m3u8DL-RE",
|
||||||
"binary": binaries.N_m3u8DL_RE,
|
"binary": binaries.N_m3u8DL_RE,
|
||||||
"required": False,
|
"required": False,
|
||||||
"desc": "HLS/DASH",
|
"desc": "HLS/DASH/ISM",
|
||||||
"cat": "Download",
|
"cat": "Download",
|
||||||
},
|
},
|
||||||
# Subtitle Tools
|
# Subtitle Tools
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
__version__ = "1.1.0"
|
__version__ = "1.1.1"
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ from unshackle.core.commands import Commands
|
|||||||
from unshackle.core.config import config
|
from unshackle.core.config import config
|
||||||
from unshackle.core.console import ComfyRichHandler, console
|
from unshackle.core.console import ComfyRichHandler, console
|
||||||
from unshackle.core.constants import context_settings
|
from unshackle.core.constants import context_settings
|
||||||
|
from unshackle.core.update_checker import UpdateChecker
|
||||||
from unshackle.core.utilities import rotate_log_file
|
from unshackle.core.utilities import rotate_log_file
|
||||||
|
|
||||||
LOGGING_PATH = None
|
LOGGING_PATH = None
|
||||||
@@ -79,6 +80,22 @@ def main(version: bool, debug: bool, log_path: Path) -> None:
|
|||||||
if version:
|
if version:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if config.update_checks:
|
||||||
|
try:
|
||||||
|
latest_version = UpdateChecker.check_for_updates_sync(__version__)
|
||||||
|
if latest_version:
|
||||||
|
console.print(
|
||||||
|
f"\n[yellow]⚠️ Update available![/yellow] "
|
||||||
|
f"Current: {__version__} → Latest: [green]{latest_version}[/green]",
|
||||||
|
justify="center",
|
||||||
|
)
|
||||||
|
console.print(
|
||||||
|
"Visit: https://github.com/unshackle-dl/unshackle/releases/latest\n",
|
||||||
|
justify="center",
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
@atexit.register
|
@atexit.register
|
||||||
def save_log():
|
def save_log():
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ class Config:
|
|||||||
self.set_terminal_bg: bool = kwargs.get("set_terminal_bg", False)
|
self.set_terminal_bg: bool = kwargs.get("set_terminal_bg", False)
|
||||||
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)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_yaml(cls, path: Path) -> Config:
|
def from_yaml(cls, path: Path) -> Config:
|
||||||
|
|||||||
106
unshackle/core/update_checker.py
Normal file
106
unshackle/core/update_checker.py
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateChecker:
|
||||||
|
"""Check for available updates from the GitHub repository."""
|
||||||
|
|
||||||
|
REPO_URL = "https://api.github.com/repos/unshackle-dl/unshackle/releases/latest"
|
||||||
|
TIMEOUT = 5
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _compare_versions(current: str, latest: str) -> bool:
|
||||||
|
"""
|
||||||
|
Simple semantic version comparison.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
current: Current version string (e.g., "1.1.0")
|
||||||
|
latest: Latest version string (e.g., "1.2.0")
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True if latest > current, False otherwise
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
current_parts = [int(x) for x in current.split(".")]
|
||||||
|
latest_parts = [int(x) for x in latest.split(".")]
|
||||||
|
|
||||||
|
max_length = max(len(current_parts), len(latest_parts))
|
||||||
|
current_parts.extend([0] * (max_length - len(current_parts)))
|
||||||
|
latest_parts.extend([0] * (max_length - len(latest_parts)))
|
||||||
|
|
||||||
|
for current_part, latest_part in zip(current_parts, latest_parts):
|
||||||
|
if latest_part > current_part:
|
||||||
|
return True
|
||||||
|
elif latest_part < current_part:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return False
|
||||||
|
except (ValueError, AttributeError):
|
||||||
|
return False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def check_for_updates(cls, current_version: str) -> Optional[str]:
|
||||||
|
"""
|
||||||
|
Check if there's a newer version available on GitHub.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
current_version: The current version string (e.g., "1.1.0")
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The latest version string if an update is available, None otherwise
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
response = await loop.run_in_executor(None, lambda: requests.get(cls.REPO_URL, timeout=cls.TIMEOUT))
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
return None
|
||||||
|
|
||||||
|
data = response.json()
|
||||||
|
latest_version = data.get("tag_name", "").lstrip("v")
|
||||||
|
|
||||||
|
if not latest_version:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if cls._compare_versions(current_version, latest_version):
|
||||||
|
return latest_version
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def check_for_updates_sync(cls, current_version: str) -> Optional[str]:
|
||||||
|
"""
|
||||||
|
Synchronous version of update check.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
current_version: The current version string (e.g., "1.1.0")
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The latest version string if an update is available, None otherwise
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
response = requests.get(cls.REPO_URL, timeout=cls.TIMEOUT)
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
return None
|
||||||
|
|
||||||
|
data = response.json()
|
||||||
|
latest_version = data.get("tag_name", "").lstrip("v")
|
||||||
|
|
||||||
|
if not latest_version:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if cls._compare_versions(current_version, latest_version):
|
||||||
|
return latest_version
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return None
|
||||||
@@ -4,6 +4,9 @@ tag: user_tag
|
|||||||
# Set terminal background color (custom option not in CONFIG.md)
|
# Set terminal background color (custom option not in CONFIG.md)
|
||||||
set_terminal_bg: false
|
set_terminal_bg: false
|
||||||
|
|
||||||
|
# Check for updates from GitHub repository on startup (default: true)
|
||||||
|
update_checks: true
|
||||||
|
|
||||||
# Muxing configuration
|
# Muxing configuration
|
||||||
muxing:
|
muxing:
|
||||||
set_title: false
|
set_title: false
|
||||||
|
|||||||
Reference in New Issue
Block a user