mirror of
https://github.com/blw1138/Zordon.git
synced 2025-12-17 08:48:13 +00:00
Prevent subprocesses from constantly opening windows on Windows (#109)
* Add subprocess.CREATE_NO_WINDOW to blender_engine.py * Convert ffmpeg_engine.py to use CREATE_NO_WINDOW * Cleanup Blender implementation * Cleanup subprocesses in base_worker.py * Cleanup subprocesses in base_engine.py * Fix main.spec for Windows (optimize=2 broke it)
This commit is contained in:
@@ -26,7 +26,7 @@ a = Analysis(
|
|||||||
runtime_hooks=[],
|
runtime_hooks=[],
|
||||||
excludes=[],
|
excludes=[],
|
||||||
noarchive=False,
|
noarchive=False,
|
||||||
optimize=2,
|
optimize=0,
|
||||||
)
|
)
|
||||||
pyz = PYZ(a.pure)
|
pyz = PYZ(a.pure)
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ from src.utilities.misc_helper import system_safe_path
|
|||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
_creationflags = subprocess.CREATE_NO_WINDOW if platform.system() == 'Windows' else 0
|
||||||
|
|
||||||
|
|
||||||
class Blender(BaseRenderEngine):
|
class Blender(BaseRenderEngine):
|
||||||
|
|
||||||
@@ -35,7 +37,8 @@ class Blender(BaseRenderEngine):
|
|||||||
try:
|
try:
|
||||||
render_path = self.renderer_path()
|
render_path = self.renderer_path()
|
||||||
if render_path:
|
if render_path:
|
||||||
ver_out = subprocess.check_output([render_path, '-v'], timeout=SUBPROCESS_TIMEOUT)
|
ver_out = subprocess.check_output([render_path, '-v'], timeout=SUBPROCESS_TIMEOUT,
|
||||||
|
creationflags=_creationflags)
|
||||||
version = ver_out.decode('utf-8').splitlines()[0].replace('Blender', '').strip()
|
version = ver_out.decode('utf-8').splitlines()[0].replace('Blender', '').strip()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f'Failed to get Blender version: {e}')
|
logger.error(f'Failed to get Blender version: {e}')
|
||||||
@@ -50,7 +53,7 @@ class Blender(BaseRenderEngine):
|
|||||||
if os.path.exists(project_path):
|
if os.path.exists(project_path):
|
||||||
try:
|
try:
|
||||||
return subprocess.run([self.renderer_path(), '-b', project_path, '--python-expr', python_expression],
|
return subprocess.run([self.renderer_path(), '-b', project_path, '--python-expr', python_expression],
|
||||||
capture_output=True, timeout=timeout)
|
capture_output=True, timeout=timeout, creationflags=_creationflags)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error running python expression in blender: {e}")
|
logger.error(f"Error running python expression in blender: {e}")
|
||||||
else:
|
else:
|
||||||
@@ -67,7 +70,7 @@ class Blender(BaseRenderEngine):
|
|||||||
command = [self.renderer_path(), '-b', '--python', script_path]
|
command = [self.renderer_path(), '-b', '--python', script_path]
|
||||||
if project_path:
|
if project_path:
|
||||||
command.insert(2, project_path)
|
command.insert(2, project_path)
|
||||||
return subprocess.run(command, capture_output=True, timeout=timeout)
|
return subprocess.run(command, capture_output=True, timeout=timeout, creationflags=_creationflags)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(f"Error running python script in blender: {e}")
|
logger.exception(f"Error running python script in blender: {e}")
|
||||||
|
|
||||||
@@ -116,7 +119,7 @@ class Blender(BaseRenderEngine):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def get_arguments(self):
|
def get_arguments(self):
|
||||||
help_text = subprocess.check_output([self.renderer_path(), '-h']).decode('utf-8')
|
help_text = subprocess.check_output([self.renderer_path(), '-h'], creationflags=_creationflags).decode('utf-8')
|
||||||
lines = help_text.splitlines()
|
lines = help_text.splitlines()
|
||||||
|
|
||||||
options = {}
|
options = {}
|
||||||
@@ -164,7 +167,7 @@ class Blender(BaseRenderEngine):
|
|||||||
|
|
||||||
def supported_render_engines(self):
|
def supported_render_engines(self):
|
||||||
engine_output = subprocess.run([self.renderer_path(), '-E', 'help'], timeout=SUBPROCESS_TIMEOUT,
|
engine_output = subprocess.run([self.renderer_path(), '-E', 'help'], timeout=SUBPROCESS_TIMEOUT,
|
||||||
capture_output=True).stdout.decode('utf-8').strip()
|
capture_output=True, creationflags=_creationflags).stdout.decode('utf-8').strip()
|
||||||
render_engines = [x.strip() for x in engine_output.split('Blender Engine Listing:')[-1].strip().splitlines()]
|
render_engines = [x.strip() for x in engine_output.split('Blender Engine Listing:')[-1].strip().splitlines()]
|
||||||
return render_engines
|
return render_engines
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import platform
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
@@ -61,8 +62,9 @@ class BaseRenderEngine(object):
|
|||||||
path = self.renderer_path()
|
path = self.renderer_path()
|
||||||
if not path:
|
if not path:
|
||||||
raise FileNotFoundError("renderer path not found")
|
raise FileNotFoundError("renderer path not found")
|
||||||
|
creationflags = subprocess.CREATE_NO_WINDOW if platform.system() == 'Windows' else 0
|
||||||
help_doc = subprocess.check_output([path, '-h'], stderr=subprocess.STDOUT,
|
help_doc = subprocess.check_output([path, '-h'], stderr=subprocess.STDOUT,
|
||||||
timeout=SUBPROCESS_TIMEOUT).decode('utf-8')
|
timeout=SUBPROCESS_TIMEOUT, creationflags=creationflags).decode('utf-8')
|
||||||
return help_doc
|
return help_doc
|
||||||
|
|
||||||
def get_project_info(self, project_path, timeout=10):
|
def get_project_info(self, project_path, timeout=10):
|
||||||
|
|||||||
@@ -326,9 +326,10 @@ class BaseRenderWorker(Base):
|
|||||||
self.__process = subprocess.Popen(subprocess_cmds, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
self.__process = subprocess.Popen(subprocess_cmds, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
||||||
universal_newlines=False, preexec_fn=os.setsid)
|
universal_newlines=False, preexec_fn=os.setsid)
|
||||||
else: # windows
|
else: # windows
|
||||||
|
creationflags = subprocess.CREATE_NEW_PROCESS_GROUP | subprocess.CREATE_NO_WINDOW
|
||||||
self.__process = subprocess.Popen(subprocess_cmds, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
self.__process = subprocess.Popen(subprocess_cmds, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
||||||
universal_newlines=False,
|
universal_newlines=False,
|
||||||
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP)
|
creationflags=creationflags)
|
||||||
|
|
||||||
# Start watchdog
|
# Start watchdog
|
||||||
self.__last_output_time = time.time()
|
self.__last_output_time = time.time()
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import re
|
|||||||
|
|
||||||
from src.engines.core.base_engine import *
|
from src.engines.core.base_engine import *
|
||||||
|
|
||||||
|
_creationflags = subprocess.CREATE_NO_WINDOW if platform.system() == 'Windows' else 0
|
||||||
|
|
||||||
|
|
||||||
class FFMPEG(BaseRenderEngine):
|
class FFMPEG(BaseRenderEngine):
|
||||||
binary_names = {'linux': 'ffmpeg', 'windows': 'ffmpeg.exe', 'macos': 'ffmpeg'}
|
binary_names = {'linux': 'ffmpeg', 'windows': 'ffmpeg.exe', 'macos': 'ffmpeg'}
|
||||||
@@ -22,8 +24,8 @@ class FFMPEG(BaseRenderEngine):
|
|||||||
return FFMPEGUI.get_options(self)
|
return FFMPEGUI.get_options(self)
|
||||||
|
|
||||||
def supported_extensions(self):
|
def supported_extensions(self):
|
||||||
help_text = (subprocess.check_output([self.renderer_path(), '-h', 'full'], stderr=subprocess.STDOUT)
|
help_text = (subprocess.check_output([self.renderer_path(), '-h', 'full'], stderr=subprocess.STDOUT,
|
||||||
.decode('utf-8'))
|
creationflags=_creationflags).decode('utf-8'))
|
||||||
found = re.findall(r'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:
|
||||||
@@ -33,8 +35,8 @@ class FFMPEG(BaseRenderEngine):
|
|||||||
def version(self):
|
def version(self):
|
||||||
version = None
|
version = None
|
||||||
try:
|
try:
|
||||||
ver_out = subprocess.check_output([self.renderer_path(), '-version'],
|
ver_out = subprocess.check_output([self.renderer_path(), '-version'], timeout=SUBPROCESS_TIMEOUT,
|
||||||
timeout=SUBPROCESS_TIMEOUT).decode('utf-8')
|
creationflags=_creationflags).decode('utf-8')
|
||||||
match = re.match(r".*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]
|
||||||
@@ -49,7 +51,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
|
||||||
]
|
]
|
||||||
output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, text=True)
|
output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, text=True,
|
||||||
|
creationflags=_creationflags)
|
||||||
video_info = json.loads(output)
|
video_info = json.loads(output)
|
||||||
|
|
||||||
# Extract the necessary information
|
# Extract the necessary information
|
||||||
@@ -80,7 +83,7 @@ class FFMPEG(BaseRenderEngine):
|
|||||||
|
|
||||||
def get_encoders(self):
|
def get_encoders(self):
|
||||||
raw_stdout = subprocess.check_output([self.renderer_path(), '-encoders'], stderr=subprocess.DEVNULL,
|
raw_stdout = subprocess.check_output([self.renderer_path(), '-encoders'], stderr=subprocess.DEVNULL,
|
||||||
timeout=SUBPROCESS_TIMEOUT).decode('utf-8')
|
timeout=SUBPROCESS_TIMEOUT, creationflags=_creationflags).decode('utf-8')
|
||||||
pattern = r'(?P<type>[VASFXBD.]{6})\s+(?P<name>\S{2,})\s+(?P<description>.*)'
|
pattern = r'(?P<type>[VASFXBD.]{6})\s+(?P<name>\S{2,})\s+(?P<description>.*)'
|
||||||
encoders = [m.groupdict() for m in re.finditer(pattern, raw_stdout)]
|
encoders = [m.groupdict() for m in re.finditer(pattern, raw_stdout)]
|
||||||
return encoders
|
return encoders
|
||||||
@@ -92,7 +95,8 @@ class FFMPEG(BaseRenderEngine):
|
|||||||
def get_all_formats(self):
|
def get_all_formats(self):
|
||||||
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,
|
||||||
|
creationflags=_creationflags).decode('utf-8')
|
||||||
pattern = r'(?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
|
||||||
@@ -104,7 +108,8 @@ class FFMPEG(BaseRenderEngine):
|
|||||||
# Extract the common extension using regex
|
# Extract the common extension using regex
|
||||||
muxer_flag = 'muxer' if 'E' in ffmpeg_format['type'] else 'demuxer'
|
muxer_flag = 'muxer' if 'E' in ffmpeg_format['type'] else 'demuxer'
|
||||||
format_detail_raw = subprocess.check_output(
|
format_detail_raw = subprocess.check_output(
|
||||||
[self.renderer_path(), '-hide_banner', '-h', f"{muxer_flag}={ffmpeg_format['id']}"]).decode('utf-8')
|
[self.renderer_path(), '-hide_banner', '-h', f"{muxer_flag}={ffmpeg_format['id']}"],
|
||||||
|
creationflags=_creationflags).decode('utf-8')
|
||||||
pattern = r"Common extensions: (\w+)"
|
pattern = r"Common extensions: (\w+)"
|
||||||
common_extensions = re.findall(pattern, format_detail_raw)
|
common_extensions = re.findall(pattern, format_detail_raw)
|
||||||
found_extensions = []
|
found_extensions = []
|
||||||
@@ -118,7 +123,7 @@ class FFMPEG(BaseRenderEngine):
|
|||||||
def get_frame_count(self, path_to_file):
|
def get_frame_count(self, path_to_file):
|
||||||
raw_stdout = subprocess.check_output([self.renderer_path(), '-i', path_to_file, '-map', '0:v:0', '-c', 'copy',
|
raw_stdout = subprocess.check_output([self.renderer_path(), '-i', path_to_file, '-map', '0:v:0', '-c', 'copy',
|
||||||
'-f', 'null', '-'], stderr=subprocess.STDOUT,
|
'-f', 'null', '-'], stderr=subprocess.STDOUT,
|
||||||
timeout=SUBPROCESS_TIMEOUT).decode('utf-8')
|
timeout=SUBPROCESS_TIMEOUT, creationflags=_creationflags).decode('utf-8')
|
||||||
match = re.findall(r'frame=\s*(\d+)', raw_stdout)
|
match = re.findall(r'frame=\s*(\d+)', raw_stdout)
|
||||||
if match:
|
if match:
|
||||||
frame_number = int(match[-1])
|
frame_number = int(match[-1])
|
||||||
@@ -126,8 +131,8 @@ class FFMPEG(BaseRenderEngine):
|
|||||||
return -1
|
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,
|
||||||
.decode('utf-8'))
|
creationflags=_creationflags).decode('utf-8'))
|
||||||
lines = help_text.splitlines()
|
lines = help_text.splitlines()
|
||||||
|
|
||||||
options = {}
|
options = {}
|
||||||
|
|||||||
Reference in New Issue
Block a user