Change Renderer classes to RenderWorkers. Created RenderWorkerFactory.

This commit is contained in:
Brett Williams
2022-10-08 17:09:13 -07:00
parent 1c304e77f5
commit 7b33f06405
6 changed files with 79 additions and 61 deletions

View File

@@ -1,8 +1,9 @@
#! /usr/bin/python
from utilities.generic_renderer import *
from utilities.render_worker import *
import glob
import json
import re
import time
def aerender_path():
@@ -15,7 +16,7 @@ def aerender_path():
return paths[0]
class AERenderer(Renderer):
class AERenderWorker(RenderWorker):
@staticmethod
def version():
@@ -33,12 +34,12 @@ class AERenderer(Renderer):
render_engine = 'aerender'
supported_extensions = ['.aep']
def __init__(self, project, comp, render_settings, omsettings, output):
super(AERenderer, self).__init__(input=project, output=output)
def __init__(self, input_path, output_path, args=None):
super(AERenderWorker, self).__init__(input_path=input_path, output_path=output_path, args=args)
self.comp = comp
self.render_settings = render_settings
self.omsettings = omsettings
self.comp = self.args.get('comp')
self.render_settings = self.args.get('render_settings')
self.omsettings = self.args.get('omsettings')
self.progress = 0
self.progress_history = []
@@ -136,7 +137,7 @@ class AERenderer(Renderer):
if __name__ == '__main__':
logging.basicConfig(format='%(asctime)s - %(message)s', datefmt='%d-%b-%y %H:%M:%S', level=logging.DEBUG)
r = AERenderer('/Users/brett/Desktop/Youtube_Vids/Film_Formats/Frame_Animations.aep', '"Film Pan"',
r = AERenderWorker('/Users/brett/Desktop/Youtube_Vids/Film_Formats/Frame_Animations.aep', '"Film Pan"',
'"Draft Settings"', '"ProRes"', '/Users/brett/Desktop/test_render')
r.start()
while r.is_running():

View File

@@ -1,10 +1,12 @@
#! /usr/bin/python
from utilities.generic_renderer import *
from utilities.render_worker import *
import time
SUPPORTED_FORMATS = ['TGA', 'RAWTGA', 'JPEG', 'IRIS', 'IRIZ', 'AVIRAW', 'AVIJPEG', 'PNG', 'BMP', 'HDR', 'TIFF', 'OPEN_EXR', 'OPEN_EXR_MULTILAYER', 'MPEG', 'CINEON', 'DPX', 'DDS', 'JP2']
SUPPORTED_FORMATS = ['TGA', 'RAWTGA', 'JPEG', 'IRIS', 'IRIZ', 'AVIRAW', 'AVIJPEG', 'PNG', 'BMP', 'HDR', 'TIFF',
'OPEN_EXR', 'OPEN_EXR_MULTILAYER', 'MPEG', 'CINEON', 'DPX', 'DDS', 'JP2']
class BlenderRenderer(Renderer):
class BlenderRenderWorker(RenderWorker):
def version(self):
version = None
@@ -20,14 +22,14 @@ class BlenderRenderer(Renderer):
supported_extensions = ['.blend']
install_paths = ['/Applications/Blender.app/Contents/MacOS/Blender']
def __init__(self, input_path, output_path, render_all_frames=False, engine='BLENDER_EEVEE'):
super(BlenderRenderer, self).__init__(input_path=input_path, output_path=output_path)
def __init__(self, input_path, output_path, args=None):
super(BlenderRenderWorker, self).__init__(input_path=input_path, output_path=output_path, args=args)
self.engine = engine # or 'CYCLES'
self.engine = self.args.get('engine', 'BLENDER_EEVEE')
self.format = 'JPEG'
self.frame = 0
self.render_all_frames = render_all_frames
self.render_all_frames = self.args.get('render_all_frames', False)
# Stats
self.current_frame = -1
@@ -65,7 +67,7 @@ class BlenderRenderer(Renderer):
sample_string = line.split('|')[-1].strip()
if "sample" in sample_string.lower():
samples = re.sub(r'[^0-9/]', '', sample_string)
samples = re.sub(r'[^\d/]', '', sample_string)
self.frame_percent_complete = int(samples.split('/')[0]) / int(samples.split('/')[-1])
@@ -110,7 +112,7 @@ class BlenderRenderer(Renderer):
if __name__ == '__main__':
logging.basicConfig(format='%(asctime)s - %(message)s', datefmt='%d-%b-%y %H:%M:%S', level=logging.DEBUG)
r = BlenderRenderer('/Users/brett/Blender Files/Ian Hubert/CyberExtras.blend', '/Users/brett/testing1234', render_all_frames=False, engine="CYCLES")
r = BlenderRenderWorker('/Users/brett/Blender Files/Ian Hubert/CyberExtras.blend', '/Users/brett/testing1234', render_all_frames=False, engine="CYCLES")
# r.engine = 'CYCLES'
r.start()
while r.is_running():

View File

@@ -1,5 +1,5 @@
#! /usr/bin/python
from generic_renderer import *
from render_worker import *
import glob
import logging
import subprocess
@@ -11,7 +11,7 @@ def compressor_path():
return '/Applications/Compressor.app/Contents/MacOS/Compressor'
class CompressorRenderer(Renderer):
class CompressorRenderWorker(RenderWorker):
renderer = 'Compressor'
@@ -94,7 +94,7 @@ class CompressorRenderer(Renderer):
# -locationpath <path> -- path to location file. Modified movie will be saved here. If unspecified, changes will be saved in place, overwriting the original file.
def __init__(self, project, settings_path, output):
super(CompressorRenderer, self).__init__(project=project, output=output)
super(CompressorRenderWorker, self).__init__(project=project, output=output)
self.settings_path = settings_path
self.batch_name = os.path.basename(project)
@@ -115,7 +115,7 @@ class CompressorRenderer(Renderer):
if __name__ == '__main__':
logging.basicConfig(format='%(asctime)s - %(message)s', datefmt='%d-%b-%y %H:%M:%S', level=logging.DEBUG)
r = CompressorRenderer('/Users/brett/Desktop/drone_raw.mp4', '/Applications/Compressor.app/Contents/Resources/Settings/Website Sharing/HD720WebShareName.compressorsetting', '/Users/brett/Desktop/test_drone_output.mp4')
r = CompressorRenderWorker('/Users/brett/Desktop/drone_raw.mp4', '/Applications/Compressor.app/Contents/Resources/Settings/Website Sharing/HD720WebShareName.compressorsetting', '/Users/brett/Desktop/test_drone_output.mp4')
r.start()
while r.is_running():
time.sleep(1)

View File

@@ -4,10 +4,10 @@ import time
import ffmpeg
from utilities.generic_renderer import *
from utilities.render_worker import *
class FFMPEGRenderer(Renderer):
class FFMPEGRenderWorker(RenderWorker):
def version(self):
version = None
@@ -23,7 +23,7 @@ class FFMPEGRenderer(Renderer):
render_engine = 'ffmpeg'
def __init__(self, input_path, output_path, args=None):
super(FFMPEGRenderer, self).__init__(input_path=input_path, output_path=output_path, ignore_extensions=True)
super(FFMPEGRenderWorker, self).__init__(input_path=input_path, output_path=output_path, ignore_extensions=True, args=args)
self.total_frames = -1
if os.path.exists(input_path):
@@ -34,7 +34,6 @@ class FFMPEGRenderer(Renderer):
break
self.frame = 0
self.args = args
# Stats
self.current_frame = -1
@@ -58,6 +57,8 @@ class FFMPEGRenderer(Renderer):
stats = found.groupdict()
self.current_frame = stats['current_frame']
self.time_elapsed = stats['time_elapsed']
elif "not found" in line:
self.errors.append(line)
if __name__ == '__main__':
@@ -65,7 +66,7 @@ if __name__ == '__main__':
test_movie = '/Users/brettwilliams/Desktop/dark_knight_rises.mp4'
r = FFMPEGRenderer(test_movie, '/Users/brettwilliams/Desktop/test-ffmpeg.mp4', args=['-c:v','libx265','-vtag','hvc1'])
r = FFMPEGRenderWorker(test_movie, '/Users/brettwilliams/Desktop/test-ffmpeg.mp4', args=['-c:v', 'libx265', '-vtag', 'hvc1'])
# r = FFMPEGRenderer(test_movie, '/Users/brettwilliams/Desktop/dark_knight_rises-output.mp4')
r.start()
while r.is_running():

View File

@@ -28,7 +28,7 @@ def string_to_status(string):
return RenderStatus.ERROR
class Renderer(object):
class RenderWorker(object):
renderer = 'GenericRenderer'
render_engine = None
@@ -39,7 +39,7 @@ class Renderer(object):
def version():
return 'Unknown'
def __init__(self, input_path, output_path, ignore_extensions=False):
def __init__(self, input_path, output_path, args=None, ignore_extensions=False):
if not ignore_extensions:
if not any(ext in input_path for ext in self.supported_extensions):
@@ -50,6 +50,7 @@ class Renderer(object):
# Essential Info
self.input = input_path
self.output = output_path
self.args = args or {}
self.date_created = datetime.now()
self.attributes = {}
self.renderer_version = self.version()
@@ -169,6 +170,8 @@ class Renderer(object):
if self.failed_attempts >= self.maximum_attempts and self.status is not RenderStatus.CANCELLED:
logger.error('{} Render of {} failed after {} attempts'.format(self.renderer, self.input, self.failed_attempts))
self.status = RenderStatus.ERROR
if not self.errors:
self.errors = [self.last_output]
self.is_finished = True
def is_running(self):
@@ -204,8 +207,33 @@ class Renderer(object):
return elapsed
class RenderWorkerFactory:
@staticmethod
def create_worker(renderer, input_path, output_path, args=None):
from utilities.blender_worker import BlenderRenderWorker
from utilities.aerender_worker import AERenderWorker
from utilities.ffmpeg_worker import FFMPEGRenderWorker
if "blender" == renderer.lower():
worker = BlenderRenderWorker(input_path, output_path, args=args)
elif "aerender" == renderer.lower() or "after effects" == renderer.lower():
worker = AERenderWorker(input_path, output_path, args=args)
elif "ffmpeg" == renderer.lower():
worker = FFMPEGRenderWorker(input_path, output_path, args=args)
else:
raise ValueError(f"Cannot find renderer for type '{renderer}'")
return worker
@staticmethod
def supported_renderers():
return ['aerender', 'blender', 'ffmpeg']
def timecode_to_frames(timecode, frame_rate):
e = [int(x) for x in timecode.split(':')]
seconds = (((e[0] * 60) + e[1] * 60) + e[2])
frames = (seconds * frame_rate) + e[-1] + 1
return frames
return frames

View File

@@ -13,11 +13,7 @@ import json
import os
from flask import Flask, jsonify, request
from utilities.aerender import AERenderer
from utilities.blender import BlenderRenderer
from utilities.ffmpeg_render import FFMPEGRenderer
from utilities.generic_renderer import RenderStatus
from utilities.generic_renderer import string_to_status
from utilities.render_worker import RenderWorkerFactory, RenderStatus, string_to_status
data = 'foo'
app = Flask(__name__)
@@ -86,11 +82,6 @@ class RenderJob:
return json_string
def render_factory(input_path, output_path):
if '.blend' in input_path.lower():
return BlenderRenderer(input_path, output_path)
class RenderServer:
render_queue = []
render_clients = []
@@ -154,14 +145,7 @@ class RenderServer:
for job in job_list:
# Identify renderer type and recreate Renderer object
# TODO: refactor to factory class
job_render_object = None
if job['renderer'] == 'Blender':
job_render_object = BlenderRenderer(job['render']['input'], job['render']['output'])
elif job['renderer'] == 'After Effects':
AERenderer()
elif job['renderer'] == 'ffmpeg':
job_render_object = FFMPEGRenderer(job['render']['input'], job['render']['output'])
job_render_object = RenderWorkerFactory.create_worker(job['renderer'], input_path=job['render']['input'], output_path=job['render']['output'])
# Load Renderer values
for key, val in job['render'].items():
@@ -335,27 +319,29 @@ def add_job():
renderer = request.json["renderer"]
input_path = request.json["input"]
output_path = request.json["output"]
priority = request.json.get('priority', 2)
args = request.json.get('args', None)
force_start = request.json.get('force_start', False)
return add_job_handler(renderer, input_path, output_path, args=args, priority=priority, force_start=force_start)
def add_job_handler(renderer, input_path, output_path, args=None, priority=2, force_start=False):
if not os.path.exists(input_path):
err_msg = f"Cannot add job. Cannot find input file: {input_path}"
logger.error(err_msg)
return {"error": err_msg}, 400
# todo: create factory class for creating renderers
if "blender" in renderer:
render_job = BlenderRenderer(input_path, output_path)
render_job.engine = request.json.get('engine', 'BLENDER_EEVEE')
elif "aerender" in renderer:
render_job = AERenderer(input_path, output_path)
elif "ffmpeg" in renderer:
render_job = FFMPEGRenderer(input_path, output_path, args=request.json.get('args', None))
else:
err_msg = "Unknown renderer: {}".format(renderer)
logger.error(err_msg)
return {'error': err_msg}, 400
try:
render_job = RenderWorkerFactory.create_worker(renderer, input_path, output_path, args)
except ValueError as e:
logger.exception(e)
return {'error': str(e)}, 400
new_job = RenderJob(render_job, priority=priority)
RenderServer.add_to_render_queue(new_job, force_start=force_start)
new_job = RenderJob(render_job, priority=request.json.get('priority', 2))
RenderServer.add_to_render_queue(new_job, force_start=request.json.get('force_start', False))
return new_job.json()