Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Houdini: Cleanup after publishing #9

Merged
merged 114 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from 101 commits
Commits
Show all changes
114 commits
Select commit Hold shift + click to select a range
f0ea0fb
expose stagingdir in houdini settings
MustafaJafar Feb 8, 2024
9285e0b
better organised file export paths
MustafaJafar Feb 8, 2024
24fd0cb
add collector: CollectStagingDirsForCleaningUp
MustafaJafar Feb 8, 2024
7025f0b
Merge branch 'develop' into feature/houdini_cleanup_after_publishing
MustafaJafar Feb 9, 2024
8113d74
fix faulty logic
MustafaJafar Feb 9, 2024
4e3d15f
apply cleaning up for some product types only
MustafaJafar Feb 9, 2024
a8d3591
add default value for filepath, add one more return
MustafaJafar Feb 14, 2024
060d826
disable CollectStagingDirsForCleaningUp by default
MustafaJafar Feb 14, 2024
fb9261a
Merge branch 'develop' into feature/houdini_cleanup_after_publishing
MustafaJafar Feb 14, 2024
2413a76
BigRoy comment - add expoted files to 'cleanupFullPaths and staging d…
MustafaJafar Feb 15, 2024
28ace53
Use full staging path in houdini create settings
MustafaJafar Feb 15, 2024
fa8a314
use standard keys
MustafaJafar Feb 15, 2024
7b3ae23
fix some bug
MustafaJafar Feb 15, 2024
52b7692
Merge branch 'develop' into feature/houdini_cleanup_after_publishing
MustafaJafar Feb 22, 2024
51b378d
BigRoy's comment - Allow admins to select product types for the colle…
MustafaJafar Feb 22, 2024
659ab20
Merge branch 'develop' into feature/houdini_cleanup_after_publishing
MustafaJafar Feb 23, 2024
da537c2
Merge branch 'develop' into feature/houdini_cleanup_after_publishing
MustafaJafar Feb 29, 2024
cf7fa9b
fix a typo with a class attribute
MustafaJafar Feb 29, 2024
492f021
add one more setting to CollectFilesForCleaningUpModel
MustafaJafar Feb 29, 2024
76b38b4
use lib function to get output parameters
MustafaJafar Feb 29, 2024
2a80259
update code logic to get the files to cleanup
MustafaJafar Feb 29, 2024
bb31dc3
consistent staging directories
MustafaJafar Mar 4, 2024
5667ee0
use dynamic product name in created Houdini ROPs
MustafaJafar Mar 4, 2024
930dbce
use os.path.join instead of string format
MustafaJafar Mar 4, 2024
54eb86c
use better list operations
MustafaJafar Mar 4, 2024
5e4138e
add TODO for Intermediate render files
MustafaJafar Mar 4, 2024
d7c0dff
skip collector if no instance node found
MustafaJafar Mar 4, 2024
746819e
better variable/setting naming and add description for it
MustafaJafar Mar 4, 2024
607a266
move create_file_list to Houdini's lib
MustafaJafar Mar 4, 2024
4b9a750
Implement TODO: get_ifd_file_list
MustafaJafar Mar 4, 2024
ffd84d4
uncomment products in product_types_enum
MustafaJafar Mar 4, 2024
405f9a0
use forward slashed with generated file list
MustafaJafar Mar 4, 2024
d5101b7
put return in a correct place
MustafaJafar Mar 5, 2024
499d2db
Merge branch 'develop' into feature/houdini_cleanup_after_publishing …
MustafaJafar Apr 2, 2024
2f10964
Merge branch 'develop' into feature/houdini_cleanup_after_publishing
MustafaJafar Apr 22, 2024
ccaba93
update comments
MustafaJafar Apr 22, 2024
af13d6a
revert changes
MustafaJafar Apr 22, 2024
ed5c5f0
add CollectFilesForCleaningUp
MustafaJafar Apr 22, 2024
2c53fcf
fix typo
MustafaJafar Apr 22, 2024
fee69d1
tweak filepath a little bit
MustafaJafar Apr 22, 2024
0ceec68
tweak filepath a little bit
MustafaJafar Apr 22, 2024
b0c62d3
tweak filepath a little bit
MustafaJafar Apr 22, 2024
064029c
remove redundant `/` in filepath
MustafaJafar Apr 22, 2024
bdbbd29
update comment
MustafaJafar Apr 22, 2024
662a931
tweak filepaths a little bit
MustafaJafar Apr 22, 2024
ed26f80
tweak filepath a little bit
MustafaJafar Apr 22, 2024
3652021
tweak filepath a little bit - add dynamic link to productname and use…
MustafaJafar Apr 22, 2024
5733fb7
revert changes in creators
MustafaJafar Apr 22, 2024
00366b4
fix wrong enum items
MustafaJafar Apr 22, 2024
0f7db69
move create_file_list to Houdini's lib
MustafaJafar Mar 4, 2024
6f2521b
update a TODO
MustafaJafar Apr 22, 2024
5b4a449
Merge branch 'develop' into feature/houdini_cleanup_after_publishing
MustafaJafar Apr 25, 2024
0ad90c3
add CollectFilesForCleaning setting
MustafaJafar Apr 25, 2024
193b57d
Merge branch 'feature/houdini_cleanup_after_publishing' of https://gi…
MustafaJafar Apr 25, 2024
da07bce
Merge branch 'develop' into feature/houdini_cleanup_after_publishing
MustafaJafar Apr 29, 2024
02ee0c4
add CollectFilesForCleaning setting
MustafaJafar Apr 29, 2024
fd81990
CollectFilesForCleaningUp: Drop the enum and make it a regular list.
MustafaJafar Apr 29, 2024
4afad33
CollectFilesForCleaningUp: update link
MustafaJafar Apr 29, 2024
ba66237
Merge branch 'develop' into feature/houdini_cleanup_after_publishing
MustafaJafar May 2, 2024
5a36e1b
Merge branch 'develop' into feature/houdini_cleanup_after_publishing
MustafaJafar May 10, 2024
93792e3
Merge branch 'develop' into feature/houdini_cleanup_after_publishing
MustafaJafar May 13, 2024
c4613e1
add Cleanup after publishing settings
MustafaJafar May 13, 2024
a0e8a33
revert server_addon/houdini/server/settings/publish.py
MustafaJafar May 13, 2024
5ee0f96
add CollectFilesForCleaningUp settings
MustafaJafar May 13, 2024
764117d
Merge branch 'develop' into feature/houdini_cleanup_after_publishing
MustafaJafar May 23, 2024
2619387
Bump Addon version - add CollectFilesForCleaningUp setting
MustafaJafar May 23, 2024
4508cc9
Merge branch 'develop' into feature/houdini_cleanup_after_publishing
MustafaJafar May 28, 2024
7543082
Merge branch 'develop' into feature/houdini_cleanup_after_publishing
MustafaJafar Jun 3, 2024
a0b65f3
add CollectFilesForCleaningUp
MustafaJafar Jun 3, 2024
d6e6669
bump version
MustafaJafar Jun 3, 2024
48020b5
update imports
MustafaJafar Jun 3, 2024
04d2b6c
add model to product types enums
MustafaJafar Jun 3, 2024
d5b8507
Merge branch 'develop' into feature/houdini_cleanup_after_publishing
antirotor Jun 11, 2024
256053f
Merge branch 'develop' into feature/houdini_cleanup_after_publishing
antirotor Jun 11, 2024
547065d
use clique instead of lib.create_file_list
MustafaJafar Jun 14, 2024
d0f2642
use string format instead of os.path.join
MustafaJafar Jun 14, 2024
6d9667e
fix a bug with products with single file and improve the logic
MustafaJafar Jun 14, 2024
9a95b3b
update a comment and refactor a variable name
MustafaJafar Jun 14, 2024
48f7559
improve the logic
MustafaJafar Jun 14, 2024
f2cff11
prefer 'expectedFiles' over 'frames' and improve the logic
MustafaJafar Jun 14, 2024
9e7bf2f
Merge branch 'develop' into feature/houdini_cleanup_after_publishing
MustafaJafar Jun 14, 2024
0af97e7
Merge branch 'develop' into feature/houdini_cleanup_after_publishing
antirotor Jun 17, 2024
b4b8ffa
:dog: style changes
antirotor Jun 17, 2024
ae0c0e7
Merge branch 'feature/houdini_cleanup_after_publishing' of github.com…
antirotor Jun 17, 2024
274f175
Merge branch 'develop' into feature/houdini_cleanup_after_publishing
MustafaJafar Jun 25, 2024
a22a99b
Add CollectFilesForCleaningUp settings
MustafaJafar Jun 25, 2024
b04defe
remove mantraifd from products enum
MustafaJafar Jun 26, 2024
09b0364
use assignment instead of update
MustafaJafar Jun 26, 2024
8a264b1
Update doc string of CollectFilesForCleaningUp
MustafaJafar Jun 26, 2024
74fe37f
update log message
MustafaJafar Jun 26, 2024
c2d851a
use for loop instead of sum
MustafaJafar Jun 26, 2024
37cbcc1
update logic of CollectFilesForCleaningUp and add collector for HDA p…
MustafaJafar Jun 26, 2024
5d5f3d9
update comment in CollectFilesForCleaningUp.process
MustafaJafar Jun 27, 2024
9784287
make files complete for the single file condition
MustafaJafar Jun 27, 2024
e9e2e0c
add type hints
MustafaJafar Jun 27, 2024
03d1f85
fix type hints
MustafaJafar Jun 27, 2024
60ec862
revert changes in HDA plugins
MustafaJafar Jun 27, 2024
d34ccc1
remove farm related logic and add notes about it
MustafaJafar Jun 27, 2024
4d1539d
Merge branch 'develop' into feature/houdini_cleanup_after_publishing
MustafaJafar Jun 27, 2024
b8ec2a9
remove a redundant default setting value
MustafaJafar Jun 27, 2024
f596455
Merge branch 'develop' into feature/houdini_cleanup_after_publishing
BigRoy Jun 27, 2024
53fb6ba
Cosmetics PEP8
BigRoy Jun 27, 2024
24cc6a9
Use `List[str]` instead of `list[os.PathLike]`
BigRoy Jun 27, 2024
9b652df
Cosmetics
BigRoy Jun 27, 2024
52837d1
Explicitly error if it doesn't behave as we expect it to
BigRoy Jun 27, 2024
ae9fc4b
Merge branch 'develop' of https://github.com/ynput/ayon-core into fea…
BigRoy Jun 27, 2024
b818abc
Refactor `BasicValidateModel` -> `BasicEnabledStatesModel`
BigRoy Jun 27, 2024
23957c2
Merge remote-tracking branch 'BigRoy/feature/houdini_cleanup_after_pu…
MustafaJafar Jun 27, 2024
2e10a5d
refactor `BasicEnabledStatesModel` -> `CollectFilesForCleaningUpModel`
MustafaJafar Jun 27, 2024
15505a9
fix code style
MustafaJafar Jun 27, 2024
067f1c2
remove unused method
MustafaJafar Jun 27, 2024
c315c25
Merge branch 'feature/houdini_cleanup_after_publishing' of https://gi…
MustafaJafar Jun 27, 2024
435e5f3
Bump Houdini addon version
BigRoy Jun 27, 2024
78ab03a
Merge branch 'develop' of https://github.com/ynput/ayon-core into fea…
BigRoy Jun 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,47 +27,35 @@ def process(self, instance):
# Why do we need this particular collector to collect the expected
# output files from a ROP node. Don't we have a dedicated collector
# for that yet?
# Answer: No, we don't have a generic expected file collector.
# Because different product types needs different logic.
# e.g. check CollectMantraROPRenderProducts
# and CollectKarmaROPRenderProducts
# Collect expected files
ropnode = hou.node(instance.data["instance_node"])
output_parm = lib.get_output_parameter(ropnode)
expected_filepath = output_parm.eval()
instance.data.setdefault("files", list())
instance.data.setdefault("expectedFiles", list())
if instance.data.get("frames"):
files = self.get_files(instance, expected_filepath)
# list of files
instance.data["files"].extend(files)
else:

frames = instance.data.get("frames", "")
MustafaJafar marked this conversation as resolved.
Show resolved Hide resolved
if isinstance(frames, str):
# single file
instance.data["files"].append(output_parm.eval())
cache_files = {"_": instance.data["files"]}
# Convert instance family to pointcache if it is bgeo or abc
# because ???
self.log.debug(instance.data["families"])
instance.data["files"].append(expected_filepath)
else:
# list of files
staging_dir, _ = os.path.split(expected_filepath)
instance.data["files"].extend(
["{}/{}".format(staging_dir, f) for f in frames]
)

cache_files = {"cache": instance.data["files"]}
BigRoy marked this conversation as resolved.
Show resolved Hide resolved

instance.data.update({
"plugin": "Houdini",
"publish": True
})
instance.data["families"].append("publish.hou")
instance.data["expectedFiles"].append(cache_files)

self.log.debug("{}".format(instance.data))

def get_files(self, instance, output_parm):
"""Get the files with the frame range data

Args:
instance (_type_): instance
output_parm (_type_): path of output parameter

Returns:
files: a list of files
"""
directory = os.path.dirname(output_parm)

files = [
os.path.join(directory, frame).replace("\\", "/")
for frame in instance.data["frames"]
]

return files
self.log.debug("Caching on farm expected files: {}".format(instance.data["expectedFiles"]))
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import os

import clique
import pyblish.api
from ayon_core.pipeline import AYONPyblishPluginMixin
from ayon_houdini.api import plugin


class CollectFilesForCleaningUp(plugin.HoudiniInstancePlugin,
AYONPyblishPluginMixin):
"""Collect Files For Cleaning Up.

This collector collects output files
and adds them to file remove list.

CAUTION:
This collector registers exported files and
the parent folder for deletion in `ExplicitCleanUp` plug-in.
please refer to `ExplicitCleanUp`'s docstring for further info.

Notes:
Artists are free to change the file path in the ROP node.

Farm instances will be processed on farm by other dedicated plugins
that live in core addon e.g. `CollectRenderedFiles` plugin.
These dedicated plugins don't support tracking and removing
intermediated render files.

Local Render instances don't track intermediated render files,
Therefore, this plugin doesn't support removing
intermediate render files.

HDA is not added to this plugin's options in server settings.
Cleaning up HDA products will break the scene as Houdini will no longer
be able to find the HDA file.
In addition,HDA plugins always save HDAs to external files.
Therefore, Cleaning up HDA products will break the ability to go back
to the workfile and continue on the HDA.
"""

# It should run after CollectFrames and Collect Render plugins,
# and before CollectLocalRenderInstances.
order = pyblish.api.CollectorOrder + 0.115

hosts = ["houdini"]
families = ["*"]
label = "Collect Files For Cleaning Up"

def process(self, instance):

if instance.data.get("farm"):
self.log.debug("Should be processed on farm, skipping.")
return

files: list[os.PathLike] = []
staging_dirs: list[os.PathLike] = []
expected_files = instance.data.get("expectedFiles", [])

# Prefer 'expectedFiles' over 'frames' because it usually contains
# more output files than just a single file or single sequence of files.
if expected_files:
# Products with expected files
# This can be Render products or submitted cache to farm.
for expected in expected_files:
# expected.values() is a list of lists
for output_files in expected.values():
staging_dir, _ = os.path.split(output_files[0])
if staging_dir not in staging_dirs:
staging_dirs.append(staging_dir)
files.extend(output_files)
BigRoy marked this conversation as resolved.
Show resolved Hide resolved
else:
# Products with frames or single file.

MustafaJafar marked this conversation as resolved.
Show resolved Hide resolved
frames = instance.data.get("frames")
if frames is None:
self.log.warning(
f"No frames data found on instance {instance}"
". Skipping collection for caching on farm..."
)
return

staging_dir = instance.data.get("stagingDir")
staging_dirs.append(staging_dir)

if isinstance(frames, str):
# single file.
files.append(f"{staging_dir}/{frames}")
else:
# list of frame.
files.extend(
[f"{staging_dir}/{frame}" for frame in frames]
)

self.log.debug(
f"Add directories to 'cleanupEmptyDir': {staging_dirs}")
instance.context.data["cleanupEmptyDirs"].extend(staging_dirs)

self.log.debug("Add files to 'cleanupFullPaths': {}".format(files))
instance.context.data["cleanupFullPaths"].extend(files)

def _get_ifd_file_list(self, ifd_file, start_frame, end_frame):

file_name = os.path.basename(ifd_file)
parent_path = os.path.dirname(ifd_file)

# Compute frames list
frame_collection, _ = clique.assemble(
[file_name],
patterns=[clique.PATTERNS["frames"]],
minimum_items=1
)

# It's always expected to be one collection.
frame_collection = frame_collection[0]
frame_collection.indexes.clear()
frame_collection.indexes.update(
list(range(start_frame, (end_frame + 1))))

return [f"{parent_path}/{frame}" for frame in frame_collection]
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
# -*- coding: utf-8 -*-
"""Collector plugin for frames data on ROP instances."""
import os
import re

import hou # noqa
import clique
import pyblish.api
from ayon_houdini.api import lib, plugin

Expand All @@ -16,86 +15,49 @@ class CollectFrames(plugin.HoudiniInstancePlugin):
order = pyblish.api.CollectorOrder + 0.1
label = "Collect Frames"
families = ["camera", "vdbcache", "imagesequence", "ass",
"redshiftproxy", "review", "pointcache", "fbx"]
"redshiftproxy", "review", "pointcache", "fbx",
"model"]

def process(self, instance):

ropnode = hou.node(instance.data["instance_node"])

start_frame = instance.data.get("frameStartHandle", None)
end_frame = instance.data.get("frameEndHandle", None)
# CollectRopFrameRange computes `start_frame` and `end_frame`
# depending on the trange value.
start_frame = instance.data["frameStartHandle"]
end_frame = instance.data["frameEndHandle"]

# Evaluate the file name at the first frame.
ropnode = hou.node(instance.data["instance_node"])
output_parm = lib.get_output_parameter(ropnode)
if start_frame is not None:
# When rendering only a single frame still explicitly
# get the name for that particular frame instead of current frame
output = output_parm.evalAtFrame(start_frame)
else:
self.log.warning("Using current frame: {}".format(hou.frame()))
output = output_parm.eval()

_, ext = lib.splitext(
output, allowed_multidot_extensions=[
".ass.gz", ".bgeo.sc", ".bgeo.gz",
".bgeo.lzma", ".bgeo.bz2"])
output = output_parm.evalAtFrame(start_frame)
file_name = os.path.basename(output)
result = file_name

# Get the filename pattern match from the output
# path, so we can compute all frames that would
# come out from rendering the ROP node if there
# is a frame pattern in the name
pattern = r"\w+\.(\d+)" + re.escape(ext)
match = re.match(pattern, file_name)

if match and start_frame is not None:

# Check if frames are bigger than 1 (file collection)
# override the result
if end_frame - start_frame > 0:
result = self.create_file_list(
match, int(start_frame), int(end_frame)
)

# todo: `frames` currently conflicts with "explicit frames" for a
# for a custom frame list. So this should be refactored.

instance.data.update({
"frames": result,
"frames": file_name, # Set frames to the file name by default.
"stagingDir": os.path.dirname(output)
})

@staticmethod
def create_file_list(match, start_frame, end_frame):
"""Collect files based on frame range and `regex.match`

Args:
match(re.match): match object
start_frame(int): start of the animation
end_frame(int): end of the animation

Returns:
list

"""

# Get the padding length
frame = match.group(1)
padding = len(frame)

# Get the parts of the filename surrounding the frame number,
# so we can put our own frame numbers in.
span = match.span(1)
prefix = match.string[: span[0]]
suffix = match.string[span[1]:]

# Generate filenames for all frames
result = []
for i in range(start_frame, end_frame + 1):

# Format frame number by the padding amount
str_frame = "{number:0{width}d}".format(number=i, width=padding)

file_name = prefix + str_frame + suffix
result.append(file_name)

return result
# Skip unnecessary logic if start and end frames are equal.
if start_frame == end_frame:
return

# Create collection using frame pattern.
# e.g. 'pointcacheBgeoCache_AB010.1001.bgeo'
# will be <Collection "pointcacheBgeoCache_AB010.%d.bgeo [1001]">
frame_collection, _ = clique.assemble(
[file_name],
patterns=[clique.PATTERNS["frames"]],
minimum_items=1
)
BigRoy marked this conversation as resolved.
Show resolved Hide resolved

# Return as no frame pattern detected.
if not frame_collection:
return

# It's always expected to be one collection.
frame_collection = frame_collection[0]
frame_collection.indexes.clear()
frame_collection.indexes.update(list(range(start_frame, (end_frame + 1))))
instance.data["frames"] = list(frame_collection)
43 changes: 43 additions & 0 deletions server_addon/houdini/server/settings/publish.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,39 @@ class CollectLocalRenderInstancesModel(BaseSettingsModel):
)


def product_types_enum():
return [
{"value": "camera", "label": "Camera (Abc)"},
{"value": "pointcache", "label": "PointCache (Abc)/PointCache (Bgeo)"},
{"value": "review", "label": "Review"},
{"value": "staticMesh", "label": "Static Mesh (FBX)"},
{"value": "usd", "label": "USD (experimental)"},
{"value": "vdbcache", "label": "VDB Cache"},
{"value": "imagesequence", "label": "Composite (Image Sequence)"},
{"value": "ass", "label": "Arnold ASS"},
{"value": "arnold_rop", "label": "Arnold ROP"},
{"value": "mantra_rop", "label": "Mantra ROP"},
{"value": "redshiftproxy", "label": "Redshift Proxy"},
{"value": "redshift_rop", "label": "Redshift ROP"},
{"value": "karma_rop", "label": "Karma ROP"},
{"value": "vray_rop", "label": "VRay ROP"},
{"value": "model", "label": "Model"},
]
MustafaJafar marked this conversation as resolved.
Show resolved Hide resolved


class CollectFilesForCleaningUpModel(BaseSettingsModel):
enabled: bool = SettingsField(title="Enabled")
optional: bool = SettingsField(title="Optional")
active: bool = SettingsField(title="Active")

families: list[str] = SettingsField(
default_factory=list,
enum_resolver=product_types_enum,
conditionalEnum=True,
title="Product Types"
)


class ValidateWorkfilePathsModel(BaseSettingsModel):
enabled: bool = SettingsField(title="Enabled")
optional: bool = SettingsField(title="Optional")
Expand Down Expand Up @@ -74,6 +107,10 @@ class PublishPluginsModel(BaseSettingsModel):
default_factory=CollectChunkSizeModel,
title="Collect Chunk Size"
)
CollectFilesForCleaningUp:CollectFilesForCleaningUpModel = SettingsField(
MustafaJafar marked this conversation as resolved.
Show resolved Hide resolved
default_factory=BasicValidateModel,
title="Collect Files For Cleaning Up."
)
CollectLocalRenderInstances: CollectLocalRenderInstancesModel = SettingsField(
default_factory=CollectLocalRenderInstancesModel,
title="Collect Local Render Instances"
Expand Down Expand Up @@ -113,6 +150,12 @@ class PublishPluginsModel(BaseSettingsModel):
"optional": True,
"chunk_size": 999999
},
"CollectFilesForCleaningUp": {
"enabled": False,
"optional": True,
"active": True,
"families" : []
},
"CollectLocalRenderInstances": {
"use_deadline_aov_filter": False,
"aov_filter": {
Expand Down
Loading