package dependency updates

This commit is contained in:
2026-09-01 18:02:31 -07:00
parent e9ef78c872
commit 4d00ef86e4
4 changed files with 123 additions and 11 deletions
+73 -11
View File
@@ -1,6 +1,6 @@
"""
Filename: plugins.py
Description: Plugins window
Description: Plugins window with deferred remote fetching and installation state checks.
Author: Tyler de Zeeuw
License: GPL-3.0
@@ -10,7 +10,7 @@ License: GPL-3.0
from typing import Any
# External library imports
from PySide6.QtCore import Qt
from PySide6.QtCore import Qt, QThread, Signal
from PySide6.QtGui import QColor
from PySide6.QtWidgets import (
QHBoxLayout, QLabel, QLineEdit, QListWidget, QListWidgetItem,
@@ -21,6 +21,20 @@ from plugin_manager import PluginManager, parse_version
from src.shared.shareddata import APP_NAME, CURRENT_VERSION, PLUGINS_URL
class RemoteFetchWorker(QThread):
"""Background thread to fetch remote repository data without lagging the main UI."""
fetched = Signal(list, bool) # (plugins_data, all_failed)
def __init__(self, manager: PluginManager, urls: list[str]) -> None:
super().__init__()
self.manager = manager
self.urls = urls
def run(self) -> None:
plugins, all_failed = self.manager.fetch_remote_repositories(self.urls)
self.fetched.emit(plugins, all_failed)
class PluginsWindow(QWidget):
def __init__(self, parent: QWidget | None, plugin_manager: PluginManager) -> None:
@@ -29,12 +43,13 @@ class PluginsWindow(QWidget):
self.main_app_window = parent
self.manager: PluginManager = plugin_manager
self.setWindowTitle(f"{APP_NAME.upper()} - Plugins")
self.setWindowTitle(f"Plugins - {APP_NAME.upper()}")
self.resize(750, 500)
self.repository_urls: list[str] = [PLUGINS_URL]
self.remote_plugins_data: list[dict[str, Any]] = []
self._has_fetched_remote: bool = False
self._fetch_worker: RemoteFetchWorker | None = None
# Refresh UI automatically if manager updates state
self.manager.plugins_changed.connect(self.refresh_installed_plugins)
@@ -74,6 +89,10 @@ class PluginsWindow(QWidget):
self.installed_list.addItem("No plugins installed.")
self._clear_details_panel()
# Re-populate browser list if remote data was already fetched to reflect newly installed/uninstalled plugins
if self._has_fetched_remote and self.remote_plugins_data:
self._populate_browser_list()
def _on_installed_item_changed(self, current: QListWidgetItem | None, _: Any) -> None:
"""Updates the right-hand details panel when a plugin is selected."""
if not current:
@@ -136,13 +155,25 @@ class PluginsWindow(QWidget):
self.manager.uninstall_plugin(info["path"])
def fetch_remote_plugins(self) -> None:
"""Asynchronously fetches remote plugins on a background thread."""
if self._fetch_worker is not None and self._fetch_worker.isRunning():
return # Fetch operation already in progress
self.browser_list.clear()
self.browser_list.addItem("Fetching remote repositories...")
self.btn_install.setEnabled(False)
self.btn_fetch.setEnabled(False)
plugins, all_failed = self.manager.fetch_remote_repositories(self.repository_urls)
# Defer network call to background worker to avoid UI freeze
self._fetch_worker = RemoteFetchWorker(self.manager, self.repository_urls)
self._fetch_worker.fetched.connect(self._on_remote_fetched)
self._fetch_worker.start()
def _on_remote_fetched(self, plugins: list[dict[str, Any]], all_failed: bool) -> None:
"""Callback executed on the main UI thread when remote fetching finishes."""
self.remote_plugins_data = plugins
self._has_fetched_remote = True
self.btn_fetch.setEnabled(True)
if plugins:
self._populate_browser_list()
@@ -154,9 +185,17 @@ class PluginsWindow(QWidget):
self.browser_list.addItem("No plugins found across configured repositories.")
def _populate_browser_list(self) -> None:
"""Populates the browser tab list and checks if each plugin is already installed."""
self.browser_list.clear()
# Gather set of currently installed identifiers (by ID and Name)
installed_info = self.manager.get_installed_plugins_info()
installed_ids = {p.get("id") for p in installed_info if p.get("id")}
installed_names = {p.get("name") for p in installed_info if p.get("name")}
for plugin in self.remote_plugins_data:
name = plugin.get("name", "Unknown")
p_id = plugin.get("id", "")
version = plugin.get("version", "v0.0")
desc = plugin.get("description", "")
platforms = plugin.get("platforms", [])
@@ -166,16 +205,24 @@ class PluginsWindow(QWidget):
is_version_ok = self.manager.current_app_version >= parse_version(min_v_str)
is_compatible = is_platform_ok and is_version_ok
# Check if plugin is already installed locally
is_installed = (bool(p_id) and p_id in installed_ids) or (bool(name) and name in installed_names)
display_text = f"{name} (v{version}) - {desc}"
if not is_compatible:
if is_installed:
display_text += " [Installed]"
elif not is_compatible:
display_text += " [Incompatible]"
item = QListWidgetItem(display_text, self.browser_list)
item.setData(Qt.ItemDataRole.UserRole, plugin)
item.setData(Qt.ItemDataRole.UserRole + 1, is_compatible)
item.setData(Qt.ItemDataRole.UserRole + 2, is_installed)
if not is_compatible:
item.setForeground(QColor("#d32f2f"))
if is_installed:
item.setForeground(QColor("#2e7d32")) # Green for installed
elif not is_compatible:
item.setForeground(QColor("#d32f2f")) # Red for incompatible
def install_selected_plugin(self) -> None:
selected = self.browser_list.currentItem()
@@ -200,7 +247,21 @@ class PluginsWindow(QWidget):
def _on_browser_item_changed(self, current: QListWidgetItem | None, _: Any) -> None:
if current:
self.btn_install.setEnabled(bool(current.data(Qt.ItemDataRole.UserRole + 1)))
is_compatible = bool(current.data(Qt.ItemDataRole.UserRole + 1))
is_installed = bool(current.data(Qt.ItemDataRole.UserRole + 2))
# Disable install button if plugin is incompatible OR ALREADY INSTALLED
self.btn_install.setEnabled(is_compatible and not is_installed)
else:
self.btn_install.setEnabled(False)
def _on_add_repo(self) -> None:
"""Adds a custom repository URL and re-fetches plugins."""
url = self.repo_input.text().strip()
if url and url not in self.repository_urls:
self.repository_urls.append(url)
self.repo_input.clear()
self.fetch_remote_plugins()
def _create_installed_tab(self) -> QWidget:
tab = QWidget()
@@ -284,6 +345,7 @@ class PluginsWindow(QWidget):
self.repo_input = QLineEdit(tab)
self.repo_input.setPlaceholderText("Enter custom plugins.json URL...")
btn_add = QPushButton("Add Repo", tab)
btn_add.clicked.connect(self._on_add_repo)
repo_layout.addWidget(self.repo_input)
repo_layout.addWidget(btn_add)
@@ -292,14 +354,14 @@ class PluginsWindow(QWidget):
self.browser_list.currentItemChanged.connect(self._on_browser_item_changed)
btn_layout = QHBoxLayout()
btn_fetch = QPushButton("Fetch Remote Lists", tab)
self.btn_fetch = QPushButton("Fetch Remote Lists", tab)
self.btn_install = QPushButton("Install Plugin", tab)
self.btn_install.setEnabled(False)
btn_fetch.clicked.connect(self.fetch_remote_plugins)
self.btn_fetch.clicked.connect(self.fetch_remote_plugins)
self.btn_install.clicked.connect(self.install_selected_plugin)
btn_layout.addWidget(btn_fetch)
btn_layout.addWidget(self.btn_fetch)
btn_layout.addStretch()
btn_layout.addWidget(self.btn_install)