mirror of
https://github.com/Omni-guides/Jackify.git
synced 2026-01-17 11:37:01 +01:00
337 lines
12 KiB
Python
337 lines
12 KiB
Python
"""
|
|
Update notification and download dialog for Jackify.
|
|
|
|
This dialog handles notifying users about available updates and
|
|
managing the download/installation process.
|
|
"""
|
|
|
|
import logging
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
|
|
from PySide6.QtWidgets import (
|
|
QDialog, QVBoxLayout, QHBoxLayout, QLabel, QPushButton,
|
|
QTextEdit, QProgressBar, QGroupBox, QCheckBox
|
|
)
|
|
from PySide6.QtCore import Qt, QThread, Signal
|
|
from PySide6.QtGui import QPixmap, QFont
|
|
|
|
from ....backend.services.update_service import UpdateService, UpdateInfo
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class UpdateDownloadThread(QThread):
|
|
"""Background thread for downloading updates."""
|
|
|
|
progress_updated = Signal(int, int) # downloaded, total
|
|
download_finished = Signal(object) # Path or None
|
|
|
|
def __init__(self, update_service: UpdateService, update_info: UpdateInfo):
|
|
super().__init__()
|
|
self.update_service = update_service
|
|
self.update_info = update_info
|
|
self.downloaded_path = None
|
|
|
|
def run(self):
|
|
"""Download the update in background."""
|
|
try:
|
|
def progress_callback(downloaded: int, total: int):
|
|
self.progress_updated.emit(downloaded, total)
|
|
|
|
self.downloaded_path = self.update_service.download_update(
|
|
self.update_info, progress_callback
|
|
)
|
|
|
|
self.download_finished.emit(self.downloaded_path)
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error in download thread: {e}")
|
|
self.download_finished.emit(None)
|
|
|
|
|
|
class UpdateDialog(QDialog):
|
|
"""Dialog for notifying users about updates and handling downloads."""
|
|
|
|
def __init__(self, update_info: UpdateInfo, update_service: UpdateService, parent=None):
|
|
super().__init__(parent)
|
|
self.update_info = update_info
|
|
self.update_service = update_service
|
|
self.downloaded_path = None
|
|
self.download_thread = None
|
|
|
|
self.setup_ui()
|
|
self.setup_connections()
|
|
|
|
def setup_ui(self):
|
|
"""Set up the dialog UI."""
|
|
self.setWindowTitle("Jackify Update Available")
|
|
self.setModal(True)
|
|
self.setMinimumSize(500, 400)
|
|
self.setMaximumSize(600, 600)
|
|
|
|
layout = QVBoxLayout(self)
|
|
|
|
# Header
|
|
header_layout = QHBoxLayout()
|
|
|
|
# Update icon (if available)
|
|
icon_label = QLabel()
|
|
icon_label.setText("^") # Update arrow symbol
|
|
icon_label.setStyleSheet("font-size: 24px; color: #3fd0ea; font-weight: bold;")
|
|
header_layout.addWidget(icon_label)
|
|
|
|
# Update title
|
|
title_layout = QVBoxLayout()
|
|
title_label = QLabel(f"Update Available: v{self.update_info.version}")
|
|
title_font = QFont()
|
|
title_font.setPointSize(14)
|
|
title_font.setBold(True)
|
|
title_label.setFont(title_font)
|
|
title_label.setStyleSheet("color: #3fd0ea;")
|
|
title_layout.addWidget(title_label)
|
|
|
|
subtitle_label = QLabel(f"Current version: v{self.update_service.current_version}")
|
|
subtitle_label.setStyleSheet("color: #666;")
|
|
title_layout.addWidget(subtitle_label)
|
|
|
|
header_layout.addLayout(title_layout)
|
|
header_layout.addStretch()
|
|
|
|
layout.addLayout(header_layout)
|
|
|
|
# File size info
|
|
if self.update_info.file_size:
|
|
size_mb = self.update_info.file_size / (1024 * 1024)
|
|
update_type = "Delta update" if self.update_info.is_delta_update else "Full update"
|
|
size_label = QLabel(f"{update_type} - Download size: {size_mb:.1f} MB")
|
|
size_label.setStyleSheet("color: #666; margin-bottom: 10px;")
|
|
layout.addWidget(size_label)
|
|
|
|
# Changelog group
|
|
changelog_group = QGroupBox("What's New")
|
|
changelog_layout = QVBoxLayout(changelog_group)
|
|
|
|
self.changelog_text = QTextEdit()
|
|
self.changelog_text.setPlainText(self.update_info.changelog or "No changelog available.")
|
|
self.changelog_text.setMaximumHeight(150)
|
|
self.changelog_text.setReadOnly(True)
|
|
changelog_layout.addWidget(self.changelog_text)
|
|
|
|
layout.addWidget(changelog_group)
|
|
|
|
# Progress section (initially hidden)
|
|
self.progress_group = QGroupBox("Download Progress")
|
|
progress_layout = QVBoxLayout(self.progress_group)
|
|
|
|
self.progress_bar = QProgressBar()
|
|
self.progress_bar.setVisible(False)
|
|
progress_layout.addWidget(self.progress_bar)
|
|
|
|
self.progress_label = QLabel("Preparing download...")
|
|
self.progress_label.setVisible(False)
|
|
progress_layout.addWidget(self.progress_label)
|
|
|
|
layout.addWidget(self.progress_group)
|
|
self.progress_group.setVisible(False)
|
|
|
|
# Options
|
|
options_group = QGroupBox("Update Options")
|
|
options_layout = QVBoxLayout(options_group)
|
|
|
|
self.auto_restart_checkbox = QCheckBox("Automatically restart Jackify after update")
|
|
self.auto_restart_checkbox.setChecked(True)
|
|
options_layout.addWidget(self.auto_restart_checkbox)
|
|
|
|
layout.addWidget(options_group)
|
|
|
|
# Buttons
|
|
button_layout = QHBoxLayout()
|
|
|
|
self.later_button = QPushButton("Remind Me Later")
|
|
self.later_button.clicked.connect(self.remind_later)
|
|
button_layout.addWidget(self.later_button)
|
|
|
|
self.skip_button = QPushButton("Skip This Version")
|
|
self.skip_button.clicked.connect(self.skip_version)
|
|
button_layout.addWidget(self.skip_button)
|
|
|
|
button_layout.addStretch()
|
|
|
|
self.download_button = QPushButton("Download && Install Update")
|
|
self.download_button.setDefault(True)
|
|
self.download_button.clicked.connect(self.start_download)
|
|
button_layout.addWidget(self.download_button)
|
|
|
|
self.install_button = QPushButton("Install && Restart")
|
|
self.install_button.setVisible(False)
|
|
self.install_button.clicked.connect(self.install_update)
|
|
self.install_button.setStyleSheet("""
|
|
QPushButton {
|
|
background-color: #23272e;
|
|
color: #3fd0ea;
|
|
font-weight: bold;
|
|
padding: 8px 16px;
|
|
border-radius: 4px;
|
|
border: 2px solid #3fd0ea;
|
|
}
|
|
QPushButton:hover {
|
|
background-color: #3fd0ea;
|
|
color: #23272e;
|
|
}
|
|
QPushButton:pressed {
|
|
background-color: #2bb8d6;
|
|
color: #23272e;
|
|
}
|
|
""")
|
|
button_layout.addWidget(self.install_button)
|
|
|
|
layout.addLayout(button_layout)
|
|
|
|
# Style the download button to match Jackify theme (dark with blue text)
|
|
self.download_button.setStyleSheet("""
|
|
QPushButton {
|
|
background-color: #23272e;
|
|
color: #3fd0ea;
|
|
font-weight: bold;
|
|
padding: 8px 16px;
|
|
border-radius: 4px;
|
|
border: 2px solid #3fd0ea;
|
|
}
|
|
QPushButton:hover {
|
|
background-color: #3fd0ea;
|
|
color: #23272e;
|
|
}
|
|
QPushButton:pressed {
|
|
background-color: #2bb8d6;
|
|
color: #23272e;
|
|
}
|
|
""")
|
|
|
|
def setup_connections(self):
|
|
"""Set up signal connections."""
|
|
pass
|
|
|
|
def start_download(self):
|
|
"""Start downloading the update."""
|
|
if not self.update_service.can_update():
|
|
self.show_error("Update not possible",
|
|
"Cannot update: not running as AppImage or insufficient permissions.")
|
|
return
|
|
|
|
# Show progress UI
|
|
self.progress_group.setVisible(True)
|
|
self.progress_bar.setVisible(True)
|
|
self.progress_label.setVisible(True)
|
|
self.progress_label.setText("Starting download...")
|
|
|
|
# Disable buttons during download
|
|
self.download_button.setEnabled(False)
|
|
self.later_button.setEnabled(False)
|
|
self.skip_button.setEnabled(False)
|
|
|
|
# Start download thread
|
|
self.download_thread = UpdateDownloadThread(self.update_service, self.update_info)
|
|
self.download_thread.progress_updated.connect(self.update_progress)
|
|
self.download_thread.download_finished.connect(self.download_completed)
|
|
self.download_thread.start()
|
|
|
|
def update_progress(self, downloaded: int, total: int):
|
|
"""Update download progress."""
|
|
if total > 0:
|
|
percentage = int((downloaded / total) * 100)
|
|
self.progress_bar.setValue(percentage)
|
|
|
|
downloaded_mb = downloaded / (1024 * 1024)
|
|
total_mb = total / (1024 * 1024)
|
|
|
|
self.progress_label.setText(f"Downloaded {downloaded_mb:.1f} MB of {total_mb:.1f} MB ({percentage}%)")
|
|
else:
|
|
self.progress_label.setText(f"Downloaded {downloaded / (1024 * 1024):.1f} MB...")
|
|
|
|
def download_completed(self, downloaded_path: Optional[Path]):
|
|
"""Handle download completion."""
|
|
if downloaded_path:
|
|
self.downloaded_path = downloaded_path
|
|
self.progress_label.setText("Download completed successfully!")
|
|
self.progress_bar.setValue(100)
|
|
|
|
# Show install button
|
|
self.download_button.setVisible(False)
|
|
self.install_button.setVisible(True)
|
|
|
|
# Re-enable other buttons
|
|
self.later_button.setEnabled(True)
|
|
self.skip_button.setEnabled(True)
|
|
|
|
else:
|
|
self.show_error("Download Failed", "Failed to download the update. Please try again later.")
|
|
|
|
# Reset UI
|
|
self.progress_group.setVisible(False)
|
|
self.download_button.setEnabled(True)
|
|
self.later_button.setEnabled(True)
|
|
self.skip_button.setEnabled(True)
|
|
|
|
def install_update(self):
|
|
"""Install the downloaded update."""
|
|
if not self.downloaded_path:
|
|
self.show_error("No Download", "No update has been downloaded.")
|
|
return
|
|
|
|
self.progress_label.setText("Installing update...")
|
|
|
|
if self.update_service.apply_update(self.downloaded_path):
|
|
self.progress_label.setText("Update applied successfully! Jackify will restart...")
|
|
|
|
# Close dialog and exit application (update helper will restart)
|
|
self.accept()
|
|
|
|
# The update helper script will handle the restart
|
|
import sys
|
|
sys.exit(0)
|
|
|
|
else:
|
|
self.show_error("Installation Failed", "Failed to apply the update. Please try again.")
|
|
|
|
def remind_later(self):
|
|
"""Close dialog and remind later."""
|
|
self.reject()
|
|
|
|
def skip_version(self):
|
|
"""Skip this version and save preference."""
|
|
try:
|
|
# Save the skipped version to config
|
|
from jackify.backend.handlers.config_handler import ConfigHandler
|
|
config_handler = ConfigHandler()
|
|
|
|
# Get current skipped versions
|
|
skipped_versions = config_handler.get('skipped_versions', [])
|
|
|
|
# Add this version to skipped list
|
|
if self.update_info.version not in skipped_versions:
|
|
skipped_versions.append(self.update_info.version)
|
|
config_handler.set('skipped_versions', skipped_versions)
|
|
config_handler.save()
|
|
|
|
logger.info(f"Skipped version {self.update_info.version}")
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error saving skip preference: {e}")
|
|
|
|
self.reject()
|
|
|
|
def show_error(self, title: str, message: str):
|
|
"""Show error message to user."""
|
|
from PySide6.QtWidgets import QMessageBox
|
|
QMessageBox.warning(self, title, message)
|
|
|
|
def closeEvent(self, event):
|
|
"""Handle dialog close event."""
|
|
if self.download_thread and self.download_thread.isRunning():
|
|
# Cancel download if in progress
|
|
self.download_thread.terminate()
|
|
self.download_thread.wait()
|
|
|
|
event.accept() |