Skip to content

load_imagesequence

Loader for image sequences and single images.

ImageSequenceLoader

Bases: LoaderPlugin

Load single image or image sequence.

Stores the imported product in a container named after the product. Single images use the OpenHarmony importImageFile API; sequences use the ImageSequenceLoader READ-node pipeline.

Source code in client/ayon_harmony/plugins/load/load_imagesequence.py
 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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
class ImageSequenceLoader(load.LoaderPlugin):
    """Load single image or image sequence.

    Stores the imported product in a container named after the product.
    Single images use the OpenHarmony importImageFile API; sequences use
    the ImageSequenceLoader READ-node pipeline.
    """

    label = "Load Image or Sequence"
    product_base_types = {
        "shot",
        "render",
        "image",
        "plate",
        "reference",
        "review",
    }
    product_types = product_base_types
    representations = {"*"}
    extensions = {"jpeg", "png", "jpg"}
    settings_category = "harmony"
    expose_only_current_frame = False

    @staticmethod
    def _resolve_files_for_representation(target_path: Path) -> list[str]:
        """Resolve ordered file paths for the sequence containing the path.

        Picks the clique collection that includes the representation file name.
        """
        parent = target_path.parent
        names = os.listdir(parent.as_posix())
        collections, remainder = clique.assemble(names)
        basename = target_path.name
        for coll in collections:
            if basename in coll:
                return [parent.joinpath(f).as_posix() for f in coll]
        if basename in remainder:
            return [parent.joinpath(basename).as_posix()]
        elif target_path.is_file():
            return [target_path.as_posix()]
        else:
            raise RuntimeError(
                "Could not resolve image files for representation path: "
                f"{target_path}"
            )

    def load(self, context, name=None, namespace=None, data=None):
        """Plugin entry point.

        Args:
            context (:class:`pyblish.api.Context`): Context.
            name (str, optional): Container name.
            namespace (str, optional): Container namespace.
            data (dict, optional): Additional data passed into loader.

        """
        fname = Path(self.filepath_from_context(context))
        self_name = self.__class__.__name__
        files = self._resolve_files_for_representation(fname)
        folder_name = context["folder"]["name"]
        product_name = context["product"]["name"]

        if len(files) == 1:
            # Single image: use ImageLoader JS API (OpenHarmony).
            image_node = harmony.send(
                {
                    "function": "AyonHarmony.importImageFile",
                    "args": [
                        files[0],
                        self.expose_only_current_frame,
                    ],
                }
            )["result"]
            result = harmony.containerise(
                name,
                namespace,
                image_node,
                context,
                self_name,
                nodes=[image_node],
            )
            harmony.imprint(image_node, {"image_mode": "single"})
            return result
        else:
            # Sequence: use ImageSequenceLoader JS API.
            group_id = str(uuid.uuid4())
            read_node = harmony.send(
                {
                    "function": "AyonHarmony.Loaders.ImageSequenceLoader.importFiles",  # noqa: E501
                    "args": [files, folder_name, product_name, 1, group_id],
                }
            )["result"]
            result = harmony.containerise(
                name,
                namespace,
                read_node,
                context,
                self_name,
                nodes=[read_node],
            )
            harmony.imprint(read_node, {"image_mode": "sequence"})
            return result

    def update(self, container, context):
        """Update loaded containers.

        Args:
            container (dict): Container data.
            context (dict): Representation context data.

        """
        if not (nodes := container.get("nodes")):
            return

        node = nodes[-1]
        repre_entity = context["representation"]
        path = Path(self.filepath_from_context(context))

        if container.get("image_mode") == "single":
            harmony.send(
                {
                    "function": "AyonHarmony.replaceImageFile",
                    "args": [node, path.as_posix()],
                }
            )
            harmony.imprint(node, {"representation": repre_entity["id"]})
            return

        files = self._resolve_files_for_representation(path)
        harmony.send(
            {
                "function": "AyonHarmony.Loaders.ImageSequenceLoader.replaceFiles",  # noqa: E501
                "args": [files, node, 1],
            }
        )

        if is_representation_from_latest(repre_entity):
            harmony.send(
                {
                    "function": "AyonHarmony.setColor",
                    "args": [node, [0, 255, 0, 255]],
                }
            )
        else:
            harmony.send(
                {
                    "function": "AyonHarmony.setColor",
                    "args": [node, [255, 0, 0, 255]],
                }
            )

        harmony.imprint(node, {"representation": repre_entity["id"]})

    def remove(self, container):
        """Remove loaded container.

        Args:
            container (dict): Container data.

        """
        if not (nodes := container.get("nodes")):
            return
        node = nodes[-1]
        harmony.imprint(node, {}, remove=True)
        harmony.send({"function": "AyonHarmony.deleteNode", "args": [node]})

    def switch(self, container, context):
        """Switch loaded representations."""
        self.update(container, context)

load(context, name=None, namespace=None, data=None)

Plugin entry point.

Parameters:

Name Type Description Default
context (

class:pyblish.api.Context): Context.

required
name str

Container name.

None
namespace str

Container namespace.

None
data dict

Additional data passed into loader.

None
Source code in client/ayon_harmony/plugins/load/load_imagesequence.py
 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
def load(self, context, name=None, namespace=None, data=None):
    """Plugin entry point.

    Args:
        context (:class:`pyblish.api.Context`): Context.
        name (str, optional): Container name.
        namespace (str, optional): Container namespace.
        data (dict, optional): Additional data passed into loader.

    """
    fname = Path(self.filepath_from_context(context))
    self_name = self.__class__.__name__
    files = self._resolve_files_for_representation(fname)
    folder_name = context["folder"]["name"]
    product_name = context["product"]["name"]

    if len(files) == 1:
        # Single image: use ImageLoader JS API (OpenHarmony).
        image_node = harmony.send(
            {
                "function": "AyonHarmony.importImageFile",
                "args": [
                    files[0],
                    self.expose_only_current_frame,
                ],
            }
        )["result"]
        result = harmony.containerise(
            name,
            namespace,
            image_node,
            context,
            self_name,
            nodes=[image_node],
        )
        harmony.imprint(image_node, {"image_mode": "single"})
        return result
    else:
        # Sequence: use ImageSequenceLoader JS API.
        group_id = str(uuid.uuid4())
        read_node = harmony.send(
            {
                "function": "AyonHarmony.Loaders.ImageSequenceLoader.importFiles",  # noqa: E501
                "args": [files, folder_name, product_name, 1, group_id],
            }
        )["result"]
        result = harmony.containerise(
            name,
            namespace,
            read_node,
            context,
            self_name,
            nodes=[read_node],
        )
        harmony.imprint(read_node, {"image_mode": "sequence"})
        return result

remove(container)

Remove loaded container.

Parameters:

Name Type Description Default
container dict

Container data.

required
Source code in client/ayon_harmony/plugins/load/load_imagesequence.py
167
168
169
170
171
172
173
174
175
176
177
178
def remove(self, container):
    """Remove loaded container.

    Args:
        container (dict): Container data.

    """
    if not (nodes := container.get("nodes")):
        return
    node = nodes[-1]
    harmony.imprint(node, {}, remove=True)
    harmony.send({"function": "AyonHarmony.deleteNode", "args": [node]})

switch(container, context)

Switch loaded representations.

Source code in client/ayon_harmony/plugins/load/load_imagesequence.py
180
181
182
def switch(self, container, context):
    """Switch loaded representations."""
    self.update(container, context)

update(container, context)

Update loaded containers.

Parameters:

Name Type Description Default
container dict

Container data.

required
context dict

Representation context data.

required
Source code in client/ayon_harmony/plugins/load/load_imagesequence.py
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
def update(self, container, context):
    """Update loaded containers.

    Args:
        container (dict): Container data.
        context (dict): Representation context data.

    """
    if not (nodes := container.get("nodes")):
        return

    node = nodes[-1]
    repre_entity = context["representation"]
    path = Path(self.filepath_from_context(context))

    if container.get("image_mode") == "single":
        harmony.send(
            {
                "function": "AyonHarmony.replaceImageFile",
                "args": [node, path.as_posix()],
            }
        )
        harmony.imprint(node, {"representation": repre_entity["id"]})
        return

    files = self._resolve_files_for_representation(path)
    harmony.send(
        {
            "function": "AyonHarmony.Loaders.ImageSequenceLoader.replaceFiles",  # noqa: E501
            "args": [files, node, 1],
        }
    )

    if is_representation_from_latest(repre_entity):
        harmony.send(
            {
                "function": "AyonHarmony.setColor",
                "args": [node, [0, 255, 0, 255]],
            }
        )
    else:
        harmony.send(
            {
                "function": "AyonHarmony.setColor",
                "args": [node, [255, 0, 0, 255]],
            }
        )

    harmony.imprint(node, {"representation": repre_entity["id"]})