Skip to content

pipeline

UnrealHost

Bases: HostBase, ILoadHost, IPublishHost

Unreal host implementation.

For some time this class will re-use functions from module based implementation for backwards compatibility of older unreal projects.

Source code in client/ayon_unreal/api/pipeline.py
 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
class UnrealHost(HostBase, ILoadHost, IPublishHost):
    """Unreal host implementation.

    For some time this class will re-use functions from module based
    implementation for backwards compatibility of older unreal projects.
    """

    name = "unreal"

    def install(self):
        install()

    def get_containers(self):
        return ls()

    @staticmethod
    def show_tools_popup():
        """Show tools popup with actions leading to show other tools."""
        show_tools_popup()

    @staticmethod
    def show_tools_dialog():
        """Show tools dialog with actions leading to show other tools."""
        show_tools_dialog()

    def update_context_data(self, data, changes):
        content_path = unreal.Paths.project_content_dir()
        op_ctx = content_path + CONTEXT_CONTAINER
        attempts = 3
        for i in range(attempts):
            try:
                with open(op_ctx, "w+") as f:
                    json.dump(data, f)
                break
            except IOError as e:
                if i == attempts - 1:
                    raise Exception(
                        "Failed to write context data. Aborting.") from e
                unreal.log_warning("Failed to write context data. Retrying...")
                i += 1
                time.sleep(3)
                continue

    def get_context_data(self):
        content_path = unreal.Paths.project_content_dir()
        op_ctx = content_path + CONTEXT_CONTAINER
        if not os.path.isfile(op_ctx):
            return {}
        with open(op_ctx, "r") as fp:
            data = json.load(fp)
        return data

show_tools_dialog() staticmethod

Show tools dialog with actions leading to show other tools.

Source code in client/ayon_unreal/api/pipeline.py
74
75
76
77
@staticmethod
def show_tools_dialog():
    """Show tools dialog with actions leading to show other tools."""
    show_tools_dialog()

show_tools_popup() staticmethod

Show tools popup with actions leading to show other tools.

Source code in client/ayon_unreal/api/pipeline.py
69
70
71
72
@staticmethod
def show_tools_popup():
    """Show tools popup with actions leading to show other tools."""
    show_tools_popup()

add_track(sequence, track)

Backward compatibility for deprecated function of add_master_track() in UE 5.5

Parameters:

Name Type Description Default
sequence LevelSequence

Level Sequence

required

Returns:

Name Type Description
MovieSceneTrack

Any tracks inherited from unreal.MovieSceneTrack

Source code in client/ayon_unreal/api/pipeline.py
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
def add_track(sequence, track):
    """Backward compatibility for deprecated function of add_master_track() in UE 5.5

    Args:
        sequence (unreal.LevelSequence): Level Sequence

    Returns:
        MovieSceneTrack: Any tracks inherited from unreal.MovieSceneTrack
    """
    if (
        UNREAL_VERSION.major == 5
        and UNREAL_VERSION.minor > 4
    ):
        return sequence.add_track(track)
    else:
        return sequence.add_master_track(track)

cast_map_to_str_dict(umap)

Cast Unreal Map to dict.

Helper function to cast Unreal Map object to plain old python dict. This will also cast values and keys to str. Useful for metadata dicts.

Parameters:

Name Type Description Default
umap

Unreal Map object

required

Returns:

Type Description
dict

dict

Source code in client/ayon_unreal/api/pipeline.py
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
def cast_map_to_str_dict(umap) -> dict:
    """Cast Unreal Map to dict.

    Helper function to cast Unreal Map object to plain old python
    dict. This will also cast values and keys to str. Useful for
    metadata dicts.

    Args:
        umap: Unreal Map object

    Returns:
        dict

    """
    return {str(key): str(value) for (key, value) in umap.items()}

containerise(name, namespace, nodes, context, loader=None, suffix='_CON')

Bundles nodes (assets) into a container and add metadata to it.

Unreal doesn't support groups of assets that you can add metadata to. But it does support folders that helps to organize asset. Unfortunately those folders are just that - you cannot add any additional information to them. Ayon Integration Plugin is providing way out - Implementing AssetContainer Blueprint class. This class when added to folder can handle metadata on it using standard :func:unreal.EditorAssetLibrary.set_metadata_tag() and :func:unreal.EditorAssetLibrary.get_metadata_tag_values(). It also stores and monitor all changes in assets in path where it resides. List of those assets is available as assets property.

This is list of strings starting with asset type and ending with its path: Material /Game/Ayon/Test/TestMaterial.TestMaterial

Source code in client/ayon_unreal/api/pipeline.py
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
def containerise(name, namespace, nodes, context, loader=None, suffix="_CON"):
    """Bundles *nodes* (assets) into a *container* and add metadata to it.

    Unreal doesn't support *groups* of assets that you can add metadata to.
    But it does support folders that helps to organize asset. Unfortunately
    those folders are just that - you cannot add any additional information
    to them. Ayon Integration Plugin is providing way out - Implementing
    `AssetContainer` Blueprint class. This class when added to folder can
    handle metadata on it using standard
    :func:`unreal.EditorAssetLibrary.set_metadata_tag()` and
    :func:`unreal.EditorAssetLibrary.get_metadata_tag_values()`. It also
    stores and monitor all changes in assets in path where it resides. List of
    those assets is available as `assets` property.

    This is list of strings starting with asset type and ending with its path:
    `Material /Game/Ayon/Test/TestMaterial.TestMaterial`

    """
    # 1 - create directory for container
    root = "/Game"
    container_name = f"{name}{suffix}"
    new_name = move_assets_to_path(root, container_name, nodes)

    # 2 - create Asset Container there
    path = f"{root}/{new_name}"
    create_container(container=container_name, path=path)

    namespace = path

    data = {
        "schema": "ayon:container-2.0",
        "id": AYON_CONTAINER_ID,
        "name": new_name,
        "namespace": namespace,
        "loader": str(loader),
        "representation": context["representation"]["id"],
    }
    # 3 - imprint data
    imprint(f"{path}/{container_name}", data)
    return path

create_container(container, path)

Helper function to create Asset Container class on given path.

This Asset Class helps to mark given path as Container and enable asset version control on it.

Parameters:

Name Type Description Default
container str

Asset Container name

required
path str

Path where to create Asset Container. This path should point into container folder

required

Returns:

Type Description
Object

class:unreal.Object: instance of created asset

Example:

create_container(
    "/Game/modelingFooCharacter_CON",
    "modelingFooCharacter_CON"
)
Source code in client/ayon_unreal/api/pipeline.py
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
def create_container(container: str, path: str) -> unreal.Object:
    """Helper function to create Asset Container class on given path.

    This Asset Class helps to mark given path as Container
    and enable asset version control on it.

    Args:
        container (str): Asset Container name
        path (str): Path where to create Asset Container. This path should
            point into container folder

    Returns:
        :class:`unreal.Object`: instance of created asset

    Example:

        create_container(
            "/Game/modelingFooCharacter_CON",
            "modelingFooCharacter_CON"
        )

    """
    factory = unreal.AyonAssetContainerFactory()
    tools = unreal.AssetToolsHelpers().get_asset_tools()

    return tools.create_asset(container, path, None, factory)

create_folder(root, name)

Create new folder.

If folder exists, append number at the end and try again, incrementing if needed.

Parameters:

Name Type Description Default
root str

path root

required
name str

folder name

required

Returns:

Name Type Description
str str

folder name

Example

create_folder("/Game/Foo") /Game/Foo create_folder("/Game/Foo") /Game/Foo1

Source code in client/ayon_unreal/api/pipeline.py
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
def create_folder(root: str, name: str) -> str:
    """Create new folder.

    If folder exists, append number at the end and try again, incrementing
    if needed.

    Args:
        root (str): path root
        name (str): folder name

    Returns:
        str: folder name

    Example:
        >>> create_folder("/Game/Foo")
        /Game/Foo
        >>> create_folder("/Game/Foo")
        /Game/Foo1

    """
    eal = unreal.EditorAssetLibrary
    index = 1
    while True:
        if eal.does_directory_exist(f"{root}/{name}"):
            name = f"{name}{index}"
            index += 1
        else:
            eal.make_directory(f"{root}/{name}")
            break

    return name

create_publish_instance(instance, path)

Helper function to create Ayon Publish Instance on given path.

This behaves similarly as :func:create_ayon_container.

Parameters:

Name Type Description Default
path str

Path where to create Publish Instance. This path should point into container folder

required
instance str

Publish Instance name

required

Returns:

Type Description
Object

class:unreal.Object: instance of created asset

Example:

create_publish_instance(
    "/Game/modelingFooCharacter_INST",
    "modelingFooCharacter_INST"
)
Source code in client/ayon_unreal/api/pipeline.py
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
def create_publish_instance(instance: str, path: str) -> unreal.Object:
    """Helper function to create Ayon Publish Instance on given path.

    This behaves similarly as :func:`create_ayon_container`.

    Args:
        path (str): Path where to create Publish Instance.
            This path should point into container folder
        instance (str): Publish Instance name

    Returns:
        :class:`unreal.Object`: instance of created asset

    Example:

        create_publish_instance(
            "/Game/modelingFooCharacter_INST",
            "modelingFooCharacter_INST"
        )

    """
    factory = unreal.AyonPublishInstanceFactory()
    tools = unreal.AssetToolsHelpers().get_asset_tools()
    return tools.create_asset(instance, path, None, factory)

find_camera_actors_in_camera_tracks(sequence)

Find the camera actors in the tracks from the Level Sequence

Parameters:

Name Type Description Default
tracks Object

Level Seqence Asset

required

Returns:

Name Type Description
Object list[Any]

Camera Actor

Source code in client/ayon_unreal/api/pipeline.py
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
def find_camera_actors_in_camera_tracks(sequence) -> list[Any]:
    """Find the camera actors in the tracks from the Level Sequence

    Args:
        tracks (Object): Level Seqence Asset

    Returns:
        Object: Camera Actor
    """
    camera_tracks = []
    camera_objects = []
    camera_tracks = get_camera_tracks(sequence)
    if camera_tracks:
        for camera_track in camera_tracks:
            sections = camera_track.get_sections()
            for section in sections:
                binding_id = section.get_camera_binding_id()
                bound_objects = unreal.LevelSequenceEditorBlueprintLibrary.get_bound_objects(
                    binding_id)
                for camera_object in bound_objects:
                    camera_objects.append(camera_object.get_path_name())
    world =  unreal.EditorLevelLibrary.get_editor_world()
    sel_actors = unreal.GameplayStatics().get_all_actors_of_class(
        world, unreal.CameraActor)
    actors = [a for a in sel_actors if a.get_path_name() in camera_objects]
    return actors

format_asset_directory(context, directory_template)

Setting up the asset directory path and name. Args: name (str): Instance name context (dict): context directory_template (str): directory template path extension (str, optional): file extension. Defaults to "abc". Returns: tuple[str, str]: asset directory, asset name

Source code in client/ayon_unreal/api/pipeline.py
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
def format_asset_directory(context, directory_template):
    """Setting up the asset directory path and name.
    Args:
        name (str): Instance name
        context (dict): context
        directory_template (str): directory template path
        extension (str, optional): file extension. Defaults to "abc".
    Returns:
        tuple[str, str]: asset directory, asset name
    """

    data = copy.deepcopy(context)
    if "{product[type]}" in directory_template:
        unreal.warning(
            "Deprecated settings: AYON is using settings "
            "that won't work in future releases. "
            "Details: {product[type]} in the template should "
            "be replaced with {product[productType]}."
        )
        directory_template = directory_template.replace(
            "{product[type]}", "{product[productType]}")

    if "{folder[type]}" in directory_template:
        unreal.warning(
            "Deprecated settings: AYON is using settings "
            "that won't work in future releases. "
            "Details: {folder[type]} in the template should "
            "be replaced with {folder[folderType]}."
        )
        directory_template = directory_template.replace(
            "{folder[type]}", "{folder[folderType]}")

    version = data["version"]["version"]

    # if user set {version[version]},
    # the copied data from data["version"]["version"] convert
    # to set the version of the exclusive version folder
    if version < 0:
        data["version"]["version"] = "hero"
    else:
        data["version"]["version"] = f"v{version:03d}"
    asset_name_with_version = set_asset_name(data)
    asset_dir = StringTemplate(directory_template).format_strict(data)
    return f"{AYON_ROOT_DIR}/{asset_dir}", asset_name_with_version

get_camera_tracks(sequence)

Get the list of movie scene camera cut tracks in the level sequence

Parameters:

Name Type Description Default
sequence Object

Level Sequence

required

Returns:

Name Type Description
list

list of movie scene camera cut tracks

Source code in client/ayon_unreal/api/pipeline.py
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
def get_camera_tracks(sequence):
    """Get the list of movie scene camera cut tracks in the level sequence

    Args:
        sequence (Object): Level Sequence

    Returns:
        list: list of movie scene camera cut tracks
    """
    camera_tracks = []
    tracks = get_tracks(sequence)
    for track in tracks:
        if str(track).count("MovieSceneCameraCutTrack"):
            camera_tracks.append(track)
    return camera_tracks

get_frame_range(sequence)

Get the Clip in/out value from the camera tracks located inside the level sequence

Parameters:

Name Type Description Default
sequence Object

Level Sequence

required

Returns:

Type Description

int32, int32 : Start Frame, End Frame

Source code in client/ayon_unreal/api/pipeline.py
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
def get_frame_range(sequence):
    """Get the Clip in/out value from the camera tracks located inside
    the level sequence

    Args:
        sequence (Object): Level Sequence

    Returns:
        int32, int32 : Start Frame, End Frame
    """
    camera_tracks = get_camera_tracks(sequence)
    if not camera_tracks:
        return sequence.get_playback_start(), sequence.get_playback_end()
    for camera_track in camera_tracks:
        sections = camera_track.get_sections()
        for section in sections:
            return section.get_start_frame(), section.get_end_frame()

get_frame_range_from_folder_attributes(folder_entity=None)

Get the current clip In/Out value Args: folder_entity (dict): folder Entity.

Returns:

Type Description

int, int: clipIn, clipOut.

Source code in client/ayon_unreal/api/pipeline.py
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
def get_frame_range_from_folder_attributes(folder_entity=None):
    """Get the current clip In/Out value
    Args:
        folder_entity (dict): folder Entity.

    Returns:
        int, int: clipIn, clipOut.
    """
    if folder_entity is None:
        folder_entity = get_current_folder_entity(fields={"attrib"})
    folder_attributes = folder_entity["attrib"]
    frame_start = (
        int(folder_attributes.get("frameStart"))
        if folder_attributes.get("frameStart") else 1
    )
    frame_end = (
        int(folder_attributes.get("frameEnd"))
        if folder_attributes.get("frameEnd") else 1
    )
    return frame_start, frame_end

get_sequence(files)

Get sequence from filename.

This will only return files if they exist on disk as it tries to collect the sequence using the filename pattern and searching for them on disk.

Supports negative frame ranges like -001, 0000, 0001 and -0001, 0000, 0001.

Parameters:

Name Type Description Default
files str

List of files

required

Returns:

Type Description

Optional[list[str]]: file sequence.

Source code in client/ayon_unreal/api/pipeline.py
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
def get_sequence(files):
    """Get sequence from filename.

    This will only return files if they exist on disk as it tries
    to collect the sequence using the filename pattern and searching
    for them on disk.

    Supports negative frame ranges like -001, 0000, 0001 and -0001,
    0000, 0001.

    Arguments:
        files (str): List of files

    Returns:
        Optional[list[str]]: file sequence.

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

    if len(collections) > 1:
        raise ValueError(
            f"Multiple collections found for {collections}. "
            "This is a bug.")

    return [os.path.basename(filename) for filename in collections[0]]

get_subsequences(sequence)

Get list of subsequences from sequence.

Parameters:

Name Type Description Default
sequence LevelSequence

Sequence

required

Returns:

Name Type Description
list LevelSequence

List of subsequences

Source code in client/ayon_unreal/api/pipeline.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
def get_subsequences(sequence: unreal.LevelSequence):
    """Get list of subsequences from sequence.

    Args:
        sequence (unreal.LevelSequence): Sequence

    Returns:
        list(unreal.LevelSequence): List of subsequences

    """
    tracks = get_tracks(sequence)
    subscene_track = next(
        (
            t
            for t in tracks
            if t.get_class() == unreal.MovieSceneSubTrack.static_class()
        ),
        None,
    )
    if subscene_track is not None and subscene_track.get_sections():
        return subscene_track.get_sections()
    return []

get_top_hierarchy_folder(path)

Get top hierarchy of the path

Parameters:

Name Type Description Default
path str

path

required

Returns:

Name Type Description
str

top hierarchy directory

Source code in client/ayon_unreal/api/pipeline.py
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
def get_top_hierarchy_folder(path):
    """Get top hierarchy of the path

    Args:
        path (str): path

    Returns:
        str: top hierarchy directory
    """
    # Split the path by the directory separator '/'
    path = path.replace(f"{AYON_ROOT_DIR}/", "")
    # Return the first part
    parts = [part for part in path.split('/') if part]
    return parts[0]

get_tracks(sequence)

Backward compatibility for deprecated function of get_master_tracks() in UE 5.5

Parameters:

Name Type Description Default
sequence LevelSequence

Level Sequence

required

Returns:

Name Type Description
Array MovieSceneTracks

Movie scene tracks

Source code in client/ayon_unreal/api/pipeline.py
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
def get_tracks(sequence):
    """Backward compatibility for deprecated function of get_master_tracks() in UE 5.5

    Args:
        sequence (unreal.LevelSequence): Level Sequence

    Returns:
        Array(MovieSceneTracks): Movie scene tracks
    """
    if (
        UNREAL_VERSION.major == 5
        and UNREAL_VERSION.minor > 4
    ):
        return sequence.get_tracks()
    else:
        return sequence.get_master_tracks()

has_asset_existing_directory(asset_name, asset_dir)

Check if the asset already existed Args: asset_name (str): asset name

Returns:

Name Type Description
str

package path

Source code in client/ayon_unreal/api/pipeline.py
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
def has_asset_existing_directory(asset_name, asset_dir):
    """Check if the asset already existed
    Args:
        asset_name (str): asset name

    Returns:
        str: package path
    """
    asset_registry = unreal.AssetRegistryHelpers.get_asset_registry()
    all_assets = asset_registry.get_assets_by_path('/Game', recursive=True)
    for game_asset in all_assets:
        if game_asset.asset_name == asset_name:
            asset_path = game_asset.get_asset().get_path_name()
            existing_asset_dir = unreal.Paths.split(asset_path)[0]
            existing_version_folder = existing_asset_dir.split("/")[-1]
            existing_asset_dir = existing_asset_dir.replace(existing_version_folder, "")
            if existing_asset_dir != asset_dir:
                return asset_path
    return None

install()

Install Unreal configuration for AYON.

Source code in client/ayon_unreal/api/pipeline.py
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
def install():
    """Install Unreal configuration for AYON."""
    print("-=" * 40)
    logo = '''.
.
                    ·

                   ·∙/
                 ·-∙•∙-·
              / \\  /∙·  / \\
\\  │  /   ∙
              \\   \\ · /   /
              \\\\   ∙ ∙  //
                \\\\/   \\//
                   ___
                  │   │
                  │   │
                  │   │
                  │___│


         ·-─═─-∙ A Y O N ∙-─═─-·
                by  YNPUT
.
'''
    print(logo)
    print("installing Ayon for Unreal ...")
    print("-=" * 40)
    logger.info("installing Ayon for Unreal")
    pyblish.api.register_host("unreal")
    pyblish.api.register_plugin_path(str(PUBLISH_PATH))
    register_loader_plugin_path(str(LOAD_PATH))
    register_creator_plugin_path(str(CREATE_PATH))
    register_inventory_action_path(str(INVENTORY_PATH))
    _register_callbacks()
    _register_events()

instantiate(root, name, data, assets=None, suffix='_INS')

Bundles nodes into container.

Marking it with metadata as publishable instance. If assets are provided, they are moved to new path where AyonPublishInstance class asset is created and imprinted with metadata.

This can then be collected for publishing by Pyblish for example.

Parameters:

Name Type Description Default
root str

root path where to create instance container

required
name str

name of the container

required
data dict

data to imprint on container

required
assets list of str

list of asset paths to include in publish instance

None
suffix str

suffix string to append to instance name

'_INS'
Source code in client/ayon_unreal/api/pipeline.py
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
def instantiate(root, name, data, assets=None, suffix="_INS"):
    """Bundles *nodes* into *container*.

    Marking it with metadata as publishable instance. If assets are provided,
    they are moved to new path where `AyonPublishInstance` class asset is
    created and imprinted with metadata.

    This can then be collected for publishing by Pyblish for example.

    Args:
        root (str): root path where to create instance container
        name (str): name of the container
        data (dict): data to imprint on container
        assets (list of str): list of asset paths to include in publish
                              instance
        suffix (str): suffix string to append to instance name

    """
    container_name = f"{name}{suffix}"

    # if we specify assets, create new folder and move them there. If not,
    # just create empty folder
    if assets:
        new_name = move_assets_to_path(root, container_name, assets)
    else:
        new_name = create_folder(root, name)

    path = f"{root}/{new_name}"
    create_publish_instance(instance=container_name, path=path)

    imprint(f"{path}/{container_name}", data)

ls()

List all containers.

List all found in Content Manager of Unreal and return metadata from them. Adding objectName to set.

Source code in client/ayon_unreal/api/pipeline.py
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
def ls():
    """List all containers.

    List all found in *Content Manager* of Unreal and return
    metadata from them. Adding `objectName` to set.

    """
    ar = unreal.AssetRegistryHelpers.get_asset_registry()
    # UE 5.1 changed how class name is specified
    class_name = ["/Script/Ayon", "AyonAssetContainer"] if UNREAL_VERSION.major == 5 and UNREAL_VERSION.minor > 0 else "AyonAssetContainer"  # noqa
    ayon_containers = ar.get_assets_by_class(class_name, True)

    # get_asset_by_class returns AssetData. To get all metadata we need to
    # load asset. get_tag_values() work only on metadata registered in
    # Asset Registry Project settings (and there is no way to set it with
    # python short of editing ini configuration file).
    for asset_data in ayon_containers:
        asset = asset_data.get_asset()
        data = unreal.EditorAssetLibrary.get_metadata_tag_values(asset)
        data["objectName"] = asset_data.asset_name
        yield cast_map_to_str_dict(data)

maintained_selection()

Stub to be either implemented or replaced.

This is needed for old publisher implementation, but it is not supported (yet) in UE.

Source code in client/ayon_unreal/api/pipeline.py
814
815
816
817
818
819
820
821
822
823
824
@contextmanager
def maintained_selection():
    """Stub to be either implemented or replaced.

    This is needed for old publisher implementation, but
    it is not supported (yet) in UE.
    """
    try:
        yield
    finally:
        pass

move_assets_to_path(root, name, assets)

Moving (renaming) list of asset paths to new destination.

Parameters:

Name Type Description Default
root str

root of the path (eg. /Game)

required
name str

name of destination directory (eg. Foo )

required
assets list of str

list of asset paths

required

Returns:

Name Type Description
str str

folder name

Example

This will get paths of all assets under /Game/Test and move them to /Game/NewTest. If /Game/NewTest already exists, then resulting path will be /Game/NewTest1

assets = unreal.EditorAssetLibrary.list_assets("/Game/Test") move_assets_to_path("/Game", "NewTest", assets) NewTest

Source code in client/ayon_unreal/api/pipeline.py
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
def move_assets_to_path(root: str, name: str, assets: List[str]) -> str:
    """Moving (renaming) list of asset paths to new destination.

    Args:
        root (str): root of the path (eg. `/Game`)
        name (str): name of destination directory (eg. `Foo` )
        assets (list of str): list of asset paths

    Returns:
        str: folder name

    Example:
        This will get paths of all assets under `/Game/Test` and move them
        to `/Game/NewTest`. If `/Game/NewTest` already exists, then resulting
        path will be `/Game/NewTest1`

        >>> assets = unreal.EditorAssetLibrary.list_assets("/Game/Test")
        >>> move_assets_to_path("/Game", "NewTest", assets)
        NewTest

    """
    eal = unreal.EditorAssetLibrary
    name = create_folder(root, name)

    unreal.log(assets)
    for asset in assets:
        loaded = eal.load_asset(asset)
        eal.rename_asset(asset, f"{root}/{name}/{loaded.get_name()}")

    return name

parse_container(container)

To get data from container, AyonAssetContainer must be loaded.

Parameters:

Name Type Description Default
container(str)

path to container

required

Returns:

Name Type Description
dict

metadata stored on container

Source code in client/ayon_unreal/api/pipeline.py
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
def parse_container(container):
    """To get data from container, AyonAssetContainer must be loaded.

    Args:
        container(str): path to container

    Returns:
        dict: metadata stored on container
    """
    asset = unreal.EditorAssetLibrary.load_asset(container)
    data = unreal.EditorAssetLibrary.get_metadata_tag_values(asset)
    data["objectName"] = asset.get_name()
    data = cast_map_to_str_dict(data)

    return data

publish()

Shorthand to publish from within host.

Source code in client/ayon_unreal/api/pipeline.py
230
231
232
233
234
def publish():
    """Shorthand to publish from within host."""
    import pyblish.util

    return pyblish.util.publish()

select_camera(sequence)

Select camera during context Args: sequence (Objects): Level Sequence Object

Source code in client/ayon_unreal/api/pipeline.py
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
@contextmanager
def select_camera(sequence):
    """Select camera during context
    Args:
        sequence (Objects): Level Sequence Object
    """
    camera_actors = find_camera_actors_in_camera_tracks(sequence)
    actor_subsys = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
    selected_actors = actor_subsys.get_selected_level_actors()
    actor_subsys.select_nothing()
    for actor in camera_actors:
        actor_subsys.set_actor_selection_state(actor, True)
    try:
        yield
    finally:
        for actor in camera_actors:
            if actor in selected_actors:
                actor_subsys.set_actor_selection_state(actor, True)
            else:
                actor_subsys.set_actor_selection_state(actor, False)

set_asset_name(data)

Set the name of the asset during loading

Parameters:

Name Type Description Default
folder_name str

folder name

required
name str

instance name

required
extension str

extension

required

Returns:

Name Type Description
str

asset name

Source code in client/ayon_unreal/api/pipeline.py
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
def set_asset_name(data):
    """Set the name of the asset during loading

    Args:
        folder_name (str): folder name
        name (str): instance name
        extension (str): extension

    Returns:
        str: asset name
    """
    asset_name = None,
    name = data["product"]["name"]
    version = data["version"]["version"]
    folder_name = data["folder"]["name"]
    extension = data["representation"]["name"]
    if not extension:
        asset_name = name
    elif folder_name:
        asset_name = "{}_{}_{}_{}".format(
            folder_name, name, version, extension)
    else:
        asset_name = "{}_{}_{}".format(name, version, extension)
    return asset_name

show_tools_dialog()

Show dialog with tools.

Dialog will stay visible.

Source code in client/ayon_unreal/api/pipeline.py
339
340
341
342
343
344
345
346
def show_tools_dialog():
    """Show dialog with tools.

    Dialog will stay visible.
    """
    from ayon_unreal.api import tools_ui

    tools_ui.show_tools_dialog()

show_tools_popup()

Show popup with tools.

Popup will disappear on click or losing focus.

Source code in client/ayon_unreal/api/pipeline.py
329
330
331
332
333
334
335
336
def show_tools_popup():
    """Show popup with tools.

    Popup will disappear on click or losing focus.
    """
    from ayon_unreal.api import tools_ui

    tools_ui.show_tools_popup()

uninstall()

Uninstall Unreal configuration for Ayon.

Source code in client/ayon_unreal/api/pipeline.py
145
146
147
148
149
150
def uninstall():
    """Uninstall Unreal configuration for Ayon."""
    pyblish.api.deregister_plugin_path(str(PUBLISH_PATH))
    deregister_loader_plugin_path(str(LOAD_PATH))
    deregister_creator_plugin_path(str(CREATE_PATH))
    deregister_inventory_action_path(str(INVENTORY_PATH))