diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b5f590..2a199b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,41 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [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 ### Added diff --git a/pyproject.toml b/pyproject.toml index 32d65e7..c33ddb1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "unshackle" -version = "1.1.0" +version = "1.1.1" description = "Modular Movie, TV, and Music Archival Software." authors = [{ name = "unshackle team" }] requires-python = ">=3.10,<3.13" diff --git a/unshackle/commands/env.py b/unshackle/commands/env.py index 5cc9c4b..f4dbe8a 100644 --- a/unshackle/commands/env.py +++ b/unshackle/commands/env.py @@ -60,7 +60,7 @@ def check() -> None: "name": "N_m3u8DL-RE", "binary": binaries.N_m3u8DL_RE, "required": False, - "desc": "HLS/DASH", + "desc": "HLS/DASH/ISM", "cat": "Download", }, # Subtitle Tools diff --git a/unshackle/core/__init__.py b/unshackle/core/__init__.py index 6849410..a82b376 100644 --- a/unshackle/core/__init__.py +++ b/unshackle/core/__init__.py @@ -1 +1 @@ -__version__ = "1.1.0" +__version__ = "1.1.1" diff --git a/unshackle/core/__main__.py b/unshackle/core/__main__.py index 63f7fa4..9a5da25 100644 --- a/unshackle/core/__main__.py +++ b/unshackle/core/__main__.py @@ -15,6 +15,7 @@ from unshackle.core.commands import Commands from unshackle.core.config import config from unshackle.core.console import ComfyRichHandler, console from unshackle.core.constants import context_settings +from unshackle.core.update_checker import UpdateChecker from unshackle.core.utilities import rotate_log_file LOGGING_PATH = None @@ -79,6 +80,22 @@ def main(version: bool, debug: bool, log_path: Path) -> None: if version: 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 def save_log(): diff --git a/unshackle/core/config.py b/unshackle/core/config.py index c3579be..64cc788 100644 --- a/unshackle/core/config.py +++ b/unshackle/core/config.py @@ -78,6 +78,7 @@ class Config: self.set_terminal_bg: bool = kwargs.get("set_terminal_bg", False) self.tag: str = kwargs.get("tag") or "" self.tmdb_api_key: str = kwargs.get("tmdb_api_key") or "" + self.update_checks: bool = kwargs.get("update_checks", True) @classmethod def from_yaml(cls, path: Path) -> Config: diff --git a/unshackle/core/update_checker.py b/unshackle/core/update_checker.py new file mode 100644 index 0000000..671d8d6 --- /dev/null +++ b/unshackle/core/update_checker.py @@ -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 diff --git a/unshackle/unshackle.yaml b/unshackle/unshackle.yaml index 9714180..c15c7c0 100644 --- a/unshackle/unshackle.yaml +++ b/unshackle/unshackle.yaml @@ -4,6 +4,9 @@ tag: user_tag # Set terminal background color (custom option not in CONFIG.md) set_terminal_bg: false +# Check for updates from GitHub repository on startup (default: true) +update_checks: true + # Muxing configuration muxing: set_title: false diff --git a/uv.lock b/uv.lock index e55a99b..1bc0d81 100644 --- a/uv.lock +++ b/uv.lock @@ -1505,7 +1505,7 @@ wheels = [ [[package]] name = "unshackle" -version = "1.1.0" +version = "1.1.1" source = { editable = "." } dependencies = [ { name = "appdirs" },