diff options
| author | xXJSONDeruloXx <danielhimebauch@gmail.com> | 2025-07-16 17:11:00 -0400 |
|---|---|---|
| committer | xXJSONDeruloXx <danielhimebauch@gmail.com> | 2025-07-16 17:11:00 -0400 |
| commit | 2811ba4e29cd27b5893fba676278f29b155068cb (patch) | |
| tree | 5eaab7f419b277196ed4c22939ce0ea74555f4ce /py_modules/lsfg_vk/plugin.py | |
| parent | 44393f6e126c3dff196283a2079162e3eb9245a2 (diff) | |
| download | decky-lsfg-vk-2811ba4e29cd27b5893fba676278f29b155068cb.tar.gz decky-lsfg-vk-2811ba4e29cd27b5893fba676278f29b155068cb.zip | |
non modal updater
Diffstat (limited to 'py_modules/lsfg_vk/plugin.py')
| -rw-r--r-- | py_modules/lsfg_vk/plugin.py | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/py_modules/lsfg_vk/plugin.py b/py_modules/lsfg_vk/plugin.py index b357ee6..3e37047 100644 --- a/py_modules/lsfg_vk/plugin.py +++ b/py_modules/lsfg_vk/plugin.py @@ -6,7 +6,12 @@ Vulkan layer for Lossless Scaling frame generation on Steam Deck. """ import os +import json +import subprocess +import urllib.request +import ssl from typing import Dict, Any +from pathlib import Path from .installation import InstallationService from .dll_detection import DllDetectionService @@ -106,6 +111,200 @@ class Plugin: enable_lsfg, multiplier, flow_scale, hdr, perf_mode, immediate_mode, disable_vkbasalt, frame_cap ) + # Self-updater methods + async def check_for_plugin_update(self) -> Dict[str, Any]: + """Check for plugin updates by comparing current version with latest GitHub release + + Returns: + Dict containing update information: + { + "update_available": bool, + "current_version": str, + "latest_version": str, + "release_notes": str, + "release_date": str, + "download_url": str, + "error": str (if error occurred) + } + """ + try: + import decky + + # Read current version from package.json + package_json_path = Path(decky.DECKY_PLUGIN_DIR) / "package.json" + current_version = "0.0.0" + + if package_json_path.exists(): + try: + with open(package_json_path, 'r', encoding='utf-8') as f: + package_data = json.load(f) + current_version = package_data.get('version', '0.0.0') + except Exception as e: + decky.logger.warning(f"Failed to read package.json: {e}") + + # Fetch latest release from GitHub + api_url = "https://api.github.com/repos/xXJSONDeruloXx/decky-lossless-scaling-vk/releases/latest" + + try: + # Create SSL context that doesn't verify certificates + # This is needed on Steam Deck where certificate verification often fails + ssl_context = ssl.create_default_context() + ssl_context.check_hostname = False + ssl_context.verify_mode = ssl.CERT_NONE + + # Use urllib to fetch the latest release info + with urllib.request.urlopen(api_url, context=ssl_context) as response: + release_data = json.loads(response.read().decode('utf-8')) + + latest_version = release_data.get('tag_name', '').lstrip('v') + release_notes = release_data.get('body', '') + release_date = release_data.get('published_at', '') + + # Find the plugin zip download URL + download_url = "" + for asset in release_data.get('assets', []): + if asset.get('name', '').endswith('.zip'): + download_url = asset.get('browser_download_url', '') + break + + # Compare versions + update_available = self._compare_versions(current_version, latest_version) + + return { + "success": True, + "update_available": update_available, + "current_version": current_version, + "latest_version": latest_version, + "release_notes": release_notes, + "release_date": release_date, + "download_url": download_url + } + + except Exception as e: + decky.logger.error(f"Failed to fetch release info: {e}") + return { + "success": False, + "error": f"Failed to check for updates: {str(e)}" + } + + except Exception as e: + return { + "success": False, + "error": f"Update check failed: {str(e)}" + } + + async def download_plugin_update(self, download_url: str) -> Dict[str, Any]: + """Download the plugin update zip file to ~/Downloads + + Args: + download_url: URL to download the plugin zip from + + Returns: + Dict containing download result: + { + "success": bool, + "download_path": str, + "error": str (if error occurred) + } + """ + try: + import decky + + # Create download path + downloads_dir = Path.home() / "Downloads" + downloads_dir.mkdir(exist_ok=True) + download_path = downloads_dir / "decky-lossless-scaling-vk.zip" + + # Remove existing file if it exists + if download_path.exists(): + download_path.unlink() + + # Download the file + decky.logger.info(f"Downloading plugin update from {download_url}") + + try: + # Create SSL context that doesn't verify certificates + # This is needed on Steam Deck where certificate verification often fails + ssl_context = ssl.create_default_context() + ssl_context.check_hostname = False + ssl_context.verify_mode = ssl.CERT_NONE + + # Use urllib to download the file with SSL context + with urllib.request.urlopen(download_url, context=ssl_context) as response: + with open(download_path, 'wb') as f: + f.write(response.read()) + + # Verify the file was downloaded successfully + if download_path.exists() and download_path.stat().st_size > 0: + decky.logger.info(f"Plugin update downloaded successfully to {download_path}") + return { + "success": True, + "download_path": str(download_path) + } + else: + return { + "success": False, + "error": "Download completed but file is empty or missing" + } + + except Exception as e: + decky.logger.error(f"Download failed: {e}") + return { + "success": False, + "error": f"Download failed: {str(e)}" + } + + except Exception as e: + return { + "success": False, + "error": f"Download preparation failed: {str(e)}" + } + + def _compare_versions(self, current: str, latest: str) -> bool: + """Compare two version strings to determine if an update is available + + Args: + current: Current version string (e.g., "1.2.3") + latest: Latest version string (e.g., "1.2.4") + + Returns: + True if latest version is newer than current version + """ + try: + # Remove 'v' prefix if present and split by dots + current_parts = current.lstrip('v').split('.') + latest_parts = latest.lstrip('v').split('.') + + # Pad with zeros if needed to ensure equal length + max_len = max(len(current_parts), len(latest_parts)) + current_parts.extend(['0'] * (max_len - len(current_parts))) + latest_parts.extend(['0'] * (max_len - len(latest_parts))) + + # Compare each part numerically + for i in range(max_len): + try: + current_num = int(current_parts[i]) + latest_num = int(latest_parts[i]) + + if latest_num > current_num: + return True + elif latest_num < current_num: + return False + # If equal, continue to next part + except ValueError: + # If conversion fails, do string comparison + if latest_parts[i] > current_parts[i]: + return True + elif latest_parts[i] < current_parts[i]: + return False + + # All parts are equal + return False + + except Exception: + # If comparison fails, assume no update available + return False + # Plugin lifecycle methods async def _main(self): """ |
