mirror of
https://github.com/blw1138/Zordon.git
synced 2025-12-17 08:48:13 +00:00
Pylint cleanup (#74)
* Misc fixes * Misc cleanup * Add all_versions to blender_downloader.py * More cleanup * Fix issue with status not reporting engine info * Misc fixes * Misc cleanup * Add all_versions to blender_downloader.py * More cleanup * Fix issue with status not reporting engine info
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
[MASTER]
|
[MASTER]
|
||||||
max-line-length = 120
|
max-line-length = 120
|
||||||
[MESSAGES CONTROL]
|
[MESSAGES CONTROL]
|
||||||
disable = missing-docstring, invalid-name, import-error
|
disable = missing-docstring, invalid-name, import-error, logging-fstring-interpolation
|
||||||
1
setup.py
1
setup.py
@@ -18,4 +18,5 @@ setup(
|
|||||||
data_files=DATA_FILES,
|
data_files=DATA_FILES,
|
||||||
options={'py2app': OPTIONS},
|
options={'py2app': OPTIONS},
|
||||||
setup_requires=['py2app'],
|
setup_requires=['py2app'],
|
||||||
|
name='Zordon'
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -60,8 +60,7 @@ def jobs_json():
|
|||||||
job_cache_token = num_to_alphanumeric(job_cache_int)
|
job_cache_token = num_to_alphanumeric(job_cache_int)
|
||||||
if hash_token and hash_token == job_cache_token:
|
if hash_token and hash_token == job_cache_token:
|
||||||
return [], 204 # no need to update
|
return [], 204 # no need to update
|
||||||
else:
|
return {'jobs': all_jobs, 'token': job_cache_token}
|
||||||
return {'jobs': all_jobs, 'token': job_cache_token}
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(f"Exception fetching all_jobs_cached: {e}")
|
logger.exception(f"Exception fetching all_jobs_cached: {e}")
|
||||||
return [], 500
|
return [], 500
|
||||||
@@ -343,10 +342,10 @@ def clear_history():
|
|||||||
def status():
|
def status():
|
||||||
renderer_data = {}
|
renderer_data = {}
|
||||||
for render_class in EngineManager.supported_engines():
|
for render_class in EngineManager.supported_engines():
|
||||||
if EngineManager.all_versions_for_engine(render_class.name): # only return renderers installed on host
|
if EngineManager.all_versions_for_engine(render_class.name()): # only return renderers installed on host
|
||||||
renderer_data[render_class.engine.name()] = \
|
renderer_data[render_class.name()] = \
|
||||||
{'versions': EngineManager.all_versions_for_engine(render_class.engine.name()),
|
{'versions': EngineManager.all_versions_for_engine(render_class.name()),
|
||||||
'is_available': RenderQueue.is_available_for_job(render_class.engine.name())
|
'is_available': RenderQueue.is_available_for_job(render_class.name())
|
||||||
}
|
}
|
||||||
|
|
||||||
# Get system info
|
# Get system info
|
||||||
@@ -376,8 +375,8 @@ def renderer_info():
|
|||||||
if installed_versions:
|
if installed_versions:
|
||||||
# fixme: using system versions only because downloaded versions may have permissions issues
|
# fixme: using system versions only because downloaded versions may have permissions issues
|
||||||
system_installed_versions = [x for x in installed_versions if x['type'] == 'system']
|
system_installed_versions = [x for x in installed_versions if x['type'] == 'system']
|
||||||
install_path = system_installed_versions[0]['path'] if system_installed_versions else (
|
install_path = system_installed_versions[0]['path'] if system_installed_versions \
|
||||||
installed_versions)[0]['path']
|
else (installed_versions)[0]['path']
|
||||||
renderer_data[engine.name()] = {'is_available': RenderQueue.is_available_for_job(engine.name()),
|
renderer_data[engine.name()] = {'is_available': RenderQueue.is_available_for_job(engine.name()),
|
||||||
'versions': installed_versions,
|
'versions': installed_versions,
|
||||||
'supported_extensions': engine.supported_extensions(),
|
'supported_extensions': engine.supported_extensions(),
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class ServerProxyManager:
|
|||||||
pub.subscribe(cls.__zeroconf_state_change, 'zeroconf_state_change')
|
pub.subscribe(cls.__zeroconf_state_change, 'zeroconf_state_change')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def __zeroconf_state_change(cls, hostname, state_change, info):
|
def __zeroconf_state_change(cls, hostname, state_change):
|
||||||
if state_change == ServiceStateChange.Added or state_change == ServiceStateChange.Updated:
|
if state_change == ServiceStateChange.Added or state_change == ServiceStateChange.Updated:
|
||||||
cls.get_proxy_for_hostname(hostname)
|
cls.get_proxy_for_hostname(hostname)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
import threading
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
@@ -15,6 +16,7 @@ supported_formats = ['.zip', '.tar.xz', '.dmg']
|
|||||||
|
|
||||||
|
|
||||||
class BlenderDownloader(EngineDownloader):
|
class BlenderDownloader(EngineDownloader):
|
||||||
|
|
||||||
engine = Blender
|
engine = Blender
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -79,6 +81,31 @@ class BlenderDownloader(EngineDownloader):
|
|||||||
|
|
||||||
return lts_versions
|
return lts_versions
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def all_versions(cls, system_os=None, cpu=None):
|
||||||
|
majors = cls.__get_major_versions()
|
||||||
|
all_versions = []
|
||||||
|
threads = []
|
||||||
|
results = [[] for _ in majors]
|
||||||
|
|
||||||
|
def thread_function(major_version, index, system_os, cpu):
|
||||||
|
results[index] = cls.__get_minor_versions(major_version, system_os, cpu)
|
||||||
|
|
||||||
|
for i, m in enumerate(majors):
|
||||||
|
thread = threading.Thread(target=thread_function, args=(m, i, system_os, cpu))
|
||||||
|
threads.append(thread)
|
||||||
|
thread.start()
|
||||||
|
|
||||||
|
# Wait for all threads to complete
|
||||||
|
for thread in threads:
|
||||||
|
thread.join()
|
||||||
|
|
||||||
|
# Extend all_versions with the results from each thread
|
||||||
|
for result in results:
|
||||||
|
all_versions.extend(result)
|
||||||
|
|
||||||
|
return all_versions
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def find_most_recent_version(cls, system_os=None, cpu=None, lts_only=False):
|
def find_most_recent_version(cls, system_os=None, cpu=None, lts_only=False):
|
||||||
try:
|
try:
|
||||||
@@ -108,8 +135,6 @@ class BlenderDownloader(EngineDownloader):
|
|||||||
major_version = '.'.join(version.split('.')[:2])
|
major_version = '.'.join(version.split('.')[:2])
|
||||||
minor_versions = [x for x in cls.__get_minor_versions(major_version, system_os, cpu) if
|
minor_versions = [x for x in cls.__get_minor_versions(major_version, system_os, cpu) if
|
||||||
x['version'] == version]
|
x['version'] == version]
|
||||||
# we get the URL instead of calculating it ourselves. May change this
|
|
||||||
|
|
||||||
cls.download_and_extract_app(remote_url=minor_versions[0]['url'], download_location=download_location,
|
cls.download_and_extract_app(remote_url=minor_versions[0]['url'], download_location=download_location,
|
||||||
timeout=timeout)
|
timeout=timeout)
|
||||||
except IndexError:
|
except IndexError:
|
||||||
|
|||||||
@@ -12,8 +12,7 @@ class BlenderRenderWorker(BaseRenderWorker):
|
|||||||
engine = Blender
|
engine = Blender
|
||||||
|
|
||||||
def __init__(self, input_path, output_path, engine_path, args=None, parent=None, name=None):
|
def __init__(self, input_path, output_path, engine_path, args=None, parent=None, name=None):
|
||||||
super(BlenderRenderWorker, self).__init__(input_path=input_path, output_path=output_path,
|
super(BlenderRenderWorker, self).__init__(input_path=input_path, output_path=output_path, engine_path=engine_path, args=args, parent=parent, name=name)
|
||||||
engine_path=engine_path, args=args, parent=parent, name=name)
|
|
||||||
|
|
||||||
# Args
|
# Args
|
||||||
self.blender_engine = self.args.get('engine', 'BLENDER_EEVEE').upper()
|
self.blender_engine = self.args.get('engine', 'BLENDER_EEVEE').upper()
|
||||||
|
|||||||
@@ -157,14 +157,14 @@ class BaseRenderWorker(Base):
|
|||||||
|
|
||||||
if not os.path.exists(self.input_path):
|
if not os.path.exists(self.input_path):
|
||||||
self.status = RenderStatus.ERROR
|
self.status = RenderStatus.ERROR
|
||||||
msg = 'Cannot find input path: {}'.format(self.input_path)
|
msg = f'Cannot find input path: {self.input_path}'
|
||||||
logger.error(msg)
|
logger.error(msg)
|
||||||
self.errors.append(msg)
|
self.errors.append(msg)
|
||||||
return
|
return
|
||||||
|
|
||||||
if not os.path.exists(self.renderer_path):
|
if not os.path.exists(self.renderer_path):
|
||||||
self.status = RenderStatus.ERROR
|
self.status = RenderStatus.ERROR
|
||||||
msg = 'Cannot find render engine path for {}'.format(self.engine.name())
|
msg = f'Cannot find render engine path for {self.engine.name()}'
|
||||||
logger.error(msg)
|
logger.error(msg)
|
||||||
self.errors.append(msg)
|
self.errors.append(msg)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -239,7 +239,7 @@ class EngineManager:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def engine_for_project_path(cls, path):
|
def engine_for_project_path(cls, path):
|
||||||
name, extension = os.path.splitext(path)
|
_, extension = os.path.splitext(path)
|
||||||
extension = extension.lower().strip('.')
|
extension = extension.lower().strip('.')
|
||||||
for engine in cls.supported_engines():
|
for engine in cls.supported_engines():
|
||||||
if extension in engine.supported_extensions():
|
if extension in engine.supported_extensions():
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ class FFMPEGDownloader(EngineDownloader):
|
|||||||
return releases
|
return releases
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def __all_versions(cls, system_os=None, cpu=None):
|
def all_versions(cls, system_os=None, cpu=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()
|
||||||
versions_per_os = {'linux': cls.__get_linux_versions, 'macos': cls.__get_macos_versions,
|
versions_per_os = {'linux': cls.__get_linux_versions, 'macos': cls.__get_macos_versions,
|
||||||
@@ -131,14 +131,14 @@ class FFMPEGDownloader(EngineDownloader):
|
|||||||
try:
|
try:
|
||||||
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()
|
||||||
return cls.__all_versions(system_os, cpu)[0]
|
return cls.all_versions(system_os, cpu)[0]
|
||||||
except (IndexError, requests.exceptions.RequestException):
|
except (IndexError, requests.exceptions.RequestException):
|
||||||
logger.error(f"Cannot get most recent version of ffmpeg")
|
logger.error(f"Cannot get most recent version of ffmpeg")
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def version_is_available_to_download(cls, version, system_os=None, cpu=None):
|
def version_is_available_to_download(cls, version, system_os=None, cpu=None):
|
||||||
for ver in cls.__all_versions(system_os, cpu):
|
for ver in cls.all_versions(system_os, cpu):
|
||||||
if ver['version'] == version:
|
if ver['version'] == version:
|
||||||
return ver
|
return ver
|
||||||
return None
|
return None
|
||||||
@@ -149,7 +149,7 @@ class FFMPEGDownloader(EngineDownloader):
|
|||||||
cpu = cpu or current_system_cpu()
|
cpu = cpu or current_system_cpu()
|
||||||
|
|
||||||
# Verify requested version is available
|
# Verify requested version is available
|
||||||
found_version = [item for item in cls.__all_versions(system_os, cpu) if item['version'] == version]
|
found_version = [item for item in cls.all_versions(system_os, cpu) if item['version'] == version]
|
||||||
if not found_version:
|
if not found_version:
|
||||||
logger.error(f"Cannot find FFMPEG version {version} for {system_os} and {cpu}")
|
logger.error(f"Cannot find FFMPEG version {version} for {system_os} and {cpu}")
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class FFMPEG(BaseRenderEngine):
|
|||||||
def supported_extensions(cls):
|
def supported_extensions(cls):
|
||||||
help_text = (subprocess.check_output([cls().renderer_path(), '-h', 'full'], stderr=subprocess.STDOUT)
|
help_text = (subprocess.check_output([cls().renderer_path(), '-h', 'full'], stderr=subprocess.STDOUT)
|
||||||
.decode('utf-8'))
|
.decode('utf-8'))
|
||||||
found = re.findall('extensions that .* is allowed to access \(default "(.*)"', help_text)
|
found = re.findall(r'extensions that .* is allowed to access \(default "(.*)"', help_text)
|
||||||
found_extensions = set()
|
found_extensions = set()
|
||||||
for match in found:
|
for match in found:
|
||||||
found_extensions.update(match.split(','))
|
found_extensions.update(match.split(','))
|
||||||
@@ -36,7 +36,7 @@ class FFMPEG(BaseRenderEngine):
|
|||||||
try:
|
try:
|
||||||
ver_out = subprocess.check_output([self.renderer_path(), '-version'],
|
ver_out = subprocess.check_output([self.renderer_path(), '-version'],
|
||||||
timeout=SUBPROCESS_TIMEOUT).decode('utf-8')
|
timeout=SUBPROCESS_TIMEOUT).decode('utf-8')
|
||||||
match = re.match(".*version\s*([\w.*]+)\W*", ver_out)
|
match = re.match(r".*version\s*([\w.*]+)\W*", ver_out)
|
||||||
if match:
|
if match:
|
||||||
version = match.groups()[0]
|
version = match.groups()[0]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -50,8 +50,8 @@ class FFMPEG(BaseRenderEngine):
|
|||||||
'ffprobe', '-v', 'quiet', '-print_format', 'json',
|
'ffprobe', '-v', 'quiet', '-print_format', 'json',
|
||||||
'-show_streams', '-select_streams', 'v', project_path
|
'-show_streams', '-select_streams', 'v', project_path
|
||||||
]
|
]
|
||||||
result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, text=True)
|
||||||
video_info = json.loads(result.stdout)
|
video_info = json.loads(output)
|
||||||
|
|
||||||
# Extract the necessary information
|
# Extract the necessary information
|
||||||
video_stream = video_info['streams'][0]
|
video_stream = video_info['streams'][0]
|
||||||
@@ -94,7 +94,7 @@ class FFMPEG(BaseRenderEngine):
|
|||||||
try:
|
try:
|
||||||
formats_raw = subprocess.check_output([self.renderer_path(), '-formats'], stderr=subprocess.DEVNULL,
|
formats_raw = subprocess.check_output([self.renderer_path(), '-formats'], stderr=subprocess.DEVNULL,
|
||||||
timeout=SUBPROCESS_TIMEOUT).decode('utf-8')
|
timeout=SUBPROCESS_TIMEOUT).decode('utf-8')
|
||||||
pattern = '(?P<type>[DE]{1,2})\s+(?P<id>\S{2,})\s+(?P<name>.*)'
|
pattern = r'(?P<type>[DE]{1,2})\s+(?P<id>\S{2,})\s+(?P<name>.*)'
|
||||||
all_formats = [m.groupdict() for m in re.finditer(pattern, formats_raw)]
|
all_formats = [m.groupdict() for m in re.finditer(pattern, formats_raw)]
|
||||||
return all_formats
|
return all_formats
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -124,6 +124,7 @@ class FFMPEG(BaseRenderEngine):
|
|||||||
if match:
|
if match:
|
||||||
frame_number = int(match[-1])
|
frame_number = int(match[-1])
|
||||||
return frame_number
|
return frame_number
|
||||||
|
return -1
|
||||||
|
|
||||||
def get_arguments(self):
|
def get_arguments(self):
|
||||||
help_text = (subprocess.check_output([self.renderer_path(), '-h', 'long'], stderr=subprocess.STDOUT)
|
help_text = (subprocess.check_output([self.renderer_path(), '-h', 'long'], stderr=subprocess.STDOUT)
|
||||||
@@ -154,4 +155,4 @@ class FFMPEG(BaseRenderEngine):
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
print(FFMPEG().supported_extensions())
|
print(FFMPEG().get_all_formats())
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ from PyQt6.QtWidgets import (
|
|||||||
|
|
||||||
from src.api.server_proxy import RenderServerProxy
|
from src.api.server_proxy import RenderServerProxy
|
||||||
from src.engines.engine_manager import EngineManager
|
from src.engines.engine_manager import EngineManager
|
||||||
from src.utilities.misc_helper import is_localhost
|
from src.utilities.misc_helper import is_localhost, launch_url
|
||||||
|
|
||||||
|
|
||||||
class EngineBrowserWindow(QMainWindow):
|
class EngineBrowserWindow(QMainWindow):
|
||||||
@@ -98,7 +98,7 @@ class EngineBrowserWindow(QMainWindow):
|
|||||||
return
|
return
|
||||||
|
|
||||||
table_data = [] # convert the data into a flat list
|
table_data = [] # convert the data into a flat list
|
||||||
for engine_name, engine_data in raw_server_data.items():
|
for _, engine_data in raw_server_data.items():
|
||||||
table_data.extend(engine_data['versions'])
|
table_data.extend(engine_data['versions'])
|
||||||
self.engine_data = table_data
|
self.engine_data = table_data
|
||||||
|
|
||||||
@@ -144,15 +144,7 @@ class EngineBrowserWindow(QMainWindow):
|
|||||||
|
|
||||||
def launch_button_click(self):
|
def launch_button_click(self):
|
||||||
engine_info = self.engine_data[self.table_widget.currentRow()]
|
engine_info = self.engine_data[self.table_widget.currentRow()]
|
||||||
path = engine_info['path']
|
launch_url(engine_info['path'])
|
||||||
if sys.platform.startswith('darwin'):
|
|
||||||
subprocess.run(['open', path])
|
|
||||||
elif sys.platform.startswith('win32'):
|
|
||||||
os.startfile(path)
|
|
||||||
elif sys.platform.startswith('linux'):
|
|
||||||
subprocess.run(['xdg-open', path])
|
|
||||||
else:
|
|
||||||
raise OSError("Unsupported operating system")
|
|
||||||
|
|
||||||
def install_button_click(self):
|
def install_button_click(self):
|
||||||
self.update_download_status()
|
self.update_download_status()
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ from .widgets.proportional_image_label import ProportionalImageLabel
|
|||||||
from .widgets.statusbar import StatusBar
|
from .widgets.statusbar import StatusBar
|
||||||
from .widgets.toolbar import ToolBar
|
from .widgets.toolbar import ToolBar
|
||||||
from src.api.serverproxy_manager import ServerProxyManager
|
from src.api.serverproxy_manager import ServerProxyManager
|
||||||
|
from src.utilities.misc_helper import launch_url
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
|
|
||||||
@@ -72,7 +73,7 @@ class MainWindow(QMainWindow):
|
|||||||
# Create a QLabel widget to display the image
|
# Create a QLabel widget to display the image
|
||||||
self.image_label = ProportionalImageLabel()
|
self.image_label = ProportionalImageLabel()
|
||||||
self.image_label.setMaximumSize(700, 500)
|
self.image_label.setMaximumSize(700, 500)
|
||||||
self.image_label.setFixedHeight(500)
|
self.image_label.setFixedHeight(300)
|
||||||
self.image_label.setAlignment(Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignHCenter)
|
self.image_label.setAlignment(Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignHCenter)
|
||||||
self.load_image_path(os.path.join(resources_dir(), 'Rectangle.png'))
|
self.load_image_path(os.path.join(resources_dir(), 'Rectangle.png'))
|
||||||
|
|
||||||
@@ -305,7 +306,7 @@ class MainWindow(QMainWindow):
|
|||||||
except ConnectionError as e:
|
except ConnectionError as e:
|
||||||
logger.error(f"Connection error fetching image: {e}")
|
logger.error(f"Connection error fetching image: {e}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(f"Error fetching image: {e}")
|
logger.error(f"Error fetching image: {e}")
|
||||||
|
|
||||||
job_id = self.selected_job_ids()[0] if self.selected_job_ids() else None
|
job_id = self.selected_job_ids()[0] if self.selected_job_ids() else None
|
||||||
local_server = is_localhost(self.current_hostname)
|
local_server = is_localhost(self.current_hostname)
|
||||||
@@ -563,15 +564,7 @@ class MainWindow(QMainWindow):
|
|||||||
for job_id in job_ids:
|
for job_id in job_ids:
|
||||||
job_info = self.current_server_proxy.get_job_info(job_id)
|
job_info = self.current_server_proxy.get_job_info(job_id)
|
||||||
path = os.path.dirname(job_info['output_path'])
|
path = os.path.dirname(job_info['output_path'])
|
||||||
|
launch_url(path)
|
||||||
if sys.platform.startswith('darwin'):
|
|
||||||
subprocess.run(['open', path])
|
|
||||||
elif sys.platform.startswith('win32'):
|
|
||||||
os.startfile(path)
|
|
||||||
elif sys.platform.startswith('linux'):
|
|
||||||
subprocess.run(['xdg-open', path])
|
|
||||||
else:
|
|
||||||
raise OSError("Unsupported operating system")
|
|
||||||
|
|
||||||
def new_job(self) -> None:
|
def new_job(self) -> None:
|
||||||
|
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
''' app/ui/widgets/dialog.py '''
|
|
||||||
@@ -11,14 +11,27 @@ logger = logging.getLogger()
|
|||||||
|
|
||||||
|
|
||||||
def launch_url(url):
|
def launch_url(url):
|
||||||
if subprocess.run(['which', 'xdg-open'], capture_output=True).returncode == 0:
|
logger = logging.getLogger(__name__)
|
||||||
subprocess.run(['xdg-open', url]) # linux
|
|
||||||
elif subprocess.run(['which', 'open'], capture_output=True).returncode == 0:
|
if shutil.which('xdg-open'):
|
||||||
subprocess.run(['open', url]) # macos
|
opener = 'xdg-open'
|
||||||
elif subprocess.run(['which', 'start'], capture_output=True).returncode == 0:
|
elif shutil.which('open'):
|
||||||
subprocess.run(['start', url]) # windows - need to validate this works
|
opener = 'open'
|
||||||
|
elif shutil.which('cmd'):
|
||||||
|
opener = 'start'
|
||||||
else:
|
else:
|
||||||
logger.error(f"No valid launchers found to launch url: {url}")
|
error_message = f"No valid launchers found to launch URL: {url}"
|
||||||
|
logger.error(error_message)
|
||||||
|
raise OSError(error_message)
|
||||||
|
|
||||||
|
try:
|
||||||
|
if opener == 'start':
|
||||||
|
# For Windows, use 'cmd /c start'
|
||||||
|
subprocess.run(['cmd', '/c', 'start', url], shell=False)
|
||||||
|
else:
|
||||||
|
subprocess.run([opener, url])
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to launch URL: {url}. Error: {e}")
|
||||||
|
|
||||||
|
|
||||||
def file_exists_in_mounts(filepath):
|
def file_exists_in_mounts(filepath):
|
||||||
|
|||||||
Reference in New Issue
Block a user