如何编写可在 OBS 中运行的 Python 脚本

我被代码海扁署名-非商业-禁演绎
阅读 16:00·字数 4800·发布 
Bilibili 空间
关注 960

前提

阅读本节的前提是已经掌握了如何在 OBS 中运行 Python 脚本,你可以查看如何配置 OBS 运行 Python 脚本一节来了解相关信息。

Python 脚本函数导出至 OBS

当一个 Python 脚本拥有特定名称和参数的函数时,OBS 将发现并使用这些函数来实现某些效果,比如,在 OBS 中显示一段说明脚本作用的文字。Python 脚本与 OBS 之间的这种联系,被官方称为 Python 脚本函数导出(Script Function Exports)。

Python 脚本函数导出至 OBS 是可选行为

Python 脚本函数导出至 OBS 是一种可选行为,你所编写的 Python 脚本可以没有任何函数导出,这不会影响 OBS 对脚本的执行。

使用 script_description 函数在 OBS 中展示 Python 脚本说明

每一个成功载入 OBS 的 Python 脚本,都会在 OBS 的脚本窗口中拥有一段说明信息。通过在 Python 脚本中定义一个名为script_description的函数,你可以设置该脚本的相关说明文字,当然,script_description应返回一个字符串。

OBS 支持在 Python 脚本说明中包含空白字符

script_description函数返回的字符串中可以包含某些空白字符,比如,制表符(\t),他们可以正常显示在 OBS 的脚本窗口中。

exports.py
# 导入模块 obspython
import obspython as obs

def script_description(): return '这是一个简单但没有任何效果的脚本\n作者:\t哎呦喂\n版本:\t0.1\n联系:\txxx'

使用 script_properties 函数在 OBS 中展示 Python 脚本属性

函数script_properties是非常重要的,当你希望 Python 脚本与 OBS 用户可以交互时,该函数需要返回一个包含脚本相关属性信息的属性集对象。OBS 将根据script_properties返回的属性集对象,在脚本窗口中创建一系列的控件,比如文本框,按钮,复选框等。通过这些控件,OBS 用户可以修改调整脚本的属性,从而改变 Python 脚本所实现的功能。

OBS 中的 Python 脚本属性是什么?

OBS 中的 Python 脚本属性(Script Properties)可以指表现在脚本窗口中的控件,也可以指用于创建这些控件的对象(脚本属性对象)。每一个脚本属性都对应了一个脚本设置(Script Settings)项,用于存储属性对应的值,不过,按钮控件除外。

OBS 中的 Python 脚本设置是什么?

Python 脚本设置被存储在一个 OBS 数据设置对象中,该对象采用类似于键值对的方式来读写一些数据。在 OBS 官方文档中,并没有类似于“Python 脚本设置”这样的正式称呼,我们采用他只是为了方便讲述。Python 脚本设置中的一个项未必会对应一个脚本属性,虽然你可以依据脚本设置来影响 Python 脚本所实现的功能,但他们对于 OBS 用户是不可见的。

针对不同的场景集合,OBS 会在合适的时机,将 Python 脚本设置以某种形式保存至本地,或反转此过程,这样,脚本设置便具有了永久化效果。通过 OBS 的脚本窗口移除某个 Python 脚本文件后,其对应的脚本设置也将从本地删除。

交互控件

想要详细了解属性集对象以及如何在 OBS 中使用控件,你可以查看如何通过 Python 脚本使用控件与 OBS 用户交互?OBS 属性集对象介绍一节。

在函数script_properties中,我们使用obs_properties_create创建了一个属性集对象,并通过obs_properties_add_text为属性集添加了一个对应数字显示框的属性对象。

exports.py
def script_properties():
	# 创建一个属性集对象
	props = obs.obs_properties_create()

# 添加一个对应数字显示框的脚本属性对象,用于表示小时 obs.obs_properties_add_int(props, 'hours', '小时:', 2, 5, 1) return props

使用 script_load,script_unload 函数在 OBS 加载或卸载 Python 脚本时执行任务

如果希望在 Python 脚本被 OBS 加载或卸载时执行某些任务,比如从文件读取或写入数据,那么你可以在 Python 脚本中定义函数script_loadscript_unload

script_load(settings)

settings 参数

settings参数为 Python 脚本对应的脚本设置对象。

什么情况下 OBS 会加载或卸载 Python 脚本?

当你在 OBS 的脚本窗口添加脚本,重新载入脚本,恢复默认时,或 OBS 启动后,Python 脚本就会被加载。当你在 OBS 的脚本窗口移除脚本,重新载入脚本时,恢复默认之前,或 OBS 关闭时,Python 脚本就会被卸载。

在下面的代码中,我们通过函数script_loadscript_unload,记录并显示了上一次脚本停止的时间。事实上,这种做法在直接关闭 OBS 时是无效的,原因会在稍后给予说明。

exports.py
# 变量 data 表示脚本设置
data = None

def script_load(settings): global data data = settings
# 读取脚本设置项 closed_time,他是脚本的停止时间 closed_time = obs.obs_data_get_string(data, 'closed_time') if closed_time: obs.script_log(obs.LOG_INFO, f'上次脚本停止的时间为 {closed_time}')
def script_unload(): # 将当前时间写入脚本设置项 closed_time,作为脚本的停止时间 from datetime import datetime obs.obs_data_set_string(data, 'closed_time', datetime.now().ctime())
[exports.py] 上次脚本停止的时间为 Mon Mar 4 07:12:37 2024

OBS obspython 模块的 obs_data_get_string,obs_data_set_string 函数

obspython模块的obs_data_get_stringobs_data_set_string函数,用于对 OBS 数据设置对象中字符串类型的数据项进行读写操作。

与之类似的函数还有obs_data_get_intobs_data_set_int等。

使用 script_update 函数在 OBS 用户修改 Python 脚本属性后执行任务

如果希望在 OBS 用户修改 Python 脚本属性(通过脚本窗口)后执行某些任务,比如,根据修改后的脚本属性调整实现的功能,那么你可以在 Python 脚本中定义函数script_update,该函数的执行时间点在script_load函数与script_unload函数之间。

需要指出的是,任何脚本属性的修改都会导致script_update函数的调用。如果仅监视某一个脚本属性的变化,那么可以使用obspython模块的obs_property_set_modified_callback函数。

script_update(settings)

settings 参数

settings参数为 Python 脚本对应的脚本设置对象。

什么情况下 OBS 会调用 Python 脚本中的 script_update 函数?

除了在 OBS 用户修改 Python 脚本属性后,script_update函数还会在添加脚本,重新载入脚本,恢复默认或 OBS 启动时被调用。

OBS 脚本窗口中控件显示的值与 Python 脚本设置项对应的值可能不相同

在 OBS 用户对脚本窗口中的控件进行有效编辑之前,控件所显示的值不会同步至对应的 Python 脚本设置项。这会产生困扰,因为 OBS 用户从该控件看到的值,可能与你从 Python 脚本设置中读取到的值并不相同。一旦脚本属性对应的窗口控件被有效编辑,那么可从 Python 脚本设置安全的读取对应控件中的值。

要避免以上不相同的情况,可以定义script_defaults函数,并在该函数中为控件对应的脚本设置项设置默认值。

之前,在函数script_properties中,我们添加了最小值为2的数字显示框,但读取其对应的 Python 脚本设置项后,其结果可能显示为0,如果该数字显示框未被有效编辑的话。

exports.py
def script_update(settings):
	# 读取脚本设置项 hours 并显示
	hours = obs.obs_data_get_int(settings, 'hours')
	obs.script_log(obs.LOG_INFO, f'当前小时为 {hours}')
[exports.py] 当前小时为 0

使用 script_save 函数在 OBS 关闭时将数据写入 Python 脚本设置

在 OBS 关闭时,Python 脚本中的script_save函数会被调用,此时可将一些重要的数据写入脚本设置,以便下次启动时读取他们。

script_save(settings)

settings 参数

settings参数为 Python 脚本对应的脚本设置对象。

在关闭 OBS 时通过 script_unload 函数写入的脚本设置项不会被存储至本地

虽然 OBS 在关闭时,会调用 Python 脚本的script_unload函数,但对脚本设置项的写入操作可能会“无效”,因为他们不会被 OBS 存储至本地,这不同于点击脚本窗口重新载入脚本按钮

保存重要数据的最佳方式是定义函数script_save而非script_unload,虽然之前的示例中演示了相反的做法。

我们使用script_save函数代替script_unload函数,这将使得 OBS 的关闭时间被视为脚本停止时间,而其他用户操作(比如,点击重新载入脚本按钮)的时间,不再被视为脚本的停止时间。

exports.py
def script_save(settings):
	# 将当前时间写入脚本设置项 closed_time,作为脚本的停止时间
	from datetime import datetime
	obs.obs_data_set_string(settings, 'closed_time', datetime.now().ctime())

使用 Python 脚本设置为 Python 脚本属性指定默认值

在 OBS 官方提供的obspython模块中,你无法直接通过参数为 Python 脚本属性指定默认值,虽然一些添加脚本属性的函数可以达到近似的效果。要为脚本窗口中的控件设置默认值,应该在 Python 脚本中定义函数script_defaults,并在该函数中为脚本属性对应的脚本设置项指定默认值。

script_defaults(settings)

settings 参数

settings参数为 Python 脚本对应的脚本设置对象。

OBS Python 脚本设置项的默认值仅在其没有具体值时被使用

需要指出,一旦 Python 脚本设置项拥有了具体的值,其默认值将不再被使用,这会发生在 OBS 用户编辑了某个控件后,或你通过函数为脚本设置项指定了一个值后。

当你点击 OBS 的脚本窗口默认按钮后,Python 脚本设置项的默认值将重新发挥作用,因为脚本设置项的具体值已经被清除。

在函数script_defaults中,我们将脚本设置项hours的默认值指定为3,这会使脚本窗口中的数字显示框默认显示3。如果之前编辑过该数字显示框,那么需要点击默认按钮,才能看到该效果。

exports.py
def script_defaults(settings):
	# 将设置项 hours 的默认值设置为 3
	obs.obs_data_set_default_int(settings, 'hours', 3)

OBS obspython 模块的 obs_data_set_default_int 函数

obspython模块的obs_data_set_default_int函数,用于为 OBS 数据设置对象中整数类型的数据项设置默认值。

使用 Python 脚本的 script_tick 函数在 OBS 绘制每一帧时执行任务

如果你希望在 OBS 绘制每一帧时执行某些任务,那么可以在 Python 脚本中定义函数script_tick

script_tick(seconds)

seconds 参数

seconds参数是上一帧到本帧所经历的秒数。

在 Python 脚本中使用 script_tick 函数可能为 OBS 带来性能问题

OBS 对script_tick函数的调用频率较高,因此,使用该函数完成复杂任务并不是一个好主意,如果你通过script_log函数不断向脚本日志窗口输出信息,那么 OBS 可能会无法响应用户,在长时间运行之后。

OBS 的obspython模块提供了另外一些关于计时器的函数,使用他们可以有效避免script_tick函数所产生的问题。

exports.py
def script_tick(seconds):
	obs.script_log(obs.LOG_INFO, f'{seconds} OBS 就要无法响应了!!!')

使用 script_path 函数获取 Python 脚本所在的目录

对于添加的 Python 脚本,OBS 会为其定义一个名为script_path的函数(不属于obspython模块),该函数可用于获取当前脚本所在的目录。

OBS 为脚本定义的 script_path 函数与 Python 模块的 __file__ 特性之间的区别

__file__特性表示了 Python 模块对应的文件路径,而script_path函数返回的是脚本文件所在文件夹的路径。

我们调整之前的代码,在script_load中调用script_path函数,并显示返回的目录。

exports.py
def script_load(settings):
	# …
	obs.script_log(obs.LOG_INFO, script_path())
# Windows 中的输出结果
[exports.py] /scripts/

使用 timer_add,timer_remove 函数为 OBS 添加或移除脚本计时器

通过obspython模块的timer_addtimer_remove函数,你可以为 OBS 添加或移除脚本计时器(Script Timers)。脚本计时器将在指定时间间隔下,反复调用给出的回调函数或方法,这不同于script_tick函数。

一旦使用了timer_add函数,被添加的脚本计时器就会开始工作。如果希望脚本计时器停止,可以通过timer_remove函数将其移除,或在回调函数和方法中调用obspython模块的remove_current_callback函数。

timer_add(callback, milliseconds)
timer_remove(callback)

callback 参数

callback参数是被脚本计时器回调的函数或方法,在timer_remove函数中,callback对应的脚本计时器将被移除。

milliseconds 参数

milliseconds参数是计时器触发的时间间隔,以毫秒为单位,接受整数类型的值。

obspython 模块的 timer_remove 函数无法移除实例方法对应的脚本计时器

如果将模块中的 Python 函数作为回调目标,那么timer_addtimer_remove可以正确运行,如果将 Python 类的实例方法作为回调目标,那么timer_remove可能失效,其对应的脚本计时器不会被移除。

在下面的代码中,welcome是脚本计时器的回调函数,我们通过remove_current_callback使该函数只被调用一次。

exports.py
# 脚本计时器的回调函数 welcome
def welcome():
	obs.script_log(obs.LOG_INFO, '这是只被调用一次的回调函数')
	# 移除 welcome 对应的脚本计时器
	obs.remove_current_callback()

# 添加脚本计时器,触发时间间隔为 3 秒 obs.timer_add(welcome, 3000)
[exports.py] 这是只被调用一次的回调函数

源码

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