Skip to content

extract_last_published

ExtractLastPublished

Bases: HoudiniExtractorPlugin

Extractor copying files from last published to staging directory.

It works only if instance data includes "last_version_published_files" and there are frames to fix.

The files from last published are based on files which will be extended/fixed for specific frames.

NOTE

This plugin is closely taken from ayon-nuke. It contains some Houdini addon specific logic as various addons may have unique methods for managing staging_dir, expectedFiles and frames.

TODO: It's preferable to to generalize this plugin for broader use and integrate it into ayon-core.

Source code in client/ayon_houdini/plugins/publish/extract_last_published.py
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 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
115
116
117
118
119
120
121
122
123
124
125
126
class ExtractLastPublished(plugin.HoudiniExtractorPlugin):
    """Extractor copying files from last published to staging directory.

    It works only if instance data includes "last_version_published_files"
    and there are frames to fix.

    The files from last published are based on files which will be
    extended/fixed for specific frames.

    NOTE:
        This plugin is closely taken from ayon-nuke.
        It contains some Houdini addon specific logic as various addons may
          have unique methods for managing `staging_dir`, `expectedFiles`
          and `frames`.
    TODO:
        It's preferable to to generalize this plugin for broader use and
          integrate it into ayon-core.
    """

    order = pyblish.api.ExtractorOrder - 0.1
    label = "Extract Last Published"
    targets = ["local"]  # Same target as `CollectFramesFixDef`
    families = ["*"]

    def process(self, instance):
        frames_to_fix = instance.data.get("frames_to_fix")
        if not frames_to_fix:
            self.log.debug("Skipping, No frames to fix.")
            return

        if not instance.data.get("integrate", True):
            self.log.debug("Skipping collecting frames to fix data for "
                           "instance because instance is set to not integrate")
            return

        last_published = instance.data.get("last_version_published_files")
        if not last_published:
            self.log.debug("Skipping, No last publish found.")
            return

        last_published_and_frames = collect_frames(last_published)
        if not all(last_published_and_frames.values()):
            self.log.debug("Skipping, No file sequence found in the "
                           "last version published files.")
            return

        staging_dir, expected_filenames = (
            self.get_expected_files_and_staging_dir(instance)
        )

        os.makedirs(staging_dir, exist_ok=True)

        expected_and_frames = collect_frames(expected_filenames)
        frames_and_expected = {v: k for k, v in expected_and_frames.items()}
        frames_to_fix = clique.parse(frames_to_fix, "{ranges}")

        anatomy = instance.context.data["anatomy"]

        # TODO: This currently copies ALL frames from the last version instead
        #  of only those within the frame range we're currently looking to
        #  publish. It should instead, iterate over all expected frames for
        #  current instance, exclude all "to fix" frames and copy the
        #  other existing ones.
        for file_path, frame in last_published_and_frames.items():
            if frame is None:
                continue
            file_path = anatomy.fill_root(file_path)
            if not os.path.exists(file_path):
                continue
            target_file_name = frames_and_expected.get(frame)
            if not target_file_name:
                continue

            out_path = os.path.join(staging_dir, target_file_name)

            # Copy only the frames that we won't render.
            if frame and frame not in frames_to_fix:
                self.log.debug(f"Copying '{file_path}' -> '{out_path}'")
                shutil.copy(file_path, out_path)

    def get_expected_files_and_staging_dir(self, instance):
        """Get expected file names or frames.

        This method includes Houdini specific code.

        Args:
            instance (pyblish.api.Instance): The instance to publish.

        Returns:
            tuple[str, list[str]]: A 2-tuple of staging dir and the list of
                expected frames for the current publish instance.
        """
        expected_filenames = []
        staging_dir = instance.data.get("stagingDir")
        expected_files = instance.data.get("expectedFiles", [])

        # 'expectedFiles' are preferred over 'frames'
        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
                expected_filenames.extend(sum(expected.values(), []))
        else:
            # Products with frames or single file.
            frames = instance.data.get("frames", "")
            if isinstance(frames, str):
                # single file.
                expected_filenames.append("{}/{}".format(staging_dir, frames))
            else:
                # list of frame.
                expected_filenames.extend(
                    ["{}/{}".format(staging_dir, f) for f in frames]
                )

        return staging_dir, expected_filenames

get_expected_files_and_staging_dir(instance)

Get expected file names or frames.

This method includes Houdini specific code.

Parameters:

Name Type Description Default
instance Instance

The instance to publish.

required

Returns:

Type Description

tuple[str, list[str]]: A 2-tuple of staging dir and the list of expected frames for the current publish instance.

Source code in client/ayon_houdini/plugins/publish/extract_last_published.py
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
def get_expected_files_and_staging_dir(self, instance):
    """Get expected file names or frames.

    This method includes Houdini specific code.

    Args:
        instance (pyblish.api.Instance): The instance to publish.

    Returns:
        tuple[str, list[str]]: A 2-tuple of staging dir and the list of
            expected frames for the current publish instance.
    """
    expected_filenames = []
    staging_dir = instance.data.get("stagingDir")
    expected_files = instance.data.get("expectedFiles", [])

    # 'expectedFiles' are preferred over 'frames'
    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
            expected_filenames.extend(sum(expected.values(), []))
    else:
        # Products with frames or single file.
        frames = instance.data.get("frames", "")
        if isinstance(frames, str):
            # single file.
            expected_filenames.append("{}/{}".format(staging_dir, frames))
        else:
            # list of frame.
            expected_filenames.extend(
                ["{}/{}".format(staging_dir, f) for f in frames]
            )

    return staging_dir, expected_filenames