Add ability to ignore system builds

This commit is contained in:
Brett Williams
2025-03-01 00:37:11 -06:00
parent 1af4169447
commit e97e3d74c8
3 changed files with 94 additions and 79 deletions

View File

@@ -34,7 +34,7 @@ class EngineManager:
return obj return obj
@classmethod @classmethod
def get_engines(cls, filter_name=None, include_corrupt=False): def get_engines(cls, filter_name=None, include_corrupt=False, ignore_system=False):
if not cls.engines_path: if not cls.engines_path:
raise FileNotFoundError("Engine path is not set") raise FileNotFoundError("Engine path is not set")
@@ -96,6 +96,7 @@ class EngineManager:
'type': 'system' 'type': 'system'
} }
if not ignore_system:
with concurrent.futures.ThreadPoolExecutor() as executor: with concurrent.futures.ThreadPoolExecutor() as executor:
futures = { futures = {
executor.submit(fetch_engine_details, eng, include_corrupt): eng.name() executor.submit(fetch_engine_details, eng, include_corrupt): eng.name()
@@ -111,31 +112,31 @@ class EngineManager:
return results return results
@classmethod @classmethod
def all_versions_for_engine(cls, engine_name, include_corrupt=False): def all_versions_for_engine(cls, engine_name, include_corrupt=False, ignore_system=False):
versions = cls.get_engines(filter_name=engine_name, include_corrupt=include_corrupt) versions = cls.get_engines(filter_name=engine_name, include_corrupt=include_corrupt, ignore_system=ignore_system)
sorted_versions = sorted(versions, key=lambda x: x['version'], reverse=True) sorted_versions = sorted(versions, key=lambda x: x['version'], reverse=True)
return sorted_versions return sorted_versions
@classmethod @classmethod
def newest_engine_version(cls, engine, system_os=None, cpu=None): def newest_engine_version(cls, engine, system_os=None, cpu=None, ignore_system=None):
system_os = system_os or current_system_os() system_os = system_os or current_system_os()
cpu = cpu or current_system_cpu() cpu = cpu or current_system_cpu()
try: try:
filtered = [x for x in cls.all_versions_for_engine(engine) if x['system_os'] == system_os and filtered = [x for x in cls.all_versions_for_engine(engine, ignore_system=ignore_system)
x['cpu'] == cpu] if x['system_os'] == system_os and x['cpu'] == cpu]
return filtered[0] return filtered[0]
except IndexError: except IndexError:
logger.error(f"Cannot find newest engine version for {engine}-{system_os}-{cpu}") logger.error(f"Cannot find newest engine version for {engine}-{system_os}-{cpu}")
return None return None
@classmethod @classmethod
def is_version_downloaded(cls, engine, version, system_os=None, cpu=None): def is_version_downloaded(cls, engine, version, system_os=None, cpu=None, ignore_system=False):
system_os = system_os or current_system_os() system_os = system_os or current_system_os()
cpu = cpu or current_system_cpu() cpu = cpu or current_system_cpu()
filtered = [x for x in cls.get_engines(filter_name=engine) if x['system_os'] == system_os and filtered = [x for x in cls.get_engines(filter_name=engine, ignore_system=ignore_system) if
x['cpu'] == cpu and x['version'] == version] x['system_os'] == system_os and x['cpu'] == cpu and x['version'] == version]
return filtered[0] if filtered else False return filtered[0] if filtered else False
@classmethod @classmethod
@@ -168,7 +169,7 @@ class EngineManager:
return None return None
@classmethod @classmethod
def download_engine(cls, engine, version, system_os=None, cpu=None, background=False): def download_engine(cls, engine, version, system_os=None, cpu=None, background=False, ignore_system=False):
engine_to_download = cls.engine_with_name(engine) engine_to_download = cls.engine_with_name(engine)
existing_task = cls.get_existing_download_task(engine, version, system_os, cpu) existing_task = cls.get_existing_download_task(engine, version, system_os, cpu)
@@ -191,7 +192,7 @@ class EngineManager:
return thread return thread
thread.join() thread.join()
found_engine = cls.is_version_downloaded(engine, version, system_os, cpu) # Check that engine downloaded found_engine = cls.is_version_downloaded(engine, version, system_os, cpu, ignore_system) # Check that engine downloaded
if not found_engine: if not found_engine:
logger.error(f"Error downloading {engine}") logger.error(f"Error downloading {engine}")
return found_engine return found_engine
@@ -217,34 +218,7 @@ class EngineManager:
return False return False
@classmethod @classmethod
def update_all_engines(cls): def is_engine_update_available(cls, engine_class, ignore_system_installs=False):
def engine_update_task(engine_class):
logger.debug(f"Checking for updates to {engine_class.name()}")
latest_version = engine_class.downloader().find_most_recent_version()
if not latest_version:
logger.warning(f"Could not find most recent version of {engine.name()} to download")
return
version_num = latest_version.get('version')
if cls.is_version_downloaded(engine_class.name(), version_num):
logger.debug(f"Latest version of {engine_class.name()} ({version_num}) already downloaded")
return
# download the engine
logger.info(f"Downloading latest version of {engine_class.name()} ({version_num})...")
cls.download_engine(engine=engine_class.name(), version=version_num, background=True)
logger.info(f"Checking for updates for render engines...")
threads = []
for engine in cls.supported_engines():
if engine.downloader():
thread = threading.Thread(target=engine_update_task, args=(engine,))
threads.append(thread)
thread.start()
@classmethod
def update_engine(cls, engine_class):
logger.debug(f"Checking for updates to {engine_class.name()}") logger.debug(f"Checking for updates to {engine_class.name()}")
latest_version = engine_class.downloader().find_most_recent_version() latest_version = engine_class.downloader().find_most_recent_version()
@@ -253,15 +227,11 @@ class EngineManager:
return return
version_num = latest_version.get('version') version_num = latest_version.get('version')
if cls.is_version_downloaded(engine_class.name(), version_num): if cls.is_version_downloaded(engine_class.name(), version_num, ignore_system=ignore_system_installs):
logger.debug(f"Latest version of {engine_class.name()} ({version_num}) already downloaded") logger.debug(f"Latest version of {engine_class.name()} ({version_num}) already downloaded")
return return
# download the engine return latest_version
logger.info(f"Downloading latest version of {engine_class.name()} ({version_num})...")
download_job = cls.download_engine(engine=engine_class.name(), version=version_num, background=True)
return {"latest": latest_version, "thread": download_job, "name": engine_class.name()}
@classmethod @classmethod
@@ -330,7 +300,8 @@ class EngineDownloadWorker(threading.Thread):
self.cpu = cpu self.cpu = cpu
def run(self): def run(self):
existing_download = EngineManager.is_version_downloaded(self.engine, self.version, self.system_os, self.cpu) existing_download = EngineManager.is_version_downloaded(self.engine, self.version, self.system_os, self.cpu,
ignore_system=True)
if existing_download: if existing_download:
logger.info(f"Requested download of {self.engine} {self.version}, but local copy already exists") logger.info(f"Requested download of {self.engine} {self.version}, but local copy already exists")
return existing_download return existing_download

View File

@@ -5,6 +5,9 @@ import socket
import sys import sys
import threading import threading
from collections import deque from collections import deque
from datetime import datetime
from PyQt6.QtCore import QSettings
from src.api.api_server import start_server from src.api.api_server import start_server
from src.api.preview_manager import PreviewManager from src.api.preview_manager import PreviewManager
@@ -16,7 +19,7 @@ from src.utilities.config import Config
from src.utilities.misc_helper import (system_safe_path, current_system_cpu, current_system_os, from src.utilities.misc_helper import (system_safe_path, current_system_cpu, current_system_os,
current_system_os_version, check_for_updates) current_system_os_version, check_for_updates)
from src.utilities.zeroconf_server import ZeroconfServer from src.utilities.zeroconf_server import ZeroconfServer
from version import APP_NAME, APP_VERSION, APP_REPO_NAME, APP_REPO_OWNER from version import APP_NAME, APP_VERSION, APP_REPO_NAME, APP_REPO_OWNER, APP_AUTHOR
logger = logging.getLogger() logger = logging.getLogger()
@@ -66,6 +69,8 @@ def run(server_only=False) -> int:
APP_VERSION)) APP_VERSION))
update_thread.start() update_thread.start()
settings = QSettings(APP_AUTHOR, APP_NAME)
# main start # main start
logger.info(f"Starting {APP_NAME} Render Server") logger.info(f"Starting {APP_NAME} Render Server")
return_code = 0 return_code = 0
@@ -92,9 +97,16 @@ def run(server_only=False) -> int:
ServerProxyManager.subscribe_to_listener() ServerProxyManager.subscribe_to_listener()
DistributedJobManager.subscribe_to_listener() DistributedJobManager.subscribe_to_listener()
# check for updates for render engines if configured or on first launch # check for updates for render engines if configured
if Config.update_engines_on_launch or not EngineManager.get_engines(): ignore_system = settings.value("engines_ignore_system_installs", False)
EngineManager.update_all_engines() if settings.value('check_for_engine_updates_on_launch', False):
for engine in EngineManager.downloadable_engines():
if settings.value(f'engine_download-{engine.name()}', False):
update_result = EngineManager.is_engine_update_available(engine, ignore_system_installs=ignore_system)
EngineManager.download_engine(engine=engine.name(), version=update_result['version'],
background=True,
ignore_system=ignore_system)
settings.setValue("engines_last_update_time", datetime.now().isoformat())
# get hostname # get hostname
local_hostname = socket.gethostname() local_hostname = socket.gethostname()

View File

@@ -184,6 +184,12 @@ class SettingsWindow(QMainWindow):
installed_layout = QVBoxLayout() installed_layout = QVBoxLayout()
self.installed_engines_table = EngineTableWidget() self.installed_engines_table = EngineTableWidget()
installed_layout.addWidget(self.installed_engines_table) installed_layout.addWidget(self.installed_engines_table)
engine_ignore_system_installs_checkbox = QCheckBox("Ignore system installs")
engine_ignore_system_installs_checkbox.setChecked(settings.value("engines_ignore_system_installs", False))
engine_ignore_system_installs_checkbox.stateChanged.connect(self.change_ignore_system_installs)
installed_layout.addWidget(engine_ignore_system_installs_checkbox)
installed_buttons_layout = QHBoxLayout() installed_buttons_layout = QHBoxLayout()
launch_engine_button = QPushButton("Launch") launch_engine_button = QPushButton("Launch")
launch_engine_button.clicked.connect(self.launch_selected_engine) launch_engine_button.clicked.connect(self.launch_selected_engine)
@@ -226,7 +232,7 @@ class SettingsWindow(QMainWindow):
self.update_last_checked_label() self.update_last_checked_label()
self.engines_last_update_label.setEnabled(at_least_one_downloadable) self.engines_last_update_label.setEnabled(at_least_one_downloadable)
engine_updates_layout.addWidget(self.engines_last_update_label) engine_updates_layout.addWidget(self.engines_last_update_label)
self.check_for_new_engines_button = QPushButton("Check for New Versions") self.check_for_new_engines_button = QPushButton("Check for New Versions...")
self.check_for_new_engines_button.setEnabled(at_least_one_downloadable) self.check_for_new_engines_button.setEnabled(at_least_one_downloadable)
self.check_for_new_engines_button.clicked.connect(self.check_for_new_engines) self.check_for_new_engines_button.clicked.connect(self.check_for_new_engines)
engine_updates_layout.addWidget(self.check_for_new_engines_button) engine_updates_layout.addWidget(self.check_for_new_engines_button)
@@ -239,6 +245,11 @@ class SettingsWindow(QMainWindow):
page.setLayout(layout) page.setLayout(layout)
return page return page
def change_ignore_system_installs(self, value):
settings.setValue("engines_ignore_system_installs", bool(value))
self.installed_engines_table.update_table()
def update_last_checked_label(self): def update_last_checked_label(self):
"""Retrieve the last check timestamp and return a human-friendly string.""" """Retrieve the last check timestamp and return a human-friendly string."""
last_checked_str = settings.value("engines_last_update_time", None) last_checked_str = settings.value("engines_last_update_time", None)
@@ -275,22 +286,27 @@ class SettingsWindow(QMainWindow):
os.path.join(os.path.join(os.path.expanduser(Config.upload_folder), os.path.join(os.path.join(os.path.expanduser(Config.upload_folder),
'engines'))) 'engines')))
results = [] ignore_system = settings.value("engines_ignore_system_installs", False)
messagebox_shown = False
for engine in EngineManager.downloadable_engines(): for engine in EngineManager.downloadable_engines():
if settings.value(f'engine_download-{engine.name()}', False): if settings.value(f'engine_download-{engine.name()}', False):
update_result = EngineManager.update_engine(engine) result = EngineManager.is_engine_update_available(engine, ignore_system_installs=ignore_system)
if update_result: if result:
results.append(update_result) result['name'] = engine.name()
if results:
for result in results:
msg_box = QMessageBox() msg_box = QMessageBox()
msg_box.setWindowTitle(f"{result['name']} {result['version']} Available") msg_box.setWindowTitle(f"{result['name']} ({result['version']}) Available")
msg_box.setText(f"A new version of {result['name']} is available ({result['version']}). It will begin downloading now.") msg_box.setText(f"A new version of {result['name']} is available ({result['version']}).\n\n"
f"Would you like to download it now?")
msg_box.setIcon(QMessageBox.Icon.Information) msg_box.setIcon(QMessageBox.Icon.Information)
msg_box.setStandardButtons(QMessageBox.StandardButton.Ok) msg_box.setStandardButtons(QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
msg_box.exec() msg_result = msg_box.exec()
else: messagebox_shown = True
if msg_result == QMessageBox.StandardButton.Yes:
EngineManager.download_engine(engine=engine.name(), version=result['version'], background=True,
ignore_system=ignore_system)
self.update_engine_download_status()
if not messagebox_shown:
msg_box = QMessageBox() msg_box = QMessageBox()
msg_box.setWindowTitle("No Updates Available") msg_box.setWindowTitle("No Updates Available")
msg_box.setText("All your render engines are up-to-date.") msg_box.setText("All your render engines are up-to-date.")
@@ -299,7 +315,16 @@ class SettingsWindow(QMainWindow):
msg_box.exec() msg_box.exec()
settings.setValue("engines_last_update_time", datetime.now().isoformat()) settings.setValue("engines_last_update_time", datetime.now().isoformat())
self.update_engine_download_status()
def update_engine_download_status(self):
running_tasks = [x for x in EngineManager.download_tasks if x.is_alive()]
if not running_tasks:
self.update_last_checked_label() self.update_last_checked_label()
return
self.engines_last_update_label.setText(f"Downloading {running_tasks[0].engine} ({running_tasks[0].version})...")
class EngineTableWidget(QWidget): class EngineTableWidget(QWidget):
def __init__(self): def __init__(self):
@@ -315,20 +340,26 @@ class EngineTableWidget(QWidget):
layout = QVBoxLayout(self) layout = QVBoxLayout(self)
layout.addWidget(self.table) layout.addWidget(self.table)
self.raw_server_data = None
def showEvent(self, event): def showEvent(self, event):
"""Runs when the widget is about to be shown.""" """Runs when the widget is about to be shown."""
self.update_table() self.update_table()
super().showEvent(event) # Ensure normal event processing super().showEvent(event) # Ensure normal event processing
def update_table(self): def update_table(self, use_cached=True):
raw_server_data = RenderServerProxy(socket.gethostname()).get_renderer_info() if not self.raw_server_data or not use_cached:
if not raw_server_data: self.raw_server_data = RenderServerProxy(socket.gethostname()).get_renderer_info()
if not self.raw_server_data:
return return
table_data = [] # convert the data into a flat list table_data = [] # convert the data into a flat list
for _, engine_data in raw_server_data.items(): for _, engine_data in self.raw_server_data.items():
table_data.extend(engine_data['versions']) table_data.extend(engine_data['versions'])
if settings.value("engines_ignore_system_installs", False):
table_data = [x for x in table_data if x['type'] != 'system']
self.table.clear() self.table.clear()
self.table.setRowCount(len(table_data)) self.table.setRowCount(len(table_data))
self.table.setColumnCount(4) self.table.setColumnCount(4)
@@ -364,6 +395,7 @@ class EngineTableWidget(QWidget):
return data return data
if __name__ == "__main__": if __name__ == "__main__":
app = QApplication([]) app = QApplication([])
window = SettingsWindow() window = SettingsWindow()