def launch(*subprocess_args): """Starts the websocket server that will be hosted in the Photoshop extension. """ from avalon import api, photoshop api.install(photoshop) sys.excepthook = safe_excepthook # Launch Photoshop 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 = 'Photoshop' print("Adding {} route".format(route_name)) WebSocketAsync.add_route( route_name, PhotoshopRoute # keep same name as in extension ) websocket_server.start_server() while True: if process.poll() is not None: print("Photoshop process is not alive. Exiting") websocket_server.stop() sys.exit(1) try: _stub = photoshop.stub() if _stub: break except Exception: time.sleep(0.5) # Wait for application launch to show Workfiles. if os.environ.get("AVALON_PHOTOSHOP_WORKFILES_ON_LAUNCH", True): if os.getenv("WORKFILES_SAVE_AS"): workfiles.show(save=False) else: workfiles.show() # Photoshop could be closed immediately, withou workfile selection try: if photoshop.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): 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"]) _, ext = os.path.splitext(scene_path) photoshop.stub().saveAs(scene_path, ext[1:], True) self.log.info("Incremented workfile to: {}".format(scene_path))
def process(self, context, plugin): # Get the errored instances failed = [] for result in context.data["results"]: if (result["error"] is not None and result["instance"] is not None and result["instance"] not in failed): failed.append(result["instance"]) # Apply pyblish.logic to get the instances for the plug-in instances = pyblish.api.instances_by_plugin(failed, plugin) stub = photoshop.stub() for instance in instances: self.log.info("validate_naming instance {}".format(instance)) name = instance.data["name"].replace(" ", "_") name = name.replace(instance.data["family"], '') instance[0].Name = name data = stub.read(instance[0]) data["subset"] = "image" + name stub.imprint(instance[0], data) name = stub.PUBLISH_ICON + name stub.rename_layer(instance.data["uuid"], name) return True
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 = photoshop.stub() if _stub: return True except Exception: pass return None
def process(self, instance): staging_dir = self.staging_dir(instance) self.log.info("Outputting image to {}".format(staging_dir)) # Perform extraction stub = photoshop.stub() files = {} with photoshop.maintained_selection(): self.log.info("Extracting %s" % str(list(instance))) with photoshop.maintained_visibility(): # Hide all other layers. extract_ids = set([ll.id for ll in stub. get_layers_in_layers([instance[0]])]) for layer in stub.get_layers(): # limit unnecessary calls to client if layer.visible and layer.id not in extract_ids: stub.set_visible(layer.id, False) save_options = [] if "png" in self.formats: save_options.append('png') if "jpg" in self.formats: save_options.append('jpg') file_basename = os.path.splitext( stub.get_active_document_name() )[0] for extension in save_options: _filename = "{}.{}".format(file_basename, extension) files[extension] = _filename full_filename = os.path.join(staging_dir, _filename) stub.saveAs(full_filename, extension, True) representations = [] for extension, filename in files.items(): representations.append({ "name": extension, "ext": extension, "files": filename, "stagingDir": staging_dir }) instance.data["representations"] = representations instance.data["stagingDir"] = staging_dir self.log.info(f"Extracted {instance} to {staging_dir}")
def process(self, context, plugin): # Get the errored instances failed = [] for result in context.data["results"]: if (result["error"] is not None and result["instance"] is not None and result["instance"] not in failed): failed.append(result["instance"]) # Apply pyblish.logic to get the instances for the plug-in instances = pyblish.api.instances_by_plugin(failed, plugin) stub = photoshop.stub() for instance in instances: data = stub.read(instance[0]) data["asset"] = os.environ["AVALON_ASSET"] stub.imprint(instance[0], data)
def process(self, context): stub = photoshop.stub() layers = stub.get_layers() layers_meta = stub.get_layers_metadata() instance_names = [] for layer in layers: layer_data = stub.read(layer, layers_meta) # Skip layers without metadata. if layer_data is None: continue # Skip containers. if "container" in layer_data["id"]: continue # child_layers = [*layer.Layers] # self.log.debug("child_layers {}".format(child_layers)) # if not child_layers: # self.log.info("%s skipped, it was empty." % layer.Name) # continue instance = context.create_instance(layer_data["subset"]) instance.append(layer) instance.data.update(layer_data) instance.data["families"] = self.families_mapping[ layer_data["family"]] instance.data["publish"] = layer.visible instance_names.append(layer_data["subset"]) # Produce diagnostic message for any graphical # user interface interested in visualising it. self.log.info("Found: \"%s\" " % instance.data["name"]) self.log.info("instance: {} ".format(instance.data)) if len(instance_names) != len(set(instance_names)): self.log.warning("Duplicate instances found. " + "Remove unwanted via SubsetManager")
def process(self): groups = [] layers = [] create_group = False stub = photoshop.stub() if (self.options or {}).get("useSelection"): multiple_instances = False selection = stub.get_selected_layers() self.log.info("selection {}".format(selection)) if len(selection) > 1: # Ask user whether to create one image or image per selected # item. msg_box = Qt.QtWidgets.QMessageBox() msg_box.setIcon(Qt.QtWidgets.QMessageBox.Warning) msg_box.setText("Multiple layers selected." "\nDo you want to make one image per layer?") msg_box.setStandardButtons(Qt.QtWidgets.QMessageBox.Yes | Qt.QtWidgets.QMessageBox.No | Qt.QtWidgets.QMessageBox.Cancel) ret = msg_box.exec_() if ret == Qt.QtWidgets.QMessageBox.Yes: multiple_instances = True elif ret == Qt.QtWidgets.QMessageBox.Cancel: return if multiple_instances: for item in selection: if item.group: groups.append(item) else: layers.append(item) else: group = stub.group_selected_layers(self.name) groups.append(group) elif len(selection) == 1: # One selected item. Use group if its a LayerSet (group), else # create a new group. if selection[0].group: groups.append(selection[0]) else: layers.append(selection[0]) elif len(selection) == 0: # No selection creates an empty group. create_group = True else: create_group = True if create_group: group = stub.create_group(self.name) groups.append(group) for layer in layers: stub.select_layers([layer]) group = stub.group_selected_layers(layer.name) groups.append(group) for group in groups: long_names = [] if group.long_name: for directory in group.long_name[::-1]: name = directory.replace(stub.PUBLISH_ICON, '').\ replace(stub.LOADED_ICON, '') long_names.append(name) self.data.update({"subset": "image" + group.name}) self.data.update({"uuid": str(group.id)}) self.data.update({"long_name": "_".join(long_names)}) stub.imprint(group, self.data) # reusing existing group, need to rename afterwards if not create_group: stub.rename_layer(group.id, stub.PUBLISH_ICON + group.name)
def process(self, context): context.data["currentFile"] = os.path.normpath( photoshop.stub().get_active_document_full_name()).replace( "\\", "/")
from avalon import api, photoshop import os import re stub = photoshop.stub() class ImageLoader(api.Loader): """Load images Stores the imported asset in a container named after the asset. """ families = ["image", "render"] representations = ["*"] def load(self, context, name=None, namespace=None, data=None): layer_name = self._get_unique_layer_name(context["asset"]["name"], name) with photoshop.maintained_selection(): layer = stub.import_smart_object(self.fname, layer_name) self[:] = [layer] namespace = namespace or layer_name return photoshop.containerise(name, namespace, layer, context, self.__class__.__name__) def update(self, container, representation): """ Switch asset or change version """ layer = container.pop("layer")
def process(self, instance): staging_dir = self.staging_dir(instance) self.log.info("Outputting image to {}".format(staging_dir)) stub = photoshop.stub() layers = [] for image_instance in instance.context: if image_instance.data["family"] != "image": continue layers.append(image_instance[0]) # Perform extraction output_image = "{}.jpg".format( os.path.splitext(stub.get_active_document_name())[0]) output_image_path = os.path.join(staging_dir, output_image) with photoshop.maintained_visibility(): # Hide all other layers. extract_ids = set( [ll.id for ll in stub.get_layers_in_layers(layers)]) self.log.info("extract_ids {}".format(extract_ids)) for layer in stub.get_layers(): # limit unnecessary calls to client if layer.visible and layer.id not in extract_ids: stub.set_visible(layer.id, False) stub.saveAs(output_image_path, 'jpg', True) ffmpeg_path = pype.lib.get_ffmpeg_tool_path("ffmpeg") instance.data["representations"].append({ "name": "jpg", "ext": "jpg", "files": output_image, "stagingDir": staging_dir }) instance.data["stagingDir"] = staging_dir # Generate thumbnail. thumbnail_path = os.path.join(staging_dir, "thumbnail.jpg") args = [ "{}".format(ffmpeg_path), "-y", "-i", output_image_path, "-vf", "scale=300:-1", "-vframes", "1", thumbnail_path ] output = pype.lib.run_subprocess(args) instance.data["representations"].append({ "name": "thumbnail", "ext": "jpg", "files": os.path.basename(thumbnail_path), "stagingDir": staging_dir, "tags": ["thumbnail"] }) # Generate mov. mov_path = os.path.join(staging_dir, "review.mov") args = [ ffmpeg_path, "-y", "-i", output_image_path, "-vf", "pad=ceil(iw/2)*2:ceil(ih/2)*2", "-vframes", "1", mov_path ] output = pype.lib.run_subprocess(args) self.log.debug(output) instance.data["representations"].append({ "name": "mov", "ext": "mov", "files": os.path.basename(mov_path), "stagingDir": staging_dir, "frameStart": 1, "frameEnd": 1, "fps": 25, "preview": True, "tags": ["review", "ftrackreview"] }) # Required for extract_review plugin (L222 onwards). instance.data["frameStart"] = 1 instance.data["frameEnd"] = 1 instance.data["fps"] = 25 self.log.info(f"Extracted {instance} to {staging_dir}")
def process(self, instance): photoshop.stub().save()