Skip to content

validate_sequence_frames

ValidateSequenceFrames

Bases: InstancePlugin, OptionalPyblishPluginMixin

Ensure the sequence of frames is complete

The files found in the folder are checked against the frameStart and frameEnd of the instance. If the first or last file is not corresponding with the first or last frame it is flagged as invalid.

Source code in client/ayon_unreal/plugins/publish/validate_sequence_frames.py
10
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
class ValidateSequenceFrames(pyblish.api.InstancePlugin,
                             OptionalPyblishPluginMixin):
    """Ensure the sequence of frames is complete

    The files found in the folder are checked against the frameStart and
    frameEnd of the instance. If the first or last file is not
    corresponding with the first or last frame it is flagged as invalid.
    """

    order = pyblish.api.ValidatorOrder
    label = "Validate Sequence Frames"
    families = ["render.local"]
    hosts = ["unreal"]
    optional = True

    def process(self, instance):
        if not self.is_active(instance.data):
            self.log.debug("Skipping Validate Frame Range...")
            return

        representations = instance.data.get("representations")

        folder_attributes = (
            instance.data
            .get("folderEntity", {})
            .get("attrib", {})
        )
        for repr in representations:
            repr_files = repr["files"]
            if isinstance(repr_files, str):
                continue

            ext = repr.get("ext")
            if not ext:
                _, ext = os.path.splitext(repr_files[0])
            elif not ext.startswith("."):
                ext = ".{}".format(ext)

            collections, remainder = clique.assemble(
                repr["files"], minimum_items=1,
                patterns=[clique.PATTERNS['frames']])

            if remainder:
                raise PublishValidationError(
                    "Some files have been found outside a sequence. "
                    f"Invalid files: {remainder}")
            if not collections:
                raise PublishValidationError(
                    "We have been unable to find a sequence in the "
                    "files. Please ensure the files are named "
                    "appropriately. "
                    f"Files: {repr_files}")
            if len(collections) > 1:
                raise PublishValidationError(
                    "Multiple collections detected. There should be a single "
                    "collection per representation. "
                    f"Collections identified: {collections}")

            collection = collections[0]
            frames = list(collection.indexes)

            if instance.data.get("slate"):
                # Slate is not part of the frame range
                frames = frames[1:]

            current_range = (frames[0], frames[-1])
            required_range = (folder_attributes.get("clipIn"),
                              folder_attributes.get("clipOut"))

            if current_range != required_range:
                raise PublishValidationError(
                    f"Invalid frame range: {current_range} - "
                    f"expected: {required_range}")

            missing = collection.holes().indexes
            if missing:
                raise PublishValidationError(
                    "Missing frames have been detected. "
                    f"Missing frames: {missing}")