How to Write Python Scripts that Run in OBS

Beatme CodeBY-NC-ND
17:58 read·2337 words· published

Prerequisites

The prerequisite for reading this article is that you have already mastered how to run Python scripts in OBS, you can check out the article How to Configure OBS to Run Python Scripts for information on that.

Python Script Functions Exported to OBS

When a Python script has functions with specific names and parameters, OBS will find and use those functions to achieve certain effects, such as displaying a piece of text in OBS that explains what the script does. This connection between Python scripts and OBS is officially known as Python Script Function Exports.

Exporting Python script functions to OBS is optional

Exporting Python script functions to OBS is an optional behavior; Python scripts you write can have no functions exported, and this will not affect OBS’s ability to execute the script.

Display Python Script Description in OBS via the script_description Function

Every Python script that is successfully loaded into OBS will have a description in the OBS Scripts Window. By defining a function called script_description in your Python script, you can set the description text for that script, and of course, script_description should return a string.

OBS supports the inclusion of whitespace characters in Python script descriptions

The string returned by the script_description function can contain certain whitespace characters, e.g., tabs (\t), and they can be displayed normally in the Scripts Window of OBS.

exports.py
# Import the obspython module
import obspython as obs

def script_description(): return 'This is a simple but ineffective script\nAuthor:\toops\nVersion:\t0.1\nContact:\txxx'

Display Python Script Properties in OBS via the script_properties Function

The script_description function is very important when you want Python scripts to be interactive with OBS users, and it needs to return a Property Set object that contains information about the script’s relevant properties. OBS will create a series of controls in the Scripts Window based on the Property Set object returned by script_description, such as textboxes, buttons, checkboxes, and so on. With these controls, OBS users can modify and adjust the script’s properties to change the functionality of the Python script.

What are the Python Script Properties in OBS?

Python Script Properties in OBS can refer either to the controls that appear in the Scripts Window or to the objects (Script Property objects) used to create them. Each Script Property corresponds to a Script Setting Item that stores the value of the property, except for button controls.

What are the Python Script Settings in OBS?

Python Script Settings are stored in an OBS Data Settings object, which reads and writes some data in a manner similar to key-value pairs. There is no official term like “Python Script Settings” in the official OBS documentation, so we’ll use it for convenience. An item in Python Script Settings does not necessarily correspond to a Script Property, and although you can influence the functionality of Python scripts based on Script Settings, they’re not visible to OBS users.

For different Scene Collections, OBS will either save the Python Script Settings locally in some form at the appropriate time, or reverse the process, so that the Script Settings are made permanent. When a Python script file is removed from the OBS’s Scripts Window, the corresponding Script Settings are also deleted locally.

In the function script_properties, we create a Property Set object using obs_properties_create and add a Property object corresponding to the numberbox to the Property Set with obs_properties_add_text.

exports.py
def script_properties():
	# Create a Property Set object
	props = obs.obs_properties_create()

# Add a Script Property object that corresponds to the numberbox for indicating hours obs.obs_properties_add_int(props, 'hours', 'Hours:', 2, 5, 1) return props

Perform Tasks when Loading or Unloading Python Scripts in OBS via the script_load, script_unload Functions

If you want to perform certain tasks, such as reading or writing data from or to a file, when a Python script is loaded or unloaded by OBS, then you can define the functions script_load, script_unload in your Python script.

script_load(settings)

settings parameter

The settings parameter is the Script Settings object corresponding to the Python script.

When does OBS load or unload Python scripts?

Python scripts will be loaded when you add a script to the OBS’s Scripts Window, reload a script, restore defaults, or when OBS starts. Python scripts will be unloaded when you remove a script from the OBS’s Scripts Window, reload a script, before restoring defaults, or when OBS is closed.

In the code below, we record and display the last stop time of the script via the functions script_load and script_unload. In fact, this is ineffective when you close OBS directly, for reasons that will be explained later.

exports.py
# The data variable represents the Script Settings
data = None

def script_load(settings): global data data = settings
# Read the Script Setting Item closed_time, which is the stop time of the script closed_time = obs.obs_data_get_string(data, 'closed_time') if closed_time: obs.script_log(obs.LOG_INFO, f'The script was last stopped at {closed_time}')
def script_unload(): # Write the current time to the Script Setting Item closed_time as the stop time of the script from datetime import datetime obs.obs_data_set_string(data, 'closed_time', datetime.now().ctime())
[exports.py] The script was last stopped at Mon Mar 4 07:12:37 2024

The obs_data_get_string, obs_data_set_string functions of the OBS obspython module

The obspython module’s obs_data_get_string and obs_data_set_string functions are used to perform read and write operations on Data Items of string type in the OBS Data Settings object.

Similar functions include obs_data_get_int, obs_data_set_int, etc.

Perform Tasks after OBS Users Modify Python Script Properties via the script_update Function

If you want to perform certain tasks after an OBS user modifies Python Script Properties (via the Scripts Window), e.g., adjusting the implemented functionality according to the modified Script Properties, then you can define the function script_update in the Python script, which is executed at a point in time between the script_load function and the script_unload function.

It should be noted that any modification of the Script Properties will result in the call of the script_update function. If you are only monitoring changes to one Script Property, then you can use the obs_property_set_modified_callback function of the obspython module.

script_update(settings)

settings parameter

The settings parameter is the Script Settings object corresponding to the Python script.

When does OBS call the script_update function in a Python script?

In addition to being called after an OBS user modifies Python Script Properties, the script_update function is also called when adding a script, reloading a script, restoring defaults, or when OBS is started.

The values displayed by the controls in the OBS Script Window may not be the same as the values corresponding to the Python Script Setting Items

Until an OBS user makes a valid edit to a control in the Scripts Window, the value displayed by the control will not be synchronized to the corresponding Python Script Setting Item. This can be confusing because the value that the OBS user sees from the control may not be the same as the value you read from the Python Script Settings. Once the window control corresponding to a Script Property has been validly edited, the value in the corresponding control can be safely read from the Python Script Settings.

To avoid the above inconsistencies, you can define the script_defaults function and set the default values for the controls’ corresponding Script Setting Items.

Previously, in the function script_properties, we added a numberbox with a minimum value of 2, but reading its corresponding Python Script Setting Item could result in 0 if the numberbox has not been validly edited.

exports.py
def script_update(settings):
	# Read the Script Setting Item hours and display it
	hours = obs.obs_data_get_int(settings, 'hours')
	obs.script_log(obs.LOG_INFO, f'The current hours are {hours}')
[exports.py] The current hours are 0

Write Data to Python Script Settings when OBS is Closed via the script_save Function

The script_save function in Python scripts is called when OBS is closed, at which point some important data can be written to the Script Settings so that they can be read the next time OBS is started.

script_save(settings)

settings parameter

The settings parameter is the Script Settings object corresponding to the Python script.

Script Settings written via the script_unload function when OBS is closed are not stored locally

Although OBS calls the script_unload function for Python scripts when it is closed, writes to Script Setting Items may be “invalidated” because they will not be stored locally by OBS, unlike clicking the Reload Scripts Button in the Scripts Window.

The best way to save important data is to define the function script_save rather than script_unload, although the previous example demonstrated the opposite.

Instead of the script_unload function, we use the script_save function, which causes the OBS close time to be considered as the script’s stop time, and the time of other user actions (e.g., clicking the Reload Scripts Button) will no longer be considered as the script’s stop time.

exports.py
def script_save(settings):
	# Write the current time to the Script Setting Item closed_time as the stop time of the script
	from datetime import datetime
	obs.obs_data_set_string(settings, 'closed_time', datetime.now().ctime())

Specify Default Values for Python Script Properties Using Python Script Settings

In the official obspython module provided with OBS, you can’t specify default values for Python Script Properties directly via parameters, although some functions that add Script Properties can achieve an approximate effect. To set default values for controls in the Scripts Window, you should define the function script_defaults in the Python script and specify default values for the Script Setting Items corresponding to the Script Properties in that function.

script_defaults(settings)

settings parameter

The settings parameter is the Script Settings object corresponding to the Python script.

The default value of an OBS Python Script Setting Item is used only when it has no specific value

It’s important to note that once a Python Script Setting Item has a specific value, its default value is no longer used, which happens after an OBS user edits a control, or after you assign a value to a Script Setting Item via a function.

When you click the Defaults Button in the Scripts Window of OBS, the default values of the Python Script Setting Items will come back to work because the specific values of the Script Setting Items have been cleared.

In the function script_defaults, we specify the default value of the Script Setting Item hours as 3, which will cause the numberbox in the Scripts Window to display 3 by default. If the numberbox has been edited before, then you will need to click the Defaults Button to see the effect.

exports.py
def script_defaults(settings):
	# Set the default value of the Setting Item hours to 3
	obs.obs_data_set_default_int(settings, 'hours', 3)

The obs_data_set_default_int function of the OBS obspython module

The obs_data_set_default_int function of the obspython module is used to set default values for Data Items of integer type in the OBS Data Settings object.

Perform Tasks at Each Frame Drawn by OBS via the Python Script's script_tick Function

If you want to perform certain tasks as OBS draws each frame, then you can define the function script_tick in your Python script.

script_tick(seconds)

seconds parameter

The seconds parameter is the number of seconds elapsed from the previous frame to this one.

Using the script_tick function in Python scripts may cause performance issues for OBS

OBS calls the script_tick function more frequently, so it is not a good idea to use this function for complex tasks, and OBS may not be able to respond to the user after a long run if you keep outputting information to the Script Log Window via the script_log function.

The obspython module of OBS provides some additional functions for timers, which can be used to avoid problems caused by the script_tick function.

exports.py
def script_tick(seconds):
	obs.script_log(obs.LOG_INFO, f'{seconds} OBS is about to become unresponsive!!!')

Get the Directory where the Python Script is Located via the script_path Function

For added Python scripts, OBS defines a function called script_path (not part of the obspython module) that can be used to get the directory where the current script is located.

The difference between the script_path function defined by OBS for scripts and the __file__ attribute of Python modules

The __file__ attribute indicates the file path of the Python module, while the script_path function returns the path to the folder where the script file is located.

We adjust the previous code to call the script_path function in script_load and display the returned directory.

exports.py
def script_load(settings):
	# …
	obs.script_log(obs.LOG_INFO, script_path())
# Output in Windows
[exports.py] /scripts/

Add or Remove Script Timers for OBS via the timer_add, timer_remove Functions

With the timer_add and timer_remove functions of the obspython module, you can add or remove Script Timers for OBS. Script Timers will repeatedly call the given callback function or method at specified intervals, unlike the script_tick function.

Once the timer_add function has been used, the added Script Timer will start working. If you want the Script Timer to stop, you can remove it with the timer_remove function or call the remove_current_callback function of the obspython module in the callback function or method.

timer_add(callback, milliseconds)
timer_remove(callback)

callback parameter

The callback parameter is the function or method that is called back by the Script Timer, and in the timer_remove function, the Script Timer corresponding to the callback will be removed.

milliseconds parameter

The milliseconds parameter is the time interval in milliseconds at which the timer will be triggered, and accepts a value of integer type.

The timer_remove function of the obspython module cannot remove the Script Timer corresponding to the instance method

If a Python function in a module is used as a callback target, timer_add and timer_remove will work correctly, and if an instance method of a Python class is used as a callback target, then timer_remove may fail and its corresponding Script Timer will not be removed.

In the code below, welcome is the callback function of the Script Timer, and we use remove_current_callback to make this function be called only once.

exports.py
# Script Timer callback function welcome
def welcome():
	obs.script_log(obs.LOG_INFO, 'This is a callback function that is only called once')
	# Remove the Script Timer corresponding to welcome
	obs.remove_current_callback()

# Add a Script Timer with a 3-second interval obs.timer_add(welcome, 3000)
[exports.py] This is a callback function that is only called once

Source Code

exports.py·codebeatme/obs-python-scripting·GitHub