class ExtractROP(plugin.HoudiniExtractorPlugin):
"""Generic Extractor for any ROP node."""
label = "Extract ROP"
order = pyblish.api.ExtractorOrder
families = ["abc", "camera", "bgeo", "pointcache", "fbx",
"vdbcache", "ass", "redshiftproxy", "mantraifd"]
targets = ["local", "remote"]
def process(self, instance: pyblish.api.Instance):
if instance.data.get("farm"):
self.log.debug("Should be processed on farm, skipping.")
return
creator_attribute = instance.data["creator_attributes"]
files = instance.data["frames"]
first_file = files[0] if isinstance(files, (list, tuple)) else files
_, ext = splitext(
first_file, allowed_multidot_extensions=[
".ass.gz", ".bgeo.sc", ".bgeo.gz",
".bgeo.lzma", ".bgeo.bz2"]
)
ext = ext.lstrip(".")
# Value `local` is used as a fallback if the `render_target`
# key is missing.
# This key might be absent because render targets are not
# yet implemented for all product types that use this plugin.
if creator_attribute.get("render_target", "local") == "local":
self.render_rop(instance)
self.validate_expected_frames(instance)
# In some cases representation name is not the the extension
# TODO: Preferably we remove this very specific naming
product_type = instance.data["productType"]
name = {
"bgeo": "bgeo",
"rs": "rs",
"ass": "ass"
}.get(product_type, ext)
representation = {
"name": name,
"ext": ext,
"files": instance.data["frames"],
"stagingDir": instance.data["stagingDir"],
"frameStart": instance.data["frameStartHandle"],
"frameEnd": instance.data["frameEndHandle"],
}
self.update_representation_data(instance, representation)
instance.data.setdefault("representations", []).append(representation)
def validate_expected_frames(self, instance: pyblish.api.Instance):
"""
Validate all expected files in `instance.data["frames"]` exist in
the staging directory.
"""
filenames = instance.data["frames"]
staging_dir = instance.data["stagingDir"]
if isinstance(filenames, str):
# Single frame
filenames = [filenames]
missing_frames = []
for filename in filenames:
filename = os.path.join(staging_dir, filename)
if not os.path.isfile(filename):
missing_frames.append(filename)
if missing_frames:
# Combine collections for simpler logs of missing files
missing_frames = format_as_collections(missing_frames)
missing_frames = "\n ".join(
f"- {sequence}" for sequence in missing_frames
)
raise PublishError(
"Failed to complete render extraction.\n"
"Please render any missing output files.",
detail=f"Missing output files: \n {missing_frames}"
)
def update_representation_data(self,
instance: pyblish.api.Instance,
representation: dict):
"""Allow subclass to override the representation data in-place"""
pass