Skip to content
WarmUpTill edited this page Aug 14, 2024 · 19 revisions

This section will describe how to use the scripting API to add custom macro conditions and actions.
The API is not limited to scripts or Python, but the examples showcased here will only be using Python for brevity.

Examples

Custom macro condition

This examples shows how to register a new condition type which will randomly evaluate to true based on provided user input percentage in the range from 0 to 100:

import obspython as obs
import threading  # Required by advss helpers
import random

CONDITION_NAME = "Random condition"


###############################################################################
# This function will define the UI for the custom condition type based on a obs_properties object.
# In this case only a single float selection field for specifying the probability of returning true.
# This isn't mandatory.
# You can also have a condition without any additional UI elements.
###############################################################################
def get_condition_properties():
    props = obs.obs_properties_create()
    obs.obs_properties_add_float(
        props, "probability", "Probability of returning true", 0, 100, 0.1
    )
    return props


###############################################################################
# You can provide default values for each of the settings you have defined earlier.
# This isn't mandatory.
# You can also have a condition without any settings to be modified.
###############################################################################
def get_condition_defaults():
    default_settings = obs.obs_data_create()
    obs.obs_data_set_default_double(default_settings, "probability", 33.3)
    return default_settings


###############################################################################
# This function will be used by the advanced scene switcher to determine if a condition evaluates to true or false.
# The settings for each instance of this condition type will be passed via the data parameter.
###############################################################################
def my_python_condition(data):
    target = obs.obs_data_get_double(data, "probability")
    value = random.uniform(0, 100)
    return value <= target


###############################################################################
# Let's register the new condition type
###############################################################################
def script_load(settings):
    advss_register_condition(
        CONDITION_NAME,
        my_python_condition,
        get_condition_properties,
        get_condition_defaults(),
    )


###############################################################################
# Deregistering is useful if you plan unloading the script files
###############################################################################
def script_unload():
    advss_deregister_condition(CONDITION_NAME)


###############################################################################
########################### boilerplate code below ############################
###############################################################################

This is the custom condition type we defined with this script in action:

RandomCondition

As you can see the frequency at which this condition returns true depends on the provided input value, just as we expect it to.

Registering an action would be identical to the example above, but instead of passing a function which returns a boolean value to advss_register_condition, you would pass a function performing your desired actions to advss_register_action.

The boilerplate code mentioned above can be found here for Python and Lua.
Usually you can just copy it directly into your script without any modifications.

Note that if you should choose to unload the scripts, which define custom macro segments, while custom macro segments are still in use, the plugin will no longer be able to query settings of those custom macro segments.
If such a state should be saved (e.g. because OBS was closed with the script no longer being loaded) the plugin will instead replace the custom macro segments with macro segments of type Unknown.
Even if you should choose to reload the script at a later point in time the settings for those Unknown macro segments will be lost and you will have to reconfigure them.

Detailed API description

The advanced scene switcher offers the following procedure handlers:

  • bool advss_register_script_action(in string name, in ptr default_settings, out string properties_signal_name, out string trigger_signal_name)
  • bool advss_register_script_condition(in string name, in ptr default_settings, out string properties_signal_name, out string trigger_signal_name)
  • bool advss_deregister_script_action(in string name)
  • bool advss_deregister_script_condition(in string name)
  • bool advss_get_variable_value(in string name, out string value)
  • bool advss_set_variable_value(in string name, in string value)

advss_register_script_action

name

The name field of calldata object associated with this procedure should specify the id of the action type you want to register. It will also be the user facing name in the action type selection.

default_settings (optional)

The name field of calldata object associated with this procedure should specify the a pointer to an obs_data_t object. It should contain the default values for the settings for your custom action type.
The ownership and thus responsibility to free this obs_data_t* pointer will be that of the advanced scene switcher.
Thus you must not free / release this pointer yourself or you risk a crash of OBS.

This value can be null, if you do not which to provide any default settings.

properties_signal_name (optional)

The properties_signal_name field of calldata object associated with this procedure will specify the name of the signal, which will be called by the advanced scene switcher, when it requests a new obs_properties_t object.
These objects are used to determine which settings UI elements should be shown to the user when creating an instance of your custom action type.

The signal will be registered by the advanced scene switcher.
You only have to connect to it.

When the signal is called by the advanced scene switcher you will have to pass the pointer to the obs_properties_t object you created via calldata_set_ptr in the field named properties.

You can ignore this field, if you do not wish to provide any controls to the user to modify the settings of this action type.

trigger_signal_name

The trigger_signal_name field of calldata object associated with this procedure will specify the name of the signal, which will be called by the advanced scene switcher, when your custom action type needs to be executed.

The signal will be registered by the advanced scene switcher.
You only have to connect to it.

Handling the trigger signal

In the following section trigger_signal_name is just a placeholder for the actual signal name.

When trigger_signal_name is called by the advanced scene switcher, the settings for the instance of this action type will be passed as a pointer to an obs_data_t object.
You can access it via calldata_ptr in the settings field of calldata object associated with this procedure.

When trigger_signal_name is called by the advanced scene switcher you will have to pass the result of your operation via calldata_set_bool in the field named result.
In case of a macro action this should always be true unless a catastrophic error occurred which should abort the whole macro's execution.

When trigger_signal_name is called by the advanced scene switcher it will pass completion_id field, which is a unique id for each instance of your custom action being triggered.

When trigger_signal_name is called by the advanced scene switcher it will also pass the name of the signal you will to emit when your action type's operation is done in the completion_signal_name field.

When you emit this completion signal you will also have to pass the completion_id value you have received via trigger_signal_name using calldata_set_int.

The signal will be registered by the advanced scene switcher.
You only have to emit it.

Return value

The return value can be queried via success from the calldata object associated with this procedure. Returns true, if the operation was successful, and false otherwise.

Example

def advss_register_action_type(name, callback, get_properties, default_settings):
    proc_handler = obs.obs_get_proc_handler()
    data = obs.calldata_create()

    obs.calldata_set_string(data, "name", name)
    obs.calldata_set_ptr(data, "default_settings", default_settings)

    obs.proc_handler_call(proc_handler, "advss_register_script_action", data)

    success = obs.calldata_bool(data, "success")
    if success == False:
        obs.script_log(obs.LOG_WARNING, f'failed to register custom action "{name}"')
        obs.calldata_destroy(data)
        return

    # Run in separate thread to avoid blocking main OBS signal handler.
    # Operation completion will be indicated via signal completion_signal_name.
    def run_helper(data):
        completion_signal_name = obs.calldata_string(data, "completion_signal_name")
        id = obs.calldata_int(data, "completion_id")

        def thread_func(settings):
            settings = obs.obs_data_create_from_json(
                obs.calldata_string(data, "settings")
            )

            callback(settings)

            reply_data = obs.calldata_create()
            obs.calldata_set_int(reply_data, "completion_id", id)
            obs.calldata_set_bool(reply_data, "result", True)
            signal_handler = obs.obs_get_signal_handler()
            obs.signal_handler_signal(
                signal_handler, completion_signal_name, reply_data
            )
            obs.obs_data_release(settings)
            obs.calldata_destroy(reply_data)

        threading.Thread(target=thread_func, args={data}).start()

    def properties_helper(data):
        if get_properties is not None:
            properties = get_properties()
        else:
            properties = None
        obs.calldata_set_ptr(data, "properties", properties)

    trigger_signal_name = obs.calldata_string(data, "trigger_signal_name")
    property_signal_name = obs.calldata_string(data, "properties_signal_name")

    signal_handler = obs.obs_get_signal_handler()
    obs.signal_handler_connect(signal_handler, trigger_signal_name, run_helper)
    obs.signal_handler_connect(signal_handler, property_signal_name, properties_helper)

    obs.calldata_destroy(data)

advss_register_script_condition

This is almost identical to advss_register_script_action with the only difference being the handling of the result field.

name

The name field of calldata object associated with this procedure should specify the id of the condition type you want to register. It will also be the user facing name in the condition type selection.

default_settings (optional)

The name field of calldata object associated with this procedure should specify the a pointer to an obs_data_t object. It should contain the default values for the settings for your custom condition type.
The ownership and thus responsibility to free this obs_data_t* pointer will be that of the advanced scene switcher.
Thus you must not free / release this pointer yourself or you risk a crash of OBS.

This value can be null, if you do not which to provide any default settings.

properties_signal_name (optional)

The properties_signal_name field of calldata object associated with this procedure will specify the name of the signal, which will be called by the advanced scene switcher, when it requests a new obs_properties_t object.
These objects are used to determine which settings UI elements should be shown to the user when creating an instance of your custom condition type.

The signal will be registered by the advanced scene switcher.
You only have to connect to it.

When the signal is called by the advanced scene switcher you will have to pass the pointer to the obs_properties_t object you created via calldata_set_ptr in the field named properties.

You can ignore this field, if you do not wish to provide any controls to the user to modify the settings of this condition type.

trigger_signal_name

The trigger_signal_name field of calldata object associated with this procedure will specify the name of the signal, which will be called by the advanced scene switcher, when your custom condition check needs to be executed.

The signal will be registered by the advanced scene switcher.
You only have to connect to it.

Handling the trigger signal

In the following section trigger_signal_name is just a placeholder for the actual signal name.

When trigger_signal_name is called by the advanced scene switcher, the settings for the instance of this condition type will be passed as a pointer to an obs_data_t object.
You can access it via calldata_ptr in the settings field of calldata object associated with this procedure.

When trigger_signal_name is called by the advanced scene switcher you will have to pass the result of your condition check via calldata_set_bool in the field named result.

When trigger_signal_name is called by the advanced scene switcher it will pass completion_id field, which is a unique id for each instance of your custom condition being triggered.

When trigger_signal_name is called by the advanced scene switcher it will also pass the name of the signal you will to emit when your condition type's operation is done in the completion_signal_name field.

When you emit this completion signal you will also have to pass the completion_id value you have received via trigger_signal_name using calldata_set_int.

The signal will be registered by the advanced scene switcher.
You only have to emit it.

Example

def advss_register_condition_type(name, callback, get_properties, default_settings):
    proc_handler = obs.obs_get_proc_handler()
    data = obs.calldata_create()

    obs.calldata_set_string(data, "name", name)
    obs.calldata_set_ptr(data, "default_settings", default_settings)

    obs.proc_handler_call(proc_handler, "advss_register_script_condition", data)

    success = obs.calldata_bool(data, "success")
    if success == False:
        obs.script_log(obs.LOG_WARNING, f'failed to register custom condition "{name}"')
        obs.calldata_destroy(data)
        return

    # Run in separate thread to avoid blocking main OBS signal handler.
    # Operation completion will be indicated via signal completion_signal_name.
    def run_helper(data):
        completion_signal_name = obs.calldata_string(data, "completion_signal_name")
        id = obs.calldata_int(data, "completion_id")

        def thread_func(settings):
            settings = obs.obs_data_create_from_json(
                obs.calldata_string(data, "settings")
            )

            callback_result = callback(settings)

            reply_data = obs.calldata_create()
            obs.calldata_set_int(reply_data, "completion_id", id)
            obs.calldata_set_bool(reply_data, "result", callback_result)
            signal_handler = obs.obs_get_signal_handler()
            obs.signal_handler_signal(
                signal_handler, completion_signal_name, reply_data
            )
            obs.obs_data_release(settings)
            obs.calldata_destroy(reply_data)

        threading.Thread(target=thread_func, args={data}).start()

    def properties_helper(data):
        if get_properties is not None:
            properties = get_properties()
        else:
            properties = None
        obs.calldata_set_ptr(data, "properties", properties)

    trigger_signal_name = obs.calldata_string(data, "trigger_signal_name")
    property_signal_name = obs.calldata_string(data, "properties_signal_name")

    signal_handler = obs.obs_get_signal_handler()
    obs.signal_handler_connect(signal_handler, trigger_signal_name, run_helper)
    obs.signal_handler_connect(signal_handler, property_signal_name, properties_helper)

    obs.calldata_destroy(data)

Return value

The return value can be queried via success from the calldata object associated with this procedure. Returns true, if the operation was successful, and false otherwise.

advss_deregister_script_action

name

The name field of calldata object associated with this procedure should specify the id of the action type you want to deregister. It will also be the user facing name in the condition type selection.

Return value

The return value can be queried via success from the calldata object associated with this procedure. Returns true, if the operation was successful, and false otherwise.

Example

def advss_deregister_action(name):
    proc_handler = obs.obs_get_proc_handler()
    data = obs.calldata_create()

    obs.calldata_set_string(data, "name", name)

    obs.proc_handler_call(proc_handler, "advss_deregister_script_action", data)

    success = obs.calldata_bool(data, "success")
    if success == False:
        obs.script_log(obs.LOG_WARNING, f'failed to deregister custom action"{name}"')

    obs.calldata_destroy(data)

advss_deregister_script_condition

This is identical to advss_deregister_script_action.

name

The name field of calldata object associated with this procedure should specify the id of the condition type you want to deregister.

Return value

The return value can be queried via success from the calldata object associated with this procedure. Returns true, if the operation was successful, and false otherwise.

Example

def advss_deregister_condition(name):
    proc_handler = obs.obs_get_proc_handler()
    data = obs.calldata_create()

    obs.calldata_set_string(data, "name", name)

    obs.proc_handler_call(proc_handler, "advss_deregister_script_condition", data)

    success = obs.calldata_bool(data, "success")
    if success == False:
        obs.script_log(obs.LOG_WARNING, f'failed to deregister custom condition "{name}"')

    obs.calldata_destroy(data)

advss_get_variable_value

name

The name field of calldata object associated with this procedure should specify the id of the name of the variable for which you want to get the current value.

value

The value field of calldata object associated with this procedure will contain the value of the specified variable, if the operation was successful.

Return value

The return value can be queried via success from the calldata object associated with this procedure. Returns true, if the operation was successful, and false otherwise.

Example

def advss_get_variable_value(name):
    proc_handler = obs.obs_get_proc_handler()
    data = obs.calldata_create()

    obs.calldata_set_string(data, "name", name)
    obs.proc_handler_call(proc_handler, "advss_get_variable_value", data)

    success = obs.calldata_bool(data, "success")
    if success == False:
        obs.script_log(obs.LOG_WARNING, f'failed to get value for variable "{name}"')
        obs.calldata_destroy(data)
        return None

    value = obs.calldata_string(data, "value")

    obs.calldata_destroy(data)
    return value

advss_set_variable_value

name

The name field of calldata object associated with this procedure should specify the id of the name of the variable for which you want to change the value.

value

The value field of calldata object associated with this procedure should specify the value you want to set for the specified variable.

Return value

The return value can be queried via success from the calldata object associated with this procedure. Returns true, if the operation was successful, and false otherwise.

Example

def advss_set_variable_value(name, value):
    proc_handler = obs.obs_get_proc_handler()
    data = obs.calldata_create()

    obs.calldata_set_string(data, "name", name)
    obs.calldata_set_string(data, "value", value)
    obs.proc_handler_call(proc_handler, "advss_set_variable_value", data)

    success = obs.calldata_bool(data, "success")
    if success == False:
        obs.script_log(obs.LOG_WARNING, f'failed to set value for variable "{name}"')

    obs.calldata_destroy(data)
    return success

Example guides

Explanations

Clone this wiki locally