def process(self, instance): errored_plugins = get_errored_plugins_from_data(instance.context) if errored_plugins: raise RuntimeError( "Skipping incrementing current file because publishing failed." ) scene_path = version_up(instance.context.data["currentFile"]) aftereffects.stub().saveAs(scene_path, True) self.log.info("Incremented workfile to: {}".format(scene_path))
def launch(*subprocess_args): """Starts the websocket server that will be hosted in the Photoshop extension. """ from avalon import api, aftereffects api.install(aftereffects) sys.excepthook = safe_excepthook # Launch aftereffects and the websocket server. process = subprocess.Popen(subprocess_args, stdout=subprocess.PIPE) websocket_server = WebServerTool() # Add Websocket route websocket_server.add_route("*", "/ws/", WebSocketAsync) # Add after effects route to websocket handler route_name = 'AfterEffects' print("Adding {} route".format(route_name)) WebSocketAsync.add_route(route_name, AfterEffectsRoute) websocket_server.start_server() while True: if process.poll() is not None: print("AfterEffects process is not alive. Exiting") websocket_server.stop() sys.exit(1) try: _stub = aftereffects.stub() if _stub: break except Exception: time.sleep(0.5) if os.environ.get("AVALON_AFTEREFFECTS_WORKFILES_ON_LAUNCH", True): if os.getenv("WORKFILES_SAVE_AS"): workfiles.show(save=False) else: workfiles.show() # AE could be closed immediately, withou workfile selection try: if aftereffects.stub(): api.emit("application.launched") self.callback_queue = queue.Queue() while True: main_thread_listen(process, websocket_server) except ConnectionNotEstablishedYet: pass finally: # Wait on Photoshop to close before closing the websocket server process.wait() websocket_server.stop()
def process(self, instance): stub = aftereffects.stub() self.log.debug("instance::{}".format(instance.data)) item = instance.data comp_name = item["comp_name"].replace(stub.PUBLISH_ICON, '') stub.rename_item(item["comp_id"], comp_name) instance.data["comp_name"] = comp_name
def process(self): stub = aftereffects.stub() # only after After Effects is up if (self.options or {}).get("useSelection"): items = stub.get_selected_items(comps=True, folders=False, footages=False) if len(items) > 1: self._show_msg("Please select only single composition at time.") return False if not items: self._show_msg("Nothing to create. Select composition " + "if 'useSelection' or create at least " + "one composition.") return False existing_subsets = [instance['subset'].lower() for instance in aftereffects.list_instances()] item = items.pop() if self.name.lower() in existing_subsets: txt = "Instance with name \"{}\" already exists.".format(self.name) self._show_msg(txt) return False self.data["members"] = [item.id] self.data["uuid"] = item.id # for SubsetManager stub.imprint(item, self.data) stub.set_label_color(item.id, 14) # Cyan options 0 - 16 stub.rename_item(item.id, stub.PUBLISH_ICON + self.data["subset"])
def process(self, context): for instance in context: if instance.data["family"] == 'render.farm': comp_id = instance.data["comp_id"] if not comp_id: self.log.debug("No comp_id filled in instance") return context.data["audioFile"] = os.path.normpath( aftereffects.stub().get_audio_url(comp_id) ).replace("\\", "/")
def is_host_connected(): """Returns True if connected, False if app is not running at all.""" if ConsoleTrayApp.process.poll() is not None: return False try: _stub = aftereffects.stub() if _stub: return True except Exception: pass return None
def get_expected_files(self, render_instance): """ Returns list of rendered files that should be created by Deadline. These are not published directly, they are source for later 'submit_publish_job'. Args: render_instance (RenderInstance): to pull anatomy and parts used in url Returns: (list) of absolut urls to rendered file """ start = render_instance.frameStart end = render_instance.frameEnd # pull file name from Render Queue Output module render_q = aftereffects.stub().get_render_info() if not render_q: raise ValueError("No file extension set in Render Queue") _, ext = os.path.splitext(os.path.basename(render_q.file_name)) base_dir = self._get_output_dir(render_instance) expected_files = [] if "#" not in render_q.file_name: # single frame (mov)W path = os.path.join(base_dir, "{}_{}_{}.{}".format( render_instance.asset, render_instance.subset, "v{:03d}".format(render_instance.version), ext.replace('.', '') )) expected_files.append(path) else: for frame in range(start, end + 1): path = os.path.join(base_dir, "{}_{}_{}.{}.{}".format( render_instance.asset, render_instance.subset, "v{:03d}".format(render_instance.version), str(frame).zfill(self.padding_width), ext.replace('.', '') )) expected_files.append(path) return expected_files
def process(self, instance): aftereffects.stub().save()
def process(self, context): context.data["currentFile"] = os.path.normpath( aftereffects.stub().get_active_document_full_name()).replace( "\\", "/")
def process(self, instance): stub = aftereffects.stub() item = instance.data # comp name contains highlight icon stub.rename_item(item["comp_id"], item["comp_name"])
import re from avalon import api, aftereffects from pype.lib import get_background_layers, get_unique_layer_name stub = aftereffects.stub() class BackgroundLoader(api.Loader): """ Load images from Background family Creates for each background separate folder with all imported images from background json AND automatically created composition with layers, each layer for separate image. For each load container is created and stored in project (.aep) metadata """ families = ["background"] representations = ["json"] def load(self, context, name=None, namespace=None, data=None): items = stub.get_items(comps=True) existing_items = [layer.name for layer in items] comp_name = get_unique_layer_name( existing_items, "{}_{}".format(context["asset"]["name"], name)) layers = get_background_layers(self.fname) comp = stub.import_background(None, stub.LOADED_ICON + comp_name,
def get_instances(self, context): instances = [] current_file = context.data["currentFile"] version = context.data["version"] asset_entity = context.data["assetEntity"] project_entity = context.data["projectEntity"] compositions = aftereffects.stub().get_items(True) compositions_by_id = {item.id: item for item in compositions} for inst in aftereffects.stub().get_metadata(): schema = inst.get('schema') # loaded asset container skip it if schema and 'container' in schema: continue if not inst["members"]: raise ValueError("Couldn't find id, unable to publish. " + "Please recreate instance.") item_id = inst["members"][0] work_area_info = aftereffects.stub().get_work_area(int(item_id)) frameStart = work_area_info.workAreaStart frameEnd = round(work_area_info.workAreaStart + float(work_area_info.workAreaDuration) * float(work_area_info.frameRate)) - 1 if inst["family"] == "render" and inst["active"]: instance = AERenderInstance( family="render.farm", # other way integrate would catch it families=["render.farm"], version=version, time="", source=current_file, label="{} - farm".format(inst["subset"]), subset=inst["subset"], asset=context.data["assetEntity"]["name"], attachTo=False, setMembers='', publish=True, renderer='aerender', name=inst["subset"], resolutionWidth=asset_entity["data"].get( "resolutionWidth", project_entity["data"]["resolutionWidth"]), resolutionHeight=asset_entity["data"].get( "resolutionHeight", project_entity["data"]["resolutionHeight"]), pixelAspect=1, tileRendering=False, tilesX=0, tilesY=0, frameStart=frameStart, frameEnd=frameEnd, frameStep=1, toBeRenderedOn='deadline' ) comp = compositions_by_id.get(int(item_id)) if not comp: raise ValueError("There is no composition for item {}". format(item_id)) instance.comp_name = comp.name instance.comp_id = item_id instance._anatomy = context.data["anatomy"] instance.anatomyData = context.data["anatomyData"] instance.outputDir = self._get_output_dir(instance) instances.append(instance) return instances
class CollectAERender(abstract_collect_render.AbstractCollectRender): order = pyblish.api.CollectorOrder + 0.498 label = "Collect After Effects Render Layers" hosts = ["aftereffects"] padding_width = 6 rendered_extension = 'png' stub = aftereffects.stub() def get_instances(self, context): instances = [] current_file = context.data["currentFile"] version = context.data["version"] asset_entity = context.data["assetEntity"] project_entity = context.data["projectEntity"] compositions = self.stub.get_items(True) compositions_by_id = {item.id: item for item in compositions} for inst in self.stub.get_metadata(): schema = inst.get('schema') # loaded asset container skip it if schema and 'container' in schema: continue if not inst["members"]: raise ValueError("Couldn't find id, unable to publish. " + "Please recreate instance.") item_id = inst["members"][0] work_area_info = self.stub.get_work_area(int(item_id)) if not work_area_info: self.log.warning("Orphaned instance, deleting metadata") self.stub.remove_instance(int(item_id)) continue frameStart = work_area_info.workAreaStart frameEnd = round(work_area_info.workAreaStart + float(work_area_info.workAreaDuration) * float(work_area_info.frameRate)) - 1 if inst["family"] == "render" and inst["active"]: instance = AERenderInstance( family="render.farm", # other way integrate would catch it families=["render.farm"], version=version, time="", source=current_file, label="{} - farm".format(inst["subset"]), subset=inst["subset"], asset=context.data["assetEntity"]["name"], attachTo=False, setMembers='', publish=True, renderer='aerender', name=inst["subset"], resolutionWidth=asset_entity["data"].get( "resolutionWidth", project_entity["data"]["resolutionWidth"]), resolutionHeight=asset_entity["data"].get( "resolutionHeight", project_entity["data"]["resolutionHeight"]), pixelAspect=1, tileRendering=False, tilesX=0, tilesY=0, frameStart=frameStart, frameEnd=frameEnd, frameStep=1, toBeRenderedOn='deadline') comp = compositions_by_id.get(int(item_id)) if not comp: raise ValueError( "There is no composition for item {}".format(item_id)) instance.comp_name = comp.name instance.comp_id = item_id instance._anatomy = context.data["anatomy"] instance.anatomyData = context.data["anatomyData"] instance.outputDir = self._get_output_dir(instance) instances.append(instance) self.log.debug("instances::{}".format(instances)) return instances def get_expected_files(self, render_instance): """ Returns list of rendered files that should be created by Deadline. These are not published directly, they are source for later 'submit_publish_job'. Args: render_instance (RenderInstance): to pull anatomy and parts used in url Returns: (list) of absolut urls to rendered file """ start = render_instance.frameStart end = render_instance.frameEnd # pull file name from Render Queue Output module render_q = self.stub.get_render_info() if not render_q: raise ValueError("No file extension set in Render Queue") _, ext = os.path.splitext(os.path.basename(render_q.file_name)) base_dir = self._get_output_dir(render_instance) expected_files = [] if "#" not in render_q.file_name: # single frame (mov)W path = os.path.join( base_dir, "{}_{}_{}.{}".format(render_instance.asset, render_instance.subset, "v{:03d}".format(render_instance.version), ext.replace('.', ''))) expected_files.append(path) else: for frame in range(start, end + 1): path = os.path.join( base_dir, "{}_{}_{}.{}.{}".format( render_instance.asset, render_instance.subset, "v{:03d}".format(render_instance.version), str(frame).zfill(self.padding_width), ext.replace('.', ''))) expected_files.append(path) return expected_files def _get_output_dir(self, render_instance): """ Returns dir path of rendered files, used in submit_publish_job for metadata.json location. Should be in separate folder inside of work area. Args: render_instance (RenderInstance): Returns: (str): absolute path to rendered files """ # render to folder of workfile base_dir = os.path.dirname(render_instance.source) file_name, _ = os.path.splitext( os.path.basename(render_instance.source)) base_dir = os.path.join(base_dir, 'renders', 'aftereffects', file_name) # for submit_publish_job return base_dir