mirror of
https://github.com/blw1138/Zordon.git
synced 2025-12-17 16:58:12 +00:00
RenderWorker cleanup and add camera methods to BlenderRenderWorker
This commit is contained in:
@@ -240,7 +240,8 @@ def renderer_info():
|
|||||||
renderer_data = {}
|
renderer_data = {}
|
||||||
for r in RenderWorkerFactory.supported_renderers():
|
for r in RenderWorkerFactory.supported_renderers():
|
||||||
renderer_class = RenderWorkerFactory.class_for_name(r)
|
renderer_class = RenderWorkerFactory.class_for_name(r)
|
||||||
renderer_data[r] = {'version': renderer_class.version(),
|
renderer_data[r] = {'available': renderer_class.renderer_path() is not None,
|
||||||
|
'version': renderer_class.version(),
|
||||||
'supported_extensions': renderer_class.supported_extensions,
|
'supported_extensions': renderer_class.supported_extensions,
|
||||||
'supported_export_formats': renderer_class.supported_export_formats}
|
'supported_export_formats': renderer_class.supported_export_formats}
|
||||||
return renderer_data
|
return renderer_data
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#! /usr/bin/python
|
#!/usr/bin/env python3
|
||||||
import glob
|
import glob
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
@@ -19,18 +19,6 @@ def aerender_path():
|
|||||||
|
|
||||||
class AERenderWorker(BaseRenderWorker):
|
class AERenderWorker(BaseRenderWorker):
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def version(cls):
|
|
||||||
version = None
|
|
||||||
try:
|
|
||||||
x = subprocess.Popen([aerender_path(), '-version'], stdout=subprocess.PIPE)
|
|
||||||
x.wait()
|
|
||||||
ver_out = str(x.stdout.read().strip())
|
|
||||||
version = ver_out.split(" ")[-1].strip()
|
|
||||||
except Exception as e:
|
|
||||||
logging.error('failed getting version: {}'.format(e))
|
|
||||||
return version
|
|
||||||
|
|
||||||
renderer = 'After Effects'
|
renderer = 'After Effects'
|
||||||
render_engine = 'aerender'
|
render_engine = 'aerender'
|
||||||
supported_extensions = ['.aep']
|
supported_extensions = ['.aep']
|
||||||
@@ -47,6 +35,18 @@ class AERenderWorker(BaseRenderWorker):
|
|||||||
self.progress_history = []
|
self.progress_history = []
|
||||||
self.attributes = {}
|
self.attributes = {}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def version(cls):
|
||||||
|
version = None
|
||||||
|
try:
|
||||||
|
render_path = cls.renderer_path()
|
||||||
|
if render_path:
|
||||||
|
ver_out = subprocess.check_output([render_path, '-version'])
|
||||||
|
version = ver_out.decode('utf-8').split(" ")[-1].strip()
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f'Failed to get {cls.renderer} version: {e}')
|
||||||
|
return version
|
||||||
|
|
||||||
def _generate_subprocess(self):
|
def _generate_subprocess(self):
|
||||||
|
|
||||||
if os.path.exists('nexrender-cli-macos'):
|
if os.path.exists('nexrender-cli-macos'):
|
||||||
|
|||||||
@@ -1,24 +1,10 @@
|
|||||||
#! /usr/bin/python
|
#!/usr/bin/env python3
|
||||||
import re
|
import re
|
||||||
import time
|
|
||||||
|
|
||||||
from utilities.render_worker import *
|
from utilities.render_worker import *
|
||||||
|
|
||||||
SUPPORTED_FORMATS = ['TGA', 'RAWTGA', 'JPEG', 'IRIS', 'IRIZ', 'AVIRAW', 'AVIJPEG', 'PNG', 'BMP', 'HDR', 'TIFF',
|
|
||||||
'OPEN_EXR', 'OPEN_EXR_MULTILAYER', 'MPEG', 'CINEON', 'DPX', 'DDS', 'JP2']
|
|
||||||
|
|
||||||
|
|
||||||
class BlenderRenderWorker(BaseRenderWorker):
|
class BlenderRenderWorker(BaseRenderWorker):
|
||||||
|
|
||||||
def version(self):
|
|
||||||
version = None
|
|
||||||
try:
|
|
||||||
ver_out = subprocess.check_output([self.renderer_path(), '-v']).decode('utf-8')
|
|
||||||
version = ver_out.splitlines()[0].replace('Blender', '').strip()
|
|
||||||
except Exception as e:
|
|
||||||
logger.error("Failed to get Blender version: {}".format(e))
|
|
||||||
return version
|
|
||||||
|
|
||||||
renderer = 'Blender'
|
renderer = 'Blender'
|
||||||
render_engine = 'blender'
|
render_engine = 'blender'
|
||||||
supported_extensions = ['.blend']
|
supported_extensions = ['.blend']
|
||||||
@@ -31,7 +17,8 @@ class BlenderRenderWorker(BaseRenderWorker):
|
|||||||
ignore_extensions=False, args=args)
|
ignore_extensions=False, args=args)
|
||||||
|
|
||||||
self.engine = self.args.get('engine', 'BLENDER_EEVEE').upper()
|
self.engine = self.args.get('engine', 'BLENDER_EEVEE').upper()
|
||||||
self.export_format = self.args.get('export_format', 'JPEG')
|
self.export_format = self.args.get('export_format', None) or 'JPEG'
|
||||||
|
self.camera = self.args.get('camera', None)
|
||||||
self.frame = 0
|
self.frame = 0
|
||||||
|
|
||||||
self.render_all_frames = self.args.get('render_all_frames', False)
|
self.render_all_frames = self.args.get('render_all_frames', False)
|
||||||
@@ -43,24 +30,34 @@ class BlenderRenderWorker(BaseRenderWorker):
|
|||||||
self.time_remaining = None
|
self.time_remaining = None
|
||||||
self.frame_percent_complete = 0.0
|
self.frame_percent_complete = 0.0
|
||||||
|
|
||||||
def generate_preview(self, blend_file):
|
@classmethod
|
||||||
pass
|
def version(cls):
|
||||||
|
version = None
|
||||||
|
try:
|
||||||
|
render_path = cls.renderer_path()
|
||||||
|
if render_path:
|
||||||
|
ver_out = subprocess.check_output([render_path, '-v'])
|
||||||
|
version = ver_out.decode('utf-8').splitlines()[0].replace('Blender', '').strip()
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f'Failed to get {cls.renderer} version: {e}')
|
||||||
|
return version
|
||||||
|
|
||||||
def _generate_subprocess(self):
|
def _generate_subprocess(self):
|
||||||
|
|
||||||
if self.export_format not in self.supported_export_formats:
|
cmd = [self.renderer_path(), '-b', self.input]
|
||||||
raise ValueError("Unsupported format for Blender: {}".format(self.export_format))
|
|
||||||
|
if self.camera:
|
||||||
|
cmd.extend(['--python-expr', f"import bpy;bpy.context.scene.camera = bpy.data.objects['{self.camera}'];"])
|
||||||
|
|
||||||
|
cmd.extend(['-E', self.engine, '-o', self.output, '-F', self.export_format])
|
||||||
|
|
||||||
|
# all frames or single
|
||||||
|
cmd.extend(['-a'] if self.render_all_frames else ['-f', str(self.frame)])
|
||||||
|
|
||||||
if self.render_all_frames:
|
|
||||||
cmd = [self.renderer_path(), '-b', self.input, '-E', self.engine, '-o', self.output,
|
|
||||||
'-F', self.export_format, '-a']
|
|
||||||
else:
|
|
||||||
cmd = [self.renderer_path(), '-b', self.input, '-E', self.engine, '-o', self.output,
|
|
||||||
'-F', self.export_format, '-f', str(self.frame)]
|
|
||||||
return cmd
|
return cmd
|
||||||
|
|
||||||
def _parse_stdout(self, line):
|
def _parse_stdout(self, line):
|
||||||
import re
|
|
||||||
pattern = re.compile(
|
pattern = re.compile(
|
||||||
r'Fra:(?P<frame>\d*).*Mem:(?P<memory>\S+).*Time:(?P<time>\S+)(?:.*Remaining:)?(?P<remaining>\S*)')
|
r'Fra:(?P<frame>\d*).*Mem:(?P<memory>\S+).*Time:(?P<time>\S+)(?:.*Remaining:)?(?P<remaining>\S*)')
|
||||||
found = pattern.search(line)
|
found = pattern.search(line)
|
||||||
@@ -120,7 +117,8 @@ class BlenderRenderWorker(BaseRenderWorker):
|
|||||||
def run_python_expression_in_blend(path, python_expression):
|
def run_python_expression_in_blend(path, python_expression):
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
try:
|
try:
|
||||||
return subprocess.run(['blender', '-b', path, '--python-expr', python_expression], capture_output=True)
|
return subprocess.run([BlenderRenderWorker.renderer_path(), '-b', path, '--python-expr', python_expression],
|
||||||
|
capture_output=True)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"Error running python expression in blender: {e}")
|
logger.warning(f"Error running python expression in blender: {e}")
|
||||||
pass
|
pass
|
||||||
@@ -155,6 +153,24 @@ def pack_blender_files(path):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_cameras_in_scene(path):
|
||||||
|
cam_script = "import bpy;print('CAM:' + str([x.name + '@' + str(x.lens) for x in bpy.data.cameras]))"
|
||||||
|
|
||||||
|
try:
|
||||||
|
results = run_python_expression_in_blend(path, cam_script)
|
||||||
|
|
||||||
|
result_text = results.stdout.decode()
|
||||||
|
p = re.compile('CAM:\[(.*)\]')
|
||||||
|
match = p.search(result_text)
|
||||||
|
if match:
|
||||||
|
cameras = match.group(1).replace("'", "").split(",")
|
||||||
|
cameras = [{'name': x.split('@')[0].strip(), 'lens': x.split('@')[-1].strip()} for x in cameras]
|
||||||
|
return cameras
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f'Error getting file details for .blend file: {e}')
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
||||||
# x = pack_blender_files('/Users/brett/Blender Files/temple_animatic.blend')
|
# x = pack_blender_files('/Users/brett/Blender Files/temple_animatic.blend')
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import logging
|
#!/usr/bin/env python3
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
import ffmpeg
|
import ffmpeg
|
||||||
@@ -7,17 +7,6 @@ from utilities.render_worker import *
|
|||||||
|
|
||||||
class FFMPEGRenderWorker(BaseRenderWorker):
|
class FFMPEGRenderWorker(BaseRenderWorker):
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def version(cls):
|
|
||||||
version = None
|
|
||||||
try:
|
|
||||||
ver_out = subprocess.check_output([cls.renderer_path(), '-version']).decode('utf-8')
|
|
||||||
match = re.match(".*version\s*(\S+)\s*Copyright", ver_out)
|
|
||||||
version = match.groups()[0]
|
|
||||||
except Exception as e:
|
|
||||||
logger.error("Failed to get FFMPEG version: {}".format(e))
|
|
||||||
return version
|
|
||||||
|
|
||||||
renderer = 'ffmpeg'
|
renderer = 'ffmpeg'
|
||||||
render_engine = 'ffmpeg'
|
render_engine = 'ffmpeg'
|
||||||
|
|
||||||
@@ -38,6 +27,18 @@ class FFMPEGRenderWorker(BaseRenderWorker):
|
|||||||
# Stats
|
# Stats
|
||||||
self.current_frame = -1
|
self.current_frame = -1
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def version(cls):
|
||||||
|
version = None
|
||||||
|
try:
|
||||||
|
ver_out = subprocess.check_output([cls.renderer_path(), '-version']).decode('utf-8')
|
||||||
|
match = re.match(".*version\s*(\S+)\s*Copyright", ver_out)
|
||||||
|
if match:
|
||||||
|
version = match.groups()[0]
|
||||||
|
except Exception as e:
|
||||||
|
logger.error("Failed to get FFMPEG version: {}".format(e))
|
||||||
|
return version
|
||||||
|
|
||||||
def _generate_subprocess(self):
|
def _generate_subprocess(self):
|
||||||
|
|
||||||
cmd = [self.renderer_path(), '-y', '-stats', '-i', self.input]
|
cmd = [self.renderer_path(), '-y', '-stats', '-i', self.input]
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
import io
|
import io
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
@@ -35,10 +36,6 @@ class BaseRenderWorker(object):
|
|||||||
install_paths = []
|
install_paths = []
|
||||||
supported_export_formats = []
|
supported_export_formats = []
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def version(cls):
|
|
||||||
raise NotImplementedError("Unknown version")
|
|
||||||
|
|
||||||
def __init__(self, input_path, output_path, args=None, ignore_extensions=True):
|
def __init__(self, input_path, output_path, args=None, ignore_extensions=True):
|
||||||
|
|
||||||
if not ignore_extensions:
|
if not ignore_extensions:
|
||||||
@@ -52,7 +49,6 @@ class BaseRenderWorker(object):
|
|||||||
self.output = output_path
|
self.output = output_path
|
||||||
self.args = args or {}
|
self.args = args or {}
|
||||||
self.date_created = datetime.now()
|
self.date_created = datetime.now()
|
||||||
self.attributes = {}
|
|
||||||
self.renderer_version = self.version()
|
self.renderer_version = self.version()
|
||||||
|
|
||||||
# Ranges
|
# Ranges
|
||||||
@@ -63,7 +59,6 @@ class BaseRenderWorker(object):
|
|||||||
self.log_path = None
|
self.log_path = None
|
||||||
self.start_time = None
|
self.start_time = None
|
||||||
self.end_time = None
|
self.end_time = None
|
||||||
self.last_error = None
|
|
||||||
|
|
||||||
# History
|
# History
|
||||||
self.status = RenderStatus.NOT_STARTED
|
self.status = RenderStatus.NOT_STARTED
|
||||||
@@ -79,17 +74,21 @@ class BaseRenderWorker(object):
|
|||||||
self.is_finished = False
|
self.is_finished = False
|
||||||
self.last_output = None
|
self.last_output = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def version(cls):
|
||||||
|
raise NotImplementedError("Unknown version")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def renderer_path(cls):
|
def renderer_path(cls):
|
||||||
path = None
|
path = None
|
||||||
try:
|
try:
|
||||||
path = subprocess.check_output(['which', cls.render_engine]).decode('utf-8').strip()
|
path = subprocess.check_output(['which', cls.render_engine]).decode('utf-8').strip()
|
||||||
except Exception as e:
|
except subprocess.CalledProcessError:
|
||||||
for p in cls.install_paths:
|
for p in cls.install_paths:
|
||||||
if os.path.exists(p):
|
if os.path.exists(p):
|
||||||
path = p
|
path = p
|
||||||
# if not path:
|
except Exception as e:
|
||||||
# logger.error("Failed to get path to {}: {}".format(self.renderer, e))
|
logging.exception(e)
|
||||||
return path
|
return path
|
||||||
|
|
||||||
def _generate_subprocess(self):
|
def _generate_subprocess(self):
|
||||||
@@ -215,6 +214,15 @@ class BaseRenderWorker(object):
|
|||||||
|
|
||||||
class RenderWorkerFactory:
|
class RenderWorkerFactory:
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def supported_classes():
|
||||||
|
# to add support for any additional RenderWorker classes, import their classes and add to list here
|
||||||
|
from utilities.blender_worker import BlenderRenderWorker
|
||||||
|
from utilities.aerender_worker import AERenderWorker
|
||||||
|
from utilities.ffmpeg_worker import FFMPEGRenderWorker
|
||||||
|
classes = [BlenderRenderWorker, AERenderWorker, FFMPEGRenderWorker]
|
||||||
|
return classes
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_worker(renderer, input_path, output_path, args=None):
|
def create_worker(renderer, input_path, output_path, args=None):
|
||||||
worker_class = RenderWorkerFactory.class_for_name(renderer)
|
worker_class = RenderWorkerFactory.class_for_name(renderer)
|
||||||
@@ -222,23 +230,14 @@ class RenderWorkerFactory:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def supported_renderers():
|
def supported_renderers():
|
||||||
return ['aerender', 'blender', 'ffmpeg']
|
return [x.render_engine for x in RenderWorkerFactory.supported_classes()]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def class_for_name(name):
|
def class_for_name(name):
|
||||||
|
|
||||||
from utilities.blender_worker import BlenderRenderWorker
|
|
||||||
from utilities.aerender_worker import AERenderWorker
|
|
||||||
from utilities.ffmpeg_worker import FFMPEGRenderWorker
|
|
||||||
|
|
||||||
name = name.lower()
|
name = name.lower()
|
||||||
|
for render_class in RenderWorkerFactory.supported_classes():
|
||||||
if "blender" == name:
|
if render_class.render_engine == name:
|
||||||
return BlenderRenderWorker
|
return render_class
|
||||||
elif "aerender" == name:
|
|
||||||
return AERenderWorker
|
|
||||||
elif "ffmpeg" == name:
|
|
||||||
return FFMPEGRenderWorker
|
|
||||||
|
|
||||||
raise LookupError(f'Cannot find class for name: {name}')
|
raise LookupError(f'Cannot find class for name: {name}')
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user