Add auto-update feature with Gitea release checking
- Add UpdateChecker class to query Gitea API for latest releases - Show update dialog with release notes when new version available - Open browser to release page for download (handles large files) - Allow users to skip specific versions or defer updates - Add "Check for Updates Now" button in settings - Check automatically on startup (respects 24-hour interval) - Pre-configured for repo.anhonesthost.net/streamer-tools Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,10 +2,13 @@
|
||||
|
||||
from PySide6.QtWidgets import (
|
||||
QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
||||
QPushButton, QLabel, QFileDialog, QMessageBox
|
||||
QPushButton, QLabel, QFileDialog, QMessageBox,
|
||||
QDialog, QTextEdit, QCheckBox
|
||||
)
|
||||
from PySide6.QtCore import Qt, QThread, Signal
|
||||
from PySide6.QtCore import Qt, QThread, Signal, QTimer
|
||||
from PySide6.QtGui import QFont
|
||||
import webbrowser
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
@@ -66,6 +69,90 @@ class EngineStartThread(QThread):
|
||||
self.finished.emit(False, f"Error initializing engine: {e}")
|
||||
|
||||
|
||||
class UpdateDialog(QDialog):
|
||||
"""Dialog showing available update information."""
|
||||
|
||||
def __init__(self, parent, current_version: str, release_info):
|
||||
"""
|
||||
Initialize the update dialog.
|
||||
|
||||
Args:
|
||||
parent: Parent window
|
||||
current_version: Current application version
|
||||
release_info: ReleaseInfo object with update details
|
||||
"""
|
||||
super().__init__(parent)
|
||||
self.release_info = release_info
|
||||
self.skip_version = False
|
||||
|
||||
self.setWindowTitle("Update Available")
|
||||
self.setModal(True)
|
||||
self.setMinimumSize(500, 400)
|
||||
self.resize(550, 450)
|
||||
|
||||
layout = QVBoxLayout()
|
||||
layout.setSpacing(15)
|
||||
layout.setContentsMargins(20, 20, 20, 20)
|
||||
self.setLayout(layout)
|
||||
|
||||
# Title
|
||||
title_label = QLabel("Update Available!")
|
||||
title_font = QFont()
|
||||
title_font.setPointSize(16)
|
||||
title_font.setBold(True)
|
||||
title_label.setFont(title_font)
|
||||
layout.addWidget(title_label)
|
||||
|
||||
# Version info
|
||||
version_label = QLabel(f"Current: {current_version} \u2192 New: {release_info.version}")
|
||||
version_font = QFont()
|
||||
version_font.setPointSize(12)
|
||||
version_label.setFont(version_font)
|
||||
layout.addWidget(version_label)
|
||||
|
||||
# Release notes
|
||||
notes_label = QLabel("Release Notes:")
|
||||
notes_label.setStyleSheet("font-weight: bold; margin-top: 10px;")
|
||||
layout.addWidget(notes_label)
|
||||
|
||||
self.notes_text = QTextEdit()
|
||||
self.notes_text.setReadOnly(True)
|
||||
self.notes_text.setPlainText(release_info.release_notes or "No release notes available.")
|
||||
layout.addWidget(self.notes_text)
|
||||
|
||||
# Skip version checkbox
|
||||
self.skip_checkbox = QCheckBox(f"Skip version {release_info.version}")
|
||||
self.skip_checkbox.setToolTip("Don't show this update again")
|
||||
layout.addWidget(self.skip_checkbox)
|
||||
|
||||
# Buttons
|
||||
button_layout = QHBoxLayout()
|
||||
button_layout.addStretch()
|
||||
|
||||
self.later_button = QPushButton("Remind Me Later")
|
||||
self.later_button.clicked.connect(self._on_later)
|
||||
button_layout.addWidget(self.later_button)
|
||||
|
||||
self.download_button = QPushButton("Download Update")
|
||||
self.download_button.setStyleSheet("background-color: #2ecc71; color: white; font-weight: bold;")
|
||||
self.download_button.clicked.connect(self._on_download)
|
||||
button_layout.addWidget(self.download_button)
|
||||
|
||||
layout.addLayout(button_layout)
|
||||
|
||||
def _on_later(self):
|
||||
"""Handle 'Remind Me Later' button click."""
|
||||
self.skip_version = self.skip_checkbox.isChecked()
|
||||
self.reject()
|
||||
|
||||
def _on_download(self):
|
||||
"""Handle 'Download Update' button click."""
|
||||
self.skip_version = self.skip_checkbox.isChecked()
|
||||
if self.release_info.download_url:
|
||||
webbrowser.open(self.release_info.download_url)
|
||||
self.accept()
|
||||
|
||||
|
||||
class MainWindow(QMainWindow):
|
||||
"""Main application window using PySide6."""
|
||||
|
||||
@@ -136,6 +223,9 @@ class MainWindow(QMainWindow):
|
||||
# Initialize components (in background)
|
||||
self._initialize_components()
|
||||
|
||||
# Schedule update check 3 seconds after startup (non-blocking)
|
||||
QTimer.singleShot(3000, self._startup_update_check)
|
||||
|
||||
def _update_splash(self, message: str):
|
||||
"""Update splash screen message if it exists."""
|
||||
if self.splash_screen:
|
||||
@@ -836,3 +926,98 @@ class MainWindow(QMainWindow):
|
||||
self.engine_start_thread.wait()
|
||||
|
||||
event.accept()
|
||||
|
||||
def _startup_update_check(self):
|
||||
"""Check for updates on startup (called via QTimer)."""
|
||||
# Only check if auto_check is enabled
|
||||
if not self.config.get('updates.auto_check', True):
|
||||
return
|
||||
|
||||
# Check if enough time has passed since last check
|
||||
last_check_str = self.config.get('updates.last_check', '')
|
||||
check_interval = self.config.get('updates.check_interval_hours', 24)
|
||||
|
||||
if last_check_str:
|
||||
try:
|
||||
last_check = datetime.fromisoformat(last_check_str)
|
||||
hours_since_check = (datetime.now() - last_check).total_seconds() / 3600
|
||||
if hours_since_check < check_interval:
|
||||
print(f"Skipping update check - last checked {hours_since_check:.1f} hours ago")
|
||||
return
|
||||
except (ValueError, TypeError):
|
||||
pass # Invalid date format, proceed with check
|
||||
|
||||
# Perform async update check
|
||||
self._check_for_updates(show_no_update_message=False)
|
||||
|
||||
def _check_for_updates(self, show_no_update_message: bool = True):
|
||||
"""
|
||||
Check for updates.
|
||||
|
||||
Args:
|
||||
show_no_update_message: Whether to show a message if no update is available
|
||||
"""
|
||||
from client.update_checker import UpdateChecker
|
||||
|
||||
gitea_url = self.config.get('updates.gitea_url', 'https://repo.anhonesthost.net')
|
||||
owner = self.config.get('updates.owner', 'streamer-tools')
|
||||
repo = self.config.get('updates.repo', 'local-transcription')
|
||||
|
||||
if not gitea_url or not owner or not repo:
|
||||
if show_no_update_message:
|
||||
QMessageBox.warning(self, "Update Check", "Update checking is not configured.")
|
||||
return
|
||||
|
||||
checker = UpdateChecker(
|
||||
current_version=__version__,
|
||||
gitea_url=gitea_url,
|
||||
owner=owner,
|
||||
repo=repo
|
||||
)
|
||||
|
||||
def on_update_check_complete(release_info, error):
|
||||
# Update last check time
|
||||
self.config.set('updates.last_check', datetime.now().isoformat())
|
||||
|
||||
if error:
|
||||
print(f"Update check failed: {error}")
|
||||
if show_no_update_message:
|
||||
QMessageBox.warning(self, "Update Check Failed", f"Could not check for updates:\n{error}")
|
||||
return
|
||||
|
||||
if release_info:
|
||||
# Check if this version is skipped
|
||||
skipped_versions = self.config.get('updates.skipped_versions', [])
|
||||
if release_info.version in skipped_versions:
|
||||
print(f"Skipping update notification for version {release_info.version} (user skipped)")
|
||||
return
|
||||
|
||||
# Show update dialog on main thread
|
||||
QTimer.singleShot(0, lambda: self._show_update_dialog(release_info))
|
||||
else:
|
||||
if show_no_update_message:
|
||||
QMessageBox.information(
|
||||
self,
|
||||
"No Updates",
|
||||
f"You are running the latest version ({__version__})."
|
||||
)
|
||||
|
||||
# Run check in background thread
|
||||
checker.check_for_update_async(on_update_check_complete)
|
||||
|
||||
def _show_update_dialog(self, release_info):
|
||||
"""
|
||||
Show the update dialog.
|
||||
|
||||
Args:
|
||||
release_info: ReleaseInfo object with update details
|
||||
"""
|
||||
dialog = UpdateDialog(self, __version__, release_info)
|
||||
dialog.exec()
|
||||
|
||||
# If user chose to skip this version, save it
|
||||
if dialog.skip_version:
|
||||
skipped_versions = self.config.get('updates.skipped_versions', [])
|
||||
if release_info.version not in skipped_versions:
|
||||
skipped_versions.append(release_info.version)
|
||||
self.config.set('updates.skipped_versions', skipped_versions)
|
||||
|
||||
Reference in New Issue
Block a user