.. _api: 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: ``/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: .. code-block:: python >>> 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: .. code-block:: python >>> 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: .. code-block:: python >>> 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. .. code-block:: python >>> 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. .. code-block:: python >>> 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. .. code-block:: python >>> 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. .. code-block:: python >>> 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. .. code-block:: python >>> 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. .. code-block:: python >>> 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. .. code-block:: python >>> 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. .. code-block:: python >>> 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. .. code-block:: python >>> from smartElements import api >>> api.get_settings() save_setting() `````````````` Save the given settings key with the given value. .. code-block:: python >>> 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. .. code-block:: python >>> 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. .. code-block:: python >>> from smartElements import api >>> api.scan_for_media(["path/to/sources", "path/to/other/sources"]) { 'footage_a.####.exr': , 'footage_b.####.exr': , 'footage_c.####.exr': , ... } # 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. .. code-block:: python >>> 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: .. code-block:: python :linenos: """Ingest via smartElements API in standalone mode. Usage: >>> -i -t """ # Import built-in modules import sys # Make sure to add the correct cp version of smartElements to your sys.path. # cp27: nuke-12 and older # cp37: nuke-13 # cp39: nuke-14 # cp310: nuke-15 sys.path.append("C:/path/to/smartElements_vX.XX.X/cpXX") # Import third-party modules import nuke from smartElements import api if __name__ == "__main__": root = "C:/root/to/footage/to/ingest" 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: `` -i -t `` 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: .. raw:: html


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. .. code-block:: python >>> from smartElements import api >>> api.get_element("stackA", "listA", "elementA") get_element_from_root(root) ``````````````````````````` Construct an element instance from given element root directory. .. code-block:: python >>> from smartElements import api >>> api.get_element_from_root("/path/to/library/stackA/listA/elementA") get_all_elements_in_stack(stack_name) ````````````````````````````````````` Get all elements in the given stack. .. code-block:: python >>> from smartElements import api api.get_all_elements_in_stack("stackA") [ , , , , ] get_all_elements_in_list(stack_name, list_name) ``````````````````````````````````````````````` Get all elements in the given list. .. code-block:: python >>> from smartElements import api api.get_all_elements_in_stack("stackA", "listA") [ , , ] 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 .. code-block:: python >>> from smartElements import api # Get all elements that have assigned 'tagA' AND 'tagB' AND 'tagC'. >>> api.get_elements_by_tags("stackA", ["tagA", "tagB", "tagC"]) [] # 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) [ , , , ] Working with Element instances ------------------------------ Once you have queried for an element as seen above, you can manipulate various things on it. .. code-block:: python >>> from smartElements import api >>> element = api.get_element("stackA", "listA", "elementA") # 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.