def _create_instances_for_aov(self, instance_data, exp_files): """Create instance for each AOV found. This will create new instance for every aov it can detect in expected files list. Arguments: instance_data (pyblish.plugin.Instance): skeleton data for instance (those needed) later by collector exp_files (list): list of expected files divided by aovs Returns: list of instances """ task = os.environ["AVALON_TASK"] subset = instance_data["subset"] instances = [] # go through aovs in expected files for aov, files in exp_files[0].items(): cols, rem = clique.assemble(files) # we shouldn't have any reminders. And if we do, it should # be just one item for single frame renders. if not cols and rem: assert len(rem) == 1, ("Found multiple non related files " "to render, don't know what to do " "with them.") col = rem[0] ext = os.path.splitext(col)[1].lstrip(".") else: # but we really expect only one collection. # Nothing else make sense. assert len( cols ) == 1, "only one image sequence type is expected" # noqa: E501 ext = cols[0].tail.lstrip(".") col = list(cols[0]) self.log.debug(col) # create subset name `familyTaskSubset_AOV` group_name = 'render{}{}{}{}'.format(task[0].upper(), task[1:], subset[0].upper(), subset[1:]) subset_name = '{}_{}'.format(group_name, aov) if isinstance(col, (list, tuple)): staging = os.path.dirname(col[0]) else: staging = os.path.dirname(col) success, rootless_staging_dir = ( self.anatomy.find_root_template_from_path(staging)) if success: staging = rootless_staging_dir else: self.log.warning( ("Could not find root path for remapping \"{}\"." " This may cause issues on farm.").format(staging)) self.log.info("Creating data for: {}".format(subset_name)) app = os.environ.get("AVALON_APP", "") preview = False if app in self.aov_filter.keys(): for aov_pattern in self.aov_filter[app]: if re.match(aov_pattern, aov): preview = True break new_instance = copy(instance_data) new_instance["subset"] = subset_name new_instance["subsetGroup"] = group_name # create represenation if isinstance(col, (list, tuple)): files = [os.path.basename(f) for f in col] else: files = os.path.basename(col) rep = { "name": ext, "ext": ext, "files": files, "frameStart": int(instance_data.get("frameStartHandle")), "frameEnd": int(instance_data.get("frameEndHandle")), # If expectedFile are absolute, we need only filenames "stagingDir": staging, "fps": new_instance.get("fps"), "tags": ["review"] if preview else [] } # support conversion from tiled to scanline if instance_data.get("convertToScanline"): self.log.info("Adding scanline conversion.") rep["tags"].append("toScanline") # poor man exclusion if ext in self.skip_integration_repre_list: rep["tags"].append("delete") self._solve_families(new_instance, preview) new_instance["representations"] = [rep] # if extending frames from existing version, copy files from there # into our destination directory if new_instance.get("extendFrames", False): self._copy_extend_frames(new_instance, rep) instances.append(new_instance) return instances
def _copy_extend_frames(self, instance, representation): """Copy existing frames from latest version. This will copy all existing frames from subset's latest version back to render directory and rename them to what renderer is expecting. Arguments: instance (pyblish.plugin.Instance): instance to get required data from representation (dict): presentation to operate on """ import speedcopy self.log.info("Preparing to copy ...") start = instance.data.get("startFrame") end = instance.data.get("endFrame") # get latest version of subset # this will stop if subset wasn't published yet version = pype.api.get_latest_version(instance.data.get("asset"), instance.data.get("subset")) # get its files based on extension subset_resources = get_resources(version, representation.get("ext")) r_col, _ = clique.assemble(subset_resources) # if override remove all frames we are expecting to be rendered # so we'll copy only those missing from current render if instance.data.get("overrideExistingFrame"): for frame in range(start, end + 1): if frame not in r_col.indexes: continue r_col.indexes.remove(frame) # now we need to translate published names from represenation # back. This is tricky, right now we'll just use same naming # and only switch frame numbers resource_files = [] r_filename = os.path.basename( representation.get("files")[0]) # first file op = re.search(self.R_FRAME_NUMBER, r_filename) pre = r_filename[:op.start("frame")] post = r_filename[op.end("frame"):] assert op is not None, "padding string wasn't found" for frame in list(r_col): fn = re.search(self.R_FRAME_NUMBER, frame) # silencing linter as we need to compare to True, not to # type assert fn is not None, "padding string wasn't found" # list of tuples (source, destination) staging = representation.get("stagingDir") staging = self.anatomy.fill_roots(staging) resource_files.append( (frame, os.path.join(staging, "{}{}{}".format(pre, fn.group("frame"), post)))) # test if destination dir exists and create it if not output_dir = os.path.dirname(representation.get("files")[0]) if not os.path.isdir(output_dir): os.makedirs(output_dir) # copy files for source in resource_files: speedcopy.copy(source[0], source[1]) self.log.info(" > {}".format(source[1])) self.log.info("Finished copying %i files" % len(resource_files))
def add_sequence(self, packager, aov_path, aov_name, package_path): """ """ from maya import cmds seq_dir, pattern = os.path.split(aov_path) self.log.info("Collecting sequence from: %s" % seq_dir) assert os.path.isdir(seq_dir), "Sequence dir not exists." # (NOTE) Did not consider frame step (byFrame) start_frame = self.data["startFrame"] end_frame = self.data["endFrame"] patterns = [ clique.PATTERNS["frames"], clique.DIGITS_PATTERN, ] minimum_items = 1 if start_frame == end_frame else 2 collections, _ = clique.assemble(os.listdir(seq_dir), patterns=patterns, minimum_items=minimum_items) assert len(collections), "Extraction failed, no sequence found." for sequence in collections: if pattern == (sequence.head + "#" * sequence.padding + sequence.tail): break else: raise Exception("No sequence match this pattern: %s" % pattern) entry_fname = (sequence.head + "%%0%dd" % sequence.padding + sequence.tail) project = self.context.data["projectDoc"] e_in, e_out, handles, _ = reveries.utils.get_timeline_data(project) camera = self.data["camera"] packager.add_data({ "sequence": { aov_name: { "imageFormat": self.data["fileExt"], "fname": entry_fname, "seqSrcDir": seq_dir, "seqStart": list(sequence.indexes)[0], "seqEnd": list(sequence.indexes)[-1], "startFrame": start_frame, "endFrame": end_frame, "byFrameStep": self.data["byFrameStep"], "edit_in": e_in, "edit_out": e_out, "handles": handles, "focalLength": cmds.getAttr(camera + ".focalLength"), "resolution": self.data["resolution"], "fps": self.context.data["fps"], "cameraUUID": utils.get_id(camera), "renderlayer": self.data["renderlayer"], } } }) for file in [entry_fname % i for i in sequence.indexes]: src = seq_dir + "/" + file dst = os.path.join(package_path, aov_name, file) packager.add_hardlink(src, dst)
def _get_representations(self, instance, exp_files): """Create representations for file sequences. This will return representations of expected files if they are not in hierarchy of aovs. There should be only one sequence of files for most cases, but if not - we create representation from each of them. Arguments: instance (pyblish.plugin.Instance): instance for which we are setting representations exp_files (list): list of expected files Returns: list of representations """ representations = [] collections, remainders = clique.assemble(exp_files) bake_render_path = instance.get("bakeRenderPath", []) # create representation for every collected sequence for collection in collections: ext = collection.tail.lstrip(".") preview = False # if filtered aov name is found in filename, toggle it for # preview video rendering for app in self.aov_filter.keys(): if os.environ.get("AVALON_APP", "") == app: for aov in self.aov_filter[app]: if re.match(aov, list(collection)[0]): preview = True break if bake_render_path: preview = False staging = os.path.dirname(list(collection)[0]) success, rootless_staging_dir = ( self.anatomy.find_root_template_from_path(staging)) if success: staging = rootless_staging_dir else: self.log.warning( ("Could not find root path for remapping \"{}\"." " This may cause issues on farm.").format(staging)) rep = { "name": ext, "ext": ext, "files": [os.path.basename(f) for f in list(collection)], "frameStart": int(instance.get("frameStartHandle")), "frameEnd": int(instance.get("frameEndHandle")), # If expectedFile are absolute, we need only filenames "stagingDir": staging, "fps": instance.get("fps"), "tags": ["review"] if preview else [], } # poor man exclusion if ext in self.skip_integration_repre_list: rep["tags"].append("delete") if instance.get("multipartExr", False): rep["tags"].append("multipartExr") # support conversion from tiled to scanline if instance.get("convertToScanline"): self.log.info("Adding scanline conversion.") rep["tags"].append("toScanline") representations.append(rep) self._solve_families(instance, preview) # add reminders as representations for remainder in remainders: ext = remainder.split(".")[-1] staging = os.path.dirname(remainder) success, rootless_staging_dir = ( self.anatomy.find_root_template_from_path(staging)) if success: staging = rootless_staging_dir else: self.log.warning( ("Could not find root path for remapping \"{}\"." " This may cause issues on farm.").format(staging)) rep = { "name": ext, "ext": ext, "files": os.path.basename(remainder), "stagingDir": os.path.dirname(remainder), } if "render" in instance.get("families"): rep.update({"fps": instance.get("fps"), "tags": ["review"]}) self._solve_families(instance, True) if remainder in bake_render_path: rep.update({ "fps": instance.get("fps"), "tags": ["review", "delete"] }) # solve families with `preview` attributes self._solve_families(instance, True) representations.append(rep) return representations
def load(self, context, name, namespace, data): directory = os.path.dirname(self.fname) from avalon.vendor import clique pattern = clique.PATTERNS["frames"] files = os.listdir(directory) collections, remainder = clique.assemble(files, patterns=[pattern], minimum_items=1) if not remainder: seqeunce = collections[0] first_image = list(seqeunce)[0] # start = min(collections) # end = max(collections) # # range = (padding % start) + '-' + (padding % end) # filename = re.sub('%[0-9]*d', range, filename) else: first_image = self.fname filepath = os.path.normpath(os.path.join(directory, first_image)) self.log.info("Opening : {}".format(filepath)) fps = context.get('project', {}).get('data', {}).get('fps', 24) cmd = [] # DJV path cmd.append(os.path.normpath(self.djv_path)) # DJV Options Start ############################################## '''layer name''' # cmd.append('-file_layer (value)') ''' Proxy scale: 1/2, 1/4, 1/8''' # cmd.append('-file_proxy 1/2') ''' Cache: True, False.''' cmd.append('-file_cache True') ''' Start in full screen ''' # cmd.append('-window_fullscreen') ''' Toolbar controls: False, True.''' # cmd.append("-window_toolbar False") ''' Window controls: False, True.''' # cmd.append("-window_playbar False") ''' Grid overlay: None, 1x1, 10x10, 100x100.''' # cmd.append("-view_grid None") ''' Heads up display: True, False.''' # cmd.append("-view_hud True") ''' Playback: Stop, Forward, Reverse.''' cmd.append("-playback Forward") ''' Frame.''' # cmd.append("-playback_frame (value)") cmd.append("-playback_speed " + str(fps)) ''' Timer: Sleep, Timeout. Value: Sleep.''' # cmd.append("-playback_timer (value)") ''' Timer resolution (seconds): 0.001.''' # cmd.append("-playback_timer_resolution (value)") ''' Time units: Timecode, Frames.''' cmd.append("-time_units Frames") # DJV Options End ################################################ # PATH TO COMPONENT cmd.append(os.path.normpath(filepath)) # Run DJV with these commands subprocess.Popen(' '.join(cmd))