Python Module Example

The Metadata code examples as one big module-style example

import unreal

# metadata constants
# NOTE: metadata needs to be registered in the Asset Registry
#       in order to work correctly
META_MANAGED_ASSET = "managed_asset"
META_ASSET_NAME = "asset_name"
META_ASSET_VERSION = "asset_version"

# mapping our metadata
METADATA_TYPE_MAP = {
    META_MANAGED_ASSET : bool,
    META_ASSET_VERSION : int
}

# the Asset Registry
asset_registry_helper = unreal.AssetRegistryHelpers()
asset_registry        = asset_registry_helper.get_asset_registry()


# If the C++ function was exposed we can declare our metadata tags from Python
if hasattr(unreal, "PythonUtilsLibrary"):
    unreal.PythonUtilsLibrary.register_metadata_tags(
        [META_MANAGED_ASSET, META_ASSET_NAME , META_ASSET_VERSION]
    )


def set_metadata(asset, key, value):
    """set the asset's metadata value"""
    unreal.EditorAssetLibrary.set_metadata_tag(asset, key, str(value))
    
    
def get_metadata(asset, key):
    """Get the loaded object or AssetData's metadata as its expected type"""
    # Get the unreal metadata value
    if isinstance(asset, unreal.AssetData):
        value = asset.get_tag_value(key)
    else:
        value = unreal.EditorAssetLibrary.get_metadata_tag(asset, key)
    
    # If the metadata was found & valid let's convert it!
    if value and value.lower() != "none":
        # Get this metadata key's expected value type, default is str (as-is)
        value_type = METADATA_TYPE_MAP.get(key, str)

        if value_type == bool:
            # bools are a special case as bool(str) only checks for length
            return value.lower() == "true"
        else:
            # most singular value types may be directly converted
            return value_type(value)


def find_assets_by_metadata(meta_dict, class_names=["object"], parent_path = ""):
    """
    Find assets in the Content Browser based on 
    a given dict of metadata {key:value} pairs
    
    use 'parent_path' to only return results that live under that asset path
    """

    # create a basic Asset Registry filter based on the class_names
    # include the parent path if provided
    base_filter = unreal.ARFilter(
        class_names=class_names,
        recursive_classes=True,
        package_paths=[parent_path] if parent_path else [],
        recursive_paths=True,
    )
    
    # Start with the first Metadata Query if provided
    first_filter = base_filter
    if meta_dict:
        first_key = list(meta_dict.keys())[0]
        first_value = meta_dict.pop(first_key)
        query = [unreal.TagAndValue(first_key, str(first_value))]
        first_filter = asset_registry_helper.set_filter_tags_and_values(base_filter, query)

    # Get the initial class-based search results
    results = asset_registry.get_assets(first_filter) or []

    for key, value in meta_dict.items():
        # Create a new ARFilter based on the {key:value} pair
        query = [unreal.TagAndValue(key, str(value))]
        meta_filter = asset_registry_helper.set_filter_tags_and_values(base_filter, query)

        # reduce the results to only those matching the given metadata
        results = asset_registry.run_assets_through_filter(results, meta_filter) or []

    # return the results as a Python list as it might currently be an unreal.Array
    return list(results)

Last updated