Skip to content

validate_frame_token_usd_render

ValidateFrameTokenUSDRender

Bases: OptionalPyblishPluginMixin, HoudiniInstancePlugin

Validate if the unexpanded string contains a frame token (e.g. '$F' or '@Frame').

This validator will only check the output parameter of the node if the "Use Custom Frames" attribute is set.

Rules

If "Use Custom Frames" attribute is set in publisher it is mandatory to have the frame token - '$F4' or similar - to ensure that each frame gets written. If this is not the case you may face black render as the output USD file may not cover the whole range.

Examples:

Good: 'render.$F4.usd' Bad: 'render.usd'

Source code in client/ayon_deadline/plugins/publish/houdini/validate_frame_token_usd_render.py
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
class ValidateFrameTokenUSDRender(
    OptionalPyblishPluginMixin,
    plugin.HoudiniInstancePlugin
):
    """Validate if the unexpanded string contains a frame
        token (e.g. '$F' or '@Frame').

    This validator will *only* check the output parameter of the node if
    the "Use Custom Frames" attribute is set.

    Rules:
        If "Use Custom Frames" attribute is set in publisher
        it is mandatory to have the frame token - '$F4' or similar -
        to ensure that each frame gets written.
        If this is not the case you may face black render
        as the output USD file may not cover the whole range.

    Examples:
        Good: '__render__.$F4.usd'
        Bad: '__render__.usd'

    """

    order = pyblish.api.ValidatorOrder
    label = "Validate Frame Token (USD Render)"
    families = ["usdrender"]
    targets = ["local"]
    hosts = ["houdini"]
    optional = True
    actions = [FixParameterAction, SelectInvalidAction]
    settings_category = "deadline"

    def process(self, instance):
        if not self.is_active(instance.data):
            return

        if not instance.data.get("farm"):
            self.log.debug("Not a farm instance, skipping.")
            return

        if "deadline.submit.publish.job" not in instance.data["families"]:
            self.log.debug(
                "Should only checked if submitting to deadline, skipping.")
            return

        job_info = instance.data["deadline"]["job_info"]

        if job_info.Frames is None:
            self.log.debug(
                "Should only checked if deadline job has custom frames,"
                " skipping."
            )
            return

        invalid = self.get_invalid(instance)
        if invalid:
            raise PublishValidationError(
                "No frame token found in the USD Export Output File parm in"
                f" '{invalid[0].path()}'"
            )

    @classmethod
    def get_invalid(cls, instance):

        node = hou.node(instance.data["instance_node"])
        output_parm = node.parm("lopoutput")
        outparm_keyframs = output_parm.keyframes()
        if outparm_keyframs:
            cls.log.warning(
                "Skipping validation: 'lopoutput' parm on "
                f"{node.path()} is animated."
            )
            return

        unexpanded_str = output_parm.unexpandedString()
        if (
            "$F" not in unexpanded_str
            and
            "@Frame" not in unexpanded_str
        ):
            return [node]

    @classmethod
    def repair(cls, instance):
        if not cls.get_invalid(instance):
            # Already fixed
            return

        rop_node = hou.node(instance.data["instance_node"])
        lopoutput = rop_node.parm("lopoutput").unexpandedString()
        path, ext = os.path.splitext(lopoutput)
        rop_node.parm("lopoutput").set(f"{path}.$F4{ext}")