API

smartElements provides an Application Programming Interface (API) which lets you communicate with smartElements from the outside. This lets you for example query stacks, lists, settings and elements and manipulate them, add new entries, update and delete them and ingest new elements - everything outside of smartElements and outside of Nuke. This lets you now to perform automations of various kinds without the need to use smartElement’s graphical user interface (GUI).

The api module can be found here: <path to smartElements_vX.X.X>/cpXX/smartElements/api.py

Where vX.X.X is your current smartElements version and cpXX is the Python version that your currently used Nuke version uses.

Note

The API works closely with your local smartElements settings that can be found in ~/.cragl/smartElements/settings.json. This is by design and ensures your api calls stay persistent with your configuration. As an example, calling api.get_stacks() will return all stacks that are currently saved in your settings. Adding a new stack using api.add_stack() will then not only create a new stack on disk, but also add it to your settings so it is available for you – also the next time you launch smartElements in an interactive Nuke session.

Getting started

To use smartElement’s API make sure the absolute path of your smartElements installation is in your sys.path. To do that launch a python session in a terminal or your IDE and enter the following:

>>> import sys
>>> sys.path.append("path/to/smartElements_vX.X.X/cpXX")

Where path/to/smartElements_vX.X.X is the absolute path to your smartElements installation and cpXX is the Python version you use.

Once smartElements is in your sys.path, you can import the smartElements API simply like so:

>>> from smartElements import api

If you don’t get any error message then you are good to go.

To get an overview about all available functions, classes and methods that the smartElements API offers, perform a dir on the api:

>>> from smartElements import api
>>> dir(api)

['Element', 'Favorites', 'LEVEL', 'LOGGER', 'Settings', '__builtins__',
'__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__',
'__spec__', 'add_list', 'add_stack', 'constants', 'get_all_elements_in_list',
'get_all_elements_in_stack', 'get_element', 'get_element_path', 'get_elements_by_tags',
'get_list_path', 'get_lists', 'get_settings', 'get_stack_path', 'get_stacks',
'getpass', 'ingest', 'json', 'logging', 'os', 'paths', 'platform',
'processor', 're', 'remove_stack', 'save_setting', 'scan_for_media',
'shutil', 'time']

Working with stacks and lists

The following provides API functions to add new stacks, show the currently configured stacks, get all lists inside a stack, etc.

get_stacks()

Show absolute paths of all currently configured stacks.

>>> from smartElements import api

>>> api.get_stacks()
["stackA", "stackB", "stackC"]

>>> api.get_stacks(absolute_paths=True)
["path/to/my/stackA", "path/to/my/stackB", "path/to/my/stackC"]

get_stack_path()

Get the absolute path of the given stack name.

>>> from smartElements import api

>>> api.get_stack_path("stackA")
"path/to/my/stackA"

# The stack needs to be added to the configuration, otherwise this raises
# a ValueError with a hint about existing stacks:
>>> api.get_stack_path("non-existing-stack")
ValueError: No such stack 'non-existing-stack' found. Choose from: ['stackA', 'stackB', 'stackC'] or add new stack using smartElements.api.add_stack.

get_lists(stack_name, absolute_paths=False)

Get all list names or absolute paths of given stack.

>>> from smartElements import api

>>> api.get_lists("stackA")
["listA", "listB", "listC"]

>>> api.get_lists("stackA", absolute_paths=True)
["path/to/my/stackA/listA", "path/to/my/stackA/listB", "path/to/my/stackA/listC"]

get_list_path(stack_name, list_name)

Get the absolute path of the given list name in given stack name.

>>> from smartElements import api

>>> api.get_list_path("stackA", "listA")
"path/to/my/stackA/listA"

# The list needs to exist, otherwise this raises a ValueError with
# a hint about existing lists:
>>> api.get_list_path("stackA", "non-existing-list")
ValueError: No such list 'non-existing-list' found in stack 'stackA'. Choose from: ['list1', 'list2', 'list3']. You can add a new list using smartElements.api.add_list.

get_element_path(stack_name, list_name, element_name)

Get the absolute path of the element in the given stack, list, name.

>>> from smartElements import api

>>> api.get_element_path("stackA", "listA", "elementA")
"path/to/my/stackA/listA/elementA"

# The element needs to exist, otherwise this raises a ValueError with
# a hint about existing elements:
>>> api.get_element_path("stackA", "listA", "non-existing-element")
No such element 'non-existing-element' found in list 'listA' in stack 'stackA'. Choose from these element names: ['elementA', 'elementB', 'elementC']. Use smartElements.api.ingest to ingest a new element.

add_stack(stack_path)

Add the given path as new stack, create it if not existing on disk.

>>> from smartElements import api

>>> api.get_stacks()
["stackA", "stackB", "stackC"]

# Let's add a new stack.
>>> api.create_stack("path/to/somewhere/my-new-stack")

>>> api.get_stacks()
["stackA", "stackB", "stackC", "my-new-stack"]

add_list(stack_name, list_name)

Add a new list with the given name to the given stack.

>>> from smartElements import api

>>> api.get_lists("stackA")
["listA", "listB", "listC"]

# Let's add a new list.
>>> api.add_list("stackA", "my-new-list")

>>> api.get_lists("stackA")
["listA", "listB", "listC", "my-new-list"]

remove_stack(stack_name)

Remove the given stack from the settings, but doesn’t delete it.

Note: This will not delete the stack or any physical file or folder, it will just remove the stack from the settings.

>>> from smartElements import api

>>> api.get_stacks()
["stackA", "stackB", "stackC"]

>>> api.remove_stack("stackA")

>>> api.get_stacks()
["stackB", "stackC"]

get_settings()

Get the whole smartElements settings dict.

>>> from smartElements import api

>>> api.get_settings()

save_setting()

Save the given settings key with the given value.

>>> from smartElements import api

>>> api.save_setting("tooltips", False)

>>> api.get_settings()["tooltips"]
False

get_tags(stack_name)

Get all element tags of the given stack.

>>> from smartElements import api

>>> api.get_tags("stackA")
["tagA", "tagB", "tagC", "tagD", "tagE", "tagF"]

scan_for_media()

Scan for media from given paths. This walks over the given paths and all sub directories to get all ingestable media from it.

>>> from smartElements import api

>>> api.scan_for_media(["path/to/sources", "path/to/other/sources"])
{
    'footage_a.####.exr': <smartElements.media.Media object at 0x1027af0d0>,
    'footage_b.####.exr': <smartElements.media.Media object at 0x1027afc50>,
    'footage_c.####.exr': <smartElements.media.Media object at 0x10263e0d0>,
    ...
}

# For convenience you can also pass one single path as source:
>>> api.scan_for_media("path/to/sources")

ingest(stack_name, list_name, paths, nuke_exe_path, ingestion_setup=None, num_threads=4)

Ingest paths to the given location. By default, this uses the processor settings from the configuration. You can however overwrite this using the ingestion_setup kwarg. By default, this ingests with 4 parallel threads in the background. Increase if needed.

>>> from smartElements import api

# Ingest all ingestable elements from "path/to/sources" to stackA -> listA.
>>> api.ingest("stackA", "listA", ["path/to/sources"], "path/to/Nuke_vXX.XvX/nuke.exe")

Note: smartElements requires Nuke (at least a standard Nuke version) to ingest data. When calling smartElements.api.ingest you need to have Nuke as a module in you path so that the nuke module can be imported. Otherwise, you will run into an ModuleNotFoundError: No module named 'nuke'. When ingesting watch your terminal for ingestion logging messages.

For more clearance how to ingest via smartElements API in standalone mode please have a look at the following code:

 1  """Ingest via smartElements API in standalone mode.
 2
 3  Usage:
 4      >>> <path to nuke.exe> -i -t <path to this .py file>
 5
 6  """
 7
 8  # Import built-in modules
 9  import sys
10
11  # Make sure to add the correct cp version of smartElements to your sys.path.
12  # cp27: nuke-12 and older
13  # cp37: nuke-13
14  # cp39: nuke-14
15  # cp310: nuke-15
16  sys.path.append("C:/path/to/smartElements_vX.XX.X/cpXX")
17
18  # Import third-party modules
19  import nuke
20  from smartElements import api
21
22
23  if __name__ == "__main__":
24      root = "C:/root/to/footage/to/ingest"
25      api.ingest("default", "test", [root], nuke.env["ExecutablePath"])

Do these steps:

  1. Save this code block to disk as ingest.py file.

  2. Point to your smartElements installation path. Make sure to update the path to add to your sys.path accordingly to your used Nuke version (L16).

  3. Adjust the root (the root directory that contains all media to ingest) to your needs (L24).

  4. Adjust the stack and list to ingest to to your needs (L25).

  5. Launch a terminal and execute: <path to nuke-XX.exe> -i -t <path to ingest.py file>

This should now crawl your specified root for media and ingest it. Note: The ingest threads stay alive open once your ingestion has finished, which means you will need to close the terminal process yourself.

Here is a demo ingesting 2 test elements in standalone:




Querying for Element instances

There are several ways to get element instances as seen below. Once you have one or several elements, you can manipulate them as needed. There are many things you can do with Element instance. Please see the Working with Element instances section below for more information.

get_element(stack, list, name)

Get a particular element of given stack, list, name.

>>> from smartElements import api

>>> api.get_element("stackA", "listA", "elementA")
<smartElements.api.Element object at 0x102613d90>

get_element_from_root(root)

Construct an element instance from given element root directory.

>>> from smartElements import api

>>> api.get_element_from_root("/path/to/library/stackA/listA/elementA")
<smartElements.api.Element object at 0x102613d90>

get_all_elements_in_stack(stack_name)

Get all elements in the given stack.

>>> from smartElements import api

api.get_all_elements_in_stack("stackA")
[
    <smartElements.api.Element object at 0x102613d90>,
    <smartElements.api.Element object at 0x102627790>,
    <smartElements.api.Element object at 0x102627310>,
    <smartElements.api.Element object at 0x102627590>,
    <smartElements.api.Element object at 0x1026272d0>
]

get_all_elements_in_list(stack_name, list_name)

Get all elements in the given list.

>>> from smartElements import api

api.get_all_elements_in_stack("stackA", "listA")
[
    <smartElements.api.Element object at 0x102613d90>,
    <smartElements.api.Element object at 0x102627790>,
    <smartElements.api.Element object at 0x102627310>
]

get_elements_by_tags(stack_name, tags, match_strict=True)

Get list of elements using the given tags, using loose or strict match. Strict match means a matching element needs to have all the given tags assigned. A loose match means a matching element needs to have at least one of the given tags assigned

>>> from smartElements import api

# Get all elements that have assigned 'tagA' AND 'tagB' AND 'tagC'.
>>> api.get_elements_by_tags("stackA", ["tagA", "tagB", "tagC"])
[<smartElements.api.Element object at 0x102613d90>]

# Get all elements that have assigned at least one of the following tags: 'tagA', 'tagB', 'tagC'.
>>> api.get_elements_by_tags("stackA", ["tagA", "tagB", "tagC"], match_strict=False)
[
    <smartElements.api.Element object at 0x102613d90>,
    <smartElements.api.Element object at 0x102627790>,
    <smartElements.api.Element object at 0x102627310>,
    <smartElements.api.Element object at 0x102627590>
]

Working with Element instances

Once you have queried for an element as seen above, you can manipulate various things on it.

>>> from smartElements import api

>>> element = api.get_element("stackA", "listA", "elementA")
<smartElements.api.Element object at 0x102613d90>

# Get all metadata for the given element.
>>> element.meta

# Get element's preview jpg sequence.
>>> element.sequence

# Set a given thumbnail for the element.
# index: If set, use a particular index of the element's sequence.
# first: If True use the first frame as thumbnail.
# middle: If True use the middle frame as thumbnail.
# end: If True use the end frame as thumbnail.
# auto: If True use the largest frame that contains the most image information, in most cases
#       (but not always) representing the most interesting and most representative frame in
#       the sequence.
>>> element.set_thumbnail(index=None, first=False, middle=False, end=False, auto=False)

# Get absolute path of element's thumbnail image or default thumbnail
# path as fallback in case the element has no thumbnail set.
>>> element.get_preview_path()

# Physically delete the element from disk. **WARNING:** There is no undo! Handle with caution!
>>> element.delete()

# Mark the element as deprecated.
>>> element.deprecate()

# Remove deprecation from element.
>>> element.undeprecate()

# Set the favorite index for this element between 1-5. Use 0 to remove favorite.
>>> set_favorite(favorite_index)

# Set the comment on the element.
>>> element.set_comment(comment)

# Get all tags of the element.
>>> element.get_tags()

# Add the given tag as string type or a list of strings for several tags.
>>> element.add_tag(tag_names)

# Remove the given tag from the element.
>>> element.remove_tag(tag_name)

# Reveal the given element in the explorer.
>>> element.reveal()

# Assign the given hex color string. E.g. #ff0000 for pure red.
>>> element.set_color(hex_color_string)

# Remove any custom color from the element.
>>> element.unset_color()

# Add or update a metadata key-value pair.
>>> element.add_metadata(key, value)

# Remove given key from the metadata.
>>> element.remove_metadata(key)

# Rename element (renames only metadata name, does not rename on disk to keep consistency).
>>> element.rename(new_name)

# Add additional files and folder to the element.
# Use replace=True to replace already existing folders/files.
>>> element.add_additional_files(files_and_folder_paths, replace=False)

Note

Is there anything missing in here that you need access to in order to automate things? Get in contact with us and let us know via info@cragl.com. We are happy to help you.