#! /usr/bin/python from utilities.generic_renderer import * import glob import json import re def aerender_path(): paths = glob.glob('/Applications/*After Effects*/aerender') if len(paths) > 1: logging.warning('Multiple After Effects installations detected') elif not paths: logging.error('After Effects installation not found') else: return paths[0] class AERenderer(Renderer): @staticmethod def version(): 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' render_engine = 'aerender' supported_extensions = ['.aep'] def __init__(self, project, comp, render_settings, omsettings, output): super(AERenderer, self).__init__(input=project, output=output) self.comp = comp self.render_settings = render_settings self.omsettings = omsettings self.progress = 0 self.progress_history = [] self.attributes = {} def _generate_subprocess(self): if os.path.exists('nexrender-cli-macos'): logging.info('nexrender found') # { # "template": { # "src": String, # "composition": String, # # "frameStart": Number, # "frameEnd": Number, # "frameIncrement": Number, # # "continueOnMissing": Boolean, # "settingsTemplate": String, # "outputModule": String, # "outputExt": String, # }, # "assets": [], # "actions": { # "prerender": [], # "postrender": [], # }, # "onChange": Function, # "onRenderProgress": Function # } job = {'template': { 'src': 'file://' + self.input, 'composition': self.comp.replace('"', ''), 'settingsTemplate': self.render_settings.replace('"', ''), 'outputModule': self.omsettings.replace('"', ''), 'outputExt': 'mov'} } x = ['./nexrender-cli-macos', "'{}'".format(json.dumps(job))] else: logging.info('nexrender not found') x = [aerender_path(), '-project', self.input, '-comp', self.comp, '-RStemplate', self.render_settings, '-OMtemplate', self.omsettings, '-output', self.output] return x def _parse_stdout(self, line): # print line if line.startswith('PROGRESS:'): # print 'progress' trimmed = line.replace('PROGRESS:', '').strip() if len(trimmed): self.progress_history.append(line) if 'Seconds' in trimmed: self._update_progress(line) elif ': ' in trimmed: tmp = trimmed.split(': ') self.attributes[tmp[0].strip()] = tmp[1].strip() elif line.startswith('WARNING:'): trimmed = line.replace('WARNING:', '').strip() self.warnings.append(trimmed) logging.warning(trimmed) elif line.startswith('aerender ERROR') or 'ERROR:' in line: self.errors.append(line) logging.error(line) def _update_progress(self, line): if not self.total_frames: duration_string = self.attributes.get('Duration', None) frame_rate = self.attributes.get('Frame Rate', '0').split(' ')[0] self.total_frames = timecode_to_frames(duration_string.split('Duration:')[-1], float(frame_rate)) match = re.match(r'PROGRESS:.*\((?P\d+)\): (?P