--[[
M0073R - ver.1.0.0
A Lua script that mute/unmute audio source based on a scene change.
11.28.25
]]
function setup(settings)
obs.utils.settings= settings
obs.register.event(function(event_id)
if event_id == obslua.OBS_FRONTEND_EVENT_SCENE_CHANGED then
InitAction()
end
end)
local shared_iixisii= obs.shared.api("iixisii-shared-api-vs1.0.0")
local base= shared_iixisii.find("name", "M0073r")
if not base then
base=obs.PairStack();base.str("name", "M0073r").bul(
"isDisplayType",true
).str("id", "m0073r-scene-name")
shared_iixisii.insert(base.data)
base.free()
shared_iixisii.save()
else
base.free()
end
for _, a_name in ipairs(obs.scene:names()) do
local alist= get_scene_cz(a_name)
if alist then
for itm in alist.next() do
local source_name= itm.str("source")
local id= itm.str("unique_id")
local scene_name= itm.str("scene")
local id_value1=scene_name .. "_" .. source_name .. "_" .. id .. "_muted"
local id_value2=scene_name .. "_" .. source_name .. "_" .. id .. "_unmuted"
local dr1= shared_iixisii.find("key", id_value1)
local dr2= shared_iixisii.find("key", id_value2)
if not dr1 then
dr1= obs.PairStack()
dr1.bul("is_event", true).str(
"key",id_value1
).bul("value", false).str(
"name", "on [ " .. tostring(source_name) .. " ] is muted on [ " .. tostring(scene_name) .. " ]"
).str("id","m0073r-scene-name")
shared_iixisii.insert(dr1.data)
shared_iixisii.save()
end
if not dr2 then
dr2= obs.PairStack()
dr2.bul("is_event", true).str(
"key",id_value2
).bul("value", false).str(
"name", "on [ " .. tostring(source_name) .. " ] is unmuted on [ " .. tostring(scene_name) .. " ]"
).str("id", "m0073r-scene-name")
shared_iixisii.insert(dr2.data)
shared_iixisii.save()
end
dr1.free();dr2.free();itm.free()
end
alist.free()
end
end
shared_iixisii.save();shared_iixisii.free()
end
function get_scene_cz(scene_name)
local surs= obs.utils.settings.obj("m0073r_" .. scene_name .. "_scene_t")
if not surs or surs.data == nil then
return nil
end
local alist= surs.get_arr("m0073r_clz_obs_arr_t")
if not alist or alist.data == nil then
surs.free()
return nil
end
surs.free()
return alist
end
function script_properties()
local p= obs.script.create()
obs.script.label(p, nil, "
| M0073R - ver.1.1.0 | | by iixisii |
")
local audible_list_count=0
local audible_list;local add_options_error;
local add_options;local opera_list
local add;add=obs.script.button(p, "add_new", "+", function()
if audible_list_count <= 0 then
return
end
add.hide()
add_options_error.hide()
add_options.show()
opera_list.hide()
for _, a_name in pairs(obs.scene:names()) do
local op= obs.script.get(a_name)
if op then
op.hide()
end
end
return true
end)
opera_list= obs.script.options(p, "opera_view_list", "View List")
opera_list.add.str("", "").add.str(
"< View All >",""
).add.str("-","").cursor().disable()
for _, scene_name in pairs(obs.scene:names()) do
opera_list.add.str("< View [ " .. tostring(scene_name) .. " ] >", scene_name)
end
opera_list.onchange(function(value)
if value == "" then
for _, nn in pairs(obs.scene:names()) do
local opv= obs.script.get(nn)
if opv then
opv.show()
end
end
else
for _, nn in pairs(obs.scene:names()) do
local opv= obs.script.get(nn)
if opv then
opv.hide()
end
end
if value ~= "" then
local opv= obs.script.get(value)
if opv then
opv.show()
end
end
end
return true
end)
add_options= obs.script.form(p, "Adding new audible")
add_options.hide()
add_options_error= add_options.add.label("add_options_error","").hide()
add_options.onconfirm:idle()
add_options.cancel:click(function()
add_options.hide()
obs.utils.settings.str("audible_source", ""
).str("scene_name", "")
opera_list.show()
add.show()
if obs.utils.settings.str("opera_view_list") == "" then
for _, nn in pairs(obs.scene:names()) do
local opv= obs.script.get(nn)
if opv then
opv.show()
end
end
else
for _, nn in pairs(obs.scene:names()) do
local opv= obs.script.get(nn)
if opv then
opv.hide()
end
end
if obs.utils.settings.str("opera_view_list") ~= "" then
local opv= obs.script.get(obs.utils.settings.str("opera_view_list"))
if opv then
opv.show()
end
end
end
return true
end)
add_options.confirm:click(function()
local audible_value= obs.utils.settings.get_str("audible_source")
if audible_value == nil or audible_value == "" then
add_options_error.show()
add_options_error.error("Please select a valid audible source!")
return true
end
local scene_value= obs.utils.settings.get_str("scene_name")
if scene_value == nil or scene_value == "def" then
add_options_error.show()
add_options_error.error("Please select a valid scene!")
return true
end
-- m0073r_clz_obs_arr_t
local surs= obs.utils.settings.obj("m0073r_" .. scene_value .. "_scene_t")
if not surs or surs.data == nil then
surs= obs.PairStack()
end
local alist= surs.get_arr("m0073r_clz_obs_arr_t")
if not alist or alist.data == nil then
alist= obs.ArrayStack()
surs.arr("m0073r_clz_obs_arr_t", alist.data)
end
local audible= obs.PairStack()
local unique_id=obs.utils.get_unique_id(10)
audible.str("source", audible_value).str(
"scene", scene_value
).int("muted", 2).str("unique_id", unique_id)
alist.insert(audible.data)
obs.utils.settings.obj("m0073r_" .. scene_value .. "_scene_t", surs.data)
audible.free()
alist.free()
surs.free()
add_options.hide()
obs.utils.settings.str("audible_source", ""
).str("scene_name", "")
add.show()
local vopt= obs.utils.settings.str("opera_view_list")
if vopt ~= "" and vopt ~= "" then
scene_value= vopt
end
local opera= obs.script.get(scene_value)
if opera then
opera.show()
local vct= obs.script.bool(opera.parent, audible_value .. "_" .. scene_value, audible_value .. " ( undefined state )")
local delete_tick=os.clock()
vct.onchange(function(value, property)
local delete_tick_value=os.clock() - delete_tick
if delete_tick_value >= 0.15 and delete_tick_value <= 0.35 then
-- delete the item from list
local alist= get_scene_cz(scene_value)
if not alist then
return false
end
local iter, index= alist.find("unique_id", unique_id)
if iter then
iter.free()
alist.rm(index)
end
alist.free()
property.remove()
return true
end
if delete_tick_value <= 0.015 then
return false
end
delete_tick=os.clock()
local alist= get_scene_cz(scene_value)
local iter= alist.find("source", audible_value)
if not iter then
alist.free()
return false
else
iter.int("muted", value and 1 or 0)
iter.free()
end
if value then
vct.title(audible_value .. " ( muted )")
else
vct.title(audible_value .. " ( unmuted )")
end
alist.free()
InitAction()
return true
end)
end
if vopt == "" then
for _, nn in pairs(obs.scene:names()) do
local opv= obs.script.get(nn)
if opv then
opv.show()
end
end
end
opera_list.show()
return true
end)
audible_list = add_options.add.options("audible_source","Audible")
audible_list.add.str("-- Click to view options --", "")
for _, source_name in ipairs(obs.front.source_names()) do
local source= obs.front.source(source_name)
local source_output_flags= obslua.obs_source_get_output_flags(source.data)
local is_audible_source= bit.band(source_output_flags, obslua.OBS_SOURCE_AUDIO) ~= 0
if is_audible_source then
audible_list_count= audible_list_count + 1
audible_list.add.str(source_name, source_name)
end
source.free()
end
if audible_list_count <= 0 then
audible_list.add.str("No Audible Sources Found", "none").cursor().disable()
end
local scene_list= add_options.add.options("scene_name","Scene")
scene_list.add.str("-- Click to view scenes --", "")
-- [[ Populate scene list and show all the audibles ]]
for _, scene_name in ipairs(obs.scene:names()) do
local op= obs.script.create()
local opera=obs.script.group(p, scene_name, "" .. scene_name .. "", op)
opera.hide()
--obs.script.label(op, "label_" .. tostring(scene_name), "-" .. tostring(scene_name).."")
scene_list.add.str(scene_name, scene_name)
local surs= obs.utils.settings.obj("m0073r_" .. scene_name .. "_scene_t")
if surs and surs.data ~= nil then
local alist= surs.get_arr("m0073r_clz_obs_arr_t")
if alist and alist.data ~= nil then
for itm in alist.next() do
local audible_source= itm.get_str("source")
local muted_state= itm.get_int("muted")
local scene_value= itm.get_str("scene")
local unique_id= itm.get_str("unique_id")
local mute_action = obs.script.bool(op, audible_source .. "_" .. scene_value, audible_source)
if muted_state >= 2 then
mute_action.title(audible_source .. " ( undefined state )")
elseif muted_state == 1 then
mute_action.title(audible_source .. " ( muted )")
else
mute_action.title(audible_source .. " ( unmuted )")
end
local delete_tick=os.clock()
mute_action.onchange(function(value, property)
local delete_tick_value=os.clock() - delete_tick
if delete_tick_value >= 0.15 and delete_tick_value <= 0.35 then
-- delete the item from list
local alist= get_scene_cz(scene_name)
if not alist then
return false
end
local iter, index= alist.find("unique_id", unique_id)
if iter then
iter.free()
alist.rm(index)
end
alist.free()
property.remove()
return true
end
if delete_tick_value <= 0.015 then
return false
end
delete_tick=os.clock()
local alist= get_scene_cz(scene_name)
local iter= alist.find("source", audible_source)
if not iter then
alist.free()
return false
else
iter.int("muted", value and 1 or 0)
iter.free()
end
if value then
mute_action.title(audible_source .. " ( muted )")
else
mute_action.title(audible_source .. " ( unmuted )")
end
alist.free()
InitAction()
return true
end)
itm.free()
end
alist.free()
end
surs.free()
end
end
if obs.utils.settings.str("opera_view_list") == "" then
for _, nn in pairs(obs.scene:names()) do
local opv= obs.script.get(nn)
if opv then
opv.show()
end
end
elseif obs.utils.settings.str("opera_view_list") ~= "" then
local opv= obs.script.get(obs.utils.settings.str("opera_view_list"))
if opv then
opv.show()
end
end
return p
end
function InitAction()
local scene_name= obs.scene:name()
local alist= get_scene_cz(scene_name)
if not alist then
return
end
local shared_iixisii= obs.shared.api("iixisii-shared-api-vs1.0.0")
for itm in alist.next() do
local audible_source_name= itm.get_str("source")
local muted_state= itm.get_int("muted")
local source= obs.front.source(audible_source_name)
local source_name= itm.str("source")
local id= itm.str("unique_id")
local scene_name= itm.str("scene")
local id_value1=scene_name .. "_" .. source_name .. "_" .. id .. "_muted"
local id_value2=scene_name .. "_" .. source_name .. "_" .. id .. "_unmuted"
local dr1= shared_iixisii.find("key", id_value1)
local dr2= shared_iixisii.find("key", id_value2)
if source and source.data ~= nil then
if muted_state == 1 then
obslua.obs_source_set_muted(source.data, true)
if dr1 and dr1.data then
dr1.bul("value", true)
end
if dr2 and dr2.data then
dr2.bul("value", false)
end
else
if dr1 and dr1.data then
dr1.bul("value", false)
end
if dr2 and dr2.data then
dr2.bul("value", true)
end
obslua.obs_source_set_muted(source.data, false)
end
end
dr1.free();dr2.free()
source.free()
itm.free()
end
shared_iixisii.save();shared_iixisii.free()
alist.free()
end
--[[
Author: iixisii
contact: @iixisii
]]
-- [[ OBS CUSTOM API BEGIN ]]
-- [[ OBS CUSTOM CALLBACKS ]]
function script_load(settings)
obs.script_shutdown=false
settings= obs.PairStack(settings)
obs.utils.settings= settings
if setup and type(setup) == "function" then
setup(settings)
end
for _, filter in pairs(obs.utils.filters) do
obslua.obs_register_source(filter)
end
end
function script_save(settings)
-- [[ OBS REGISTER HOTKEY SAVE DATA]]
for name, iter in pairs(obs.register.hotkey_id_list) do
local new_data= obslua.obs_hotkey_save(iter.id)
if new_data then
obs.utils.settings.arr(name, new_data)
obslua.obs_data_array_release(new_data)
end
end
-- [[ OBS REGISTER HOTKEY SAVE DATA END]]
end
function script_unload()
obs.utils.script_shutdown=true
end
-- [[ OBS CUSTOM CALLBACKS END ]]
obs={utils={script_shutdown=false,
OBS_SCENEITEM_TYPE = 1;OBS_SRC_TYPE = 2;OBS_OBJ_TYPE = 3;
OBS_ARR_TYPE = 4;OBS_SCENE_TYPE = 5;OBS_SCENEITEM_LIST_TYPE = 6;
OBS_SRC_LIST_TYPE = 7;OBS_UN_IN_TYPE = -1;table={};
expect_wrapper={},
properties={
list={};options={};
};filters={}};scene={};client={};mem={};script={},enum={
path={
read=obslua.OBS_PATH_FILE;write=obslua.OBS_PATH_FILE_SAVE;folder=obslua.OBS_PATH_DIRECTORY
};
button={
default=obslua.OBS_BUTTON_DEFAULT;url=obslua.OBS_BUTTON_URL;
};list={
string=obslua.OBS_EDITABLE_LIST_TYPE_STRINGS;
url=obslua.OBS_EDITABLE_LIST_TYPE_FILES_AND_URLS;
file=obslua.OBS_EDITABLE_LIST_TYPE_FILES
};
text={
error=obslua.OBS_TEXT_INFO_ERROR;
default=obslua.OBS_TEXT_INFO;
warn=obslua.OBS_TEXT_INFO_WARNING;
input=obslua.OBS_TEXT_DEFAULT;password=obslua.OBS_TEXT_PASSWORD;
textarea=obslua.OBS_TEXT_MULTILINE;
};group={
normal= obslua.OBS_GROUP_NORMAL;checked= obslua.OBS_GROUP_CHECKABLE;
};options={
string=obslua.OBS_COMBO_FORMAT_STRING; int=obslua.OBS_COMBO_FORMAT_INT;
float=obslua.OBS_COMBO_FORMAT_FLOAT;bool=obslua.OBS_COMBO_FORMAT_BOOL;
edit=obslua.OBS_COMBO_TYPE_EDITABLE;default=obslua.OBS_COMBO_TYPE_LIST;
radio=obslua.OBS_COMBO_TYPE_RADIO;
};number={
int=obslua.OBS_COMBO_FORMAT_INT;float=obslua.OBS_COMBO_FORMAT_FLOAT;
slider=1000;input=2000
}
},register={
hotkey_id_list={},event_id_list={}
};front={},shared={}};
bit= require('bit')
-- dkjson= require('dkjson')
math.randomseed(os.time())
-- schedule an event
scheduled_events = {}
-- [[ MEMORY MANAGE API ]]
function obs.utils.scheduler(timeout)
-- if type(timeout) ~= "number" or timeout < 0 then
-- return obs.script_log(obslua.LOG_ERROR, "[Scheduler] invalid timeout value")
-- end
local scheduler_callback = nil
local function interval()
obslua.timer_remove(interval)
if type(scheduler_callback) ~= "function" then
return
end
return scheduler_callback(scheduler_callback)
end
local self = nil; self = {
after = function(callback)
if type(callback) == "function" or type(timeout) ~= "number" or timeout < 0 then
scheduler_callback = callback
else
obslua.script_log(obslua.LOG_ERROR, "[Scheduler] invalid callback/timeout " .. type(callback))
return false
end
obslua.timer_add(interval, timeout)
end;push = function(callback)
if callback == nil or type(callback) ~= "function" then
obslua.script_log(obslua.LOG_WARNING, "[Scheduler] invalid callback at {push} " .. type(callback))
return false
end
obslua.timer_add(callback, timeout)
table.insert(scheduled_events, callback)
return {
clear = function()
if callback == nil or type(callback) ~= "function" then
return nil
end
return obslua.timer_remove(callback)
end;
}
end; clear = function()
if scheduler_callback ~= nil then
obslua.timer_remove(scheduler_callback)
end
for _, clb in pairs(scheduled_events) do
obslua.timer_remove(clb)
end
scheduled_events = {}; scheduler_callback = nil
end;update=function(timeout_t)
if type(timeout_t) ~= "number" or timeout_t < 0 then
obslua.script_log(obslua.LOG_ERROR, "[Scheduler] invalid timeout value")
return false
end
timeout= timeout_t
return self
end
}
return self
end
function obs.wrap(object, object_type)
local self = nil
self = {
type = object_type, data = object;item=object;
get_source=function()
if self.type == obs.utils.OBS_SRC_TYPE then
return self.data
elseif self.type == obs.utils.OBS_SCENEITEM_TYPE then
return obslua.obs_sceneitem_get_source(self.data)
else
return self.data
end
end;released=false;
free = function()
if self.data == nil or self.released then
return
end
if self.type == obs.utils.OBS_SCENE_TYPE then
obslua.obs_scene_release(self.data)
self.data = nil;self.item=nil;self.released=true
elseif self.type == obs.utils.OBS_SRC_TYPE then
obslua.obs_source_release(self.data)
self.data = nil;self.item=nil;self.released=true
elseif self.type == obs.utils.OBS_ARR_TYPE then
obslua.obs_data_array_release(self.data)
self.data = nil;self.item=nil;self.released=true
elseif self.type == obs.utils.OBS_OBJ_TYPE then
obslua.obs_data_release(self.data)
self.data = nil;self.item=nil;self.released=true
elseif self.type == obs.utils.OBS_SCENEITEM_TYPE then
obslua.obs_sceneitem_release(self.data)
self.data = nil;self.item=nil;self.released=true
elseif self.type == obs.utils.OBS_SCENEITEM_LIST_TYPE then
obslua.sceneitem_list_release(self.data)
self.data = nil;self.item=nil;self.released=true
elseif self.type == obs.utils.OBS_SRC_LIST_TYPE then
obslua.source_list_release(self.data)
self.data = nil;self.item=nil;self.released=true
elseif self.type == obs.utils.OBS_UN_IN_TYPE then
self.data = nil;self.item=nil;self.released=true
return
else
self.data = nil
end
end
}
table.insert(obs.utils.expect_wrapper, self)
return self
end
function obs.shared.api(named_api)
local arr_data_t= nil
local function init_obs_data_t()
for _, scene_name in pairs(obs.scene:names()) do
local a_scene= obs.scene:get_scene(scene_name)
if a_scene and a_scene.source then
local s_data_t= obs.PairStack(
obslua.obs_source_get_settings(a_scene.source)
)
if not s_data_t or s_data_t.data == nil then
a_scene.free()
else
if arr_data_t and arr_data_t.data then
-- replace data to the current
s_data_t.arr(named_api, arr_data_t.data)
else
-- register data to the current
arr_data_t= s_data_t.arr(named_api)
if not arr_data_t or arr_data_t.data == nil then
arr_data_t= obs.ArrayStack()
s_data_t.arr(named_api, arr_data_t.data)
arr_data_t.free()
arr_data_t=nil
end
end
s_data_t.free()
a_scene.free()
end
end
end
if not arr_data_t or arr_data_t.data == nil then
arr_data_t= obs.ArrayStack()
end
end
init_obs_data_t()
function arr_data_t.save()
init_obs_data_t()
end
function arr_data_t.del()
local del_count=0
for _, scene_name in pairs(obs.scene:names()) do
local a_scene= obs.scene:get_scene(scene_name)
if a_scene and a_scene.source then
local s_data_t= obs.PairStack(
obslua.obs_source_get_settings(a_scene.source)
)
if not s_data_t or s_data_t.data == nil then
a_scene.free()
else
s_data_t.del(named_api)
del_count=del_count+1
end
end
end
return del_count
end
-- obs.utils.table.append(obj_data_t, arr_data_t)
return arr_data_t
end
function obs.expect(callback)
return function(...)
local args = {...}
local data = nil
local caller = ""
for i, v in ipairs(args) do
if caller ~= "" then
caller = caller .. ","
end
caller = caller .. "args[" .. tostring(i) .. "]"
end
caller = "return function(callback,args) return callback(" .. caller .. ") end";
local run = loadstring(caller)
local success, result = pcall(function()
data = run()(callback, args)
end)
local free_count=0
if not success then
for _, iter in pairs(obs.utils.expect_wrapper) do
if iter and type(iter.free) == "function" then
local s, r = pcall(function()
iter.free()
end)
if s then
free_count = free_count + 1
end
end
end
obslua.script_log(obslua.LOG_ERROR, "[ErrorWrapper ERROR] => " .. tostring(result))
end
return data
end
end
-- array handle
function obs.ArrayStack(stack, name, fallback)
if fallback == nil then
fallback=true
end
local self = nil
self = {
index = 0;get = function(index)
if type(index) ~= "number" or index < 0 or index > self.size() then
return nil
end
return obs.PairStack(obslua.obs_data_array_item(self.data, index), nil, true)
end;next = obs.expect(function(__index)
if type(self.index) ~= "number" or self.index < 0 or self.index > self.size() then
return assert(false,"[ArrayStack] Invalid data provided or corrupted data for (" .. tostring(name)..")")
end
return coroutine.wrap(function()
if self.size() <= 0 then
return nil
end
local i =0
if __index == nil or type(__index) ~= "number" or __index < 0 or __index > self.size() then
__index=0
end
for i=__index, self.size()-1 do
coroutine.yield(obs.PairStack(
obslua.obs_data_array_item(self.data, i), nil, false
))
end
end)
-- local temp = self.index;self.index = self.index + 1
-- return obs.PairStack(obslua.obs_data_array_item(self.data, temp), nil, true)
end);find= function(key, value)
local index=0
for itm in self.next() do
if itm.get_str(key) == value or itm.get_int(key) == value
or itm.get_bul(key) == value or itm.get_dbl(key) == value then
return itm, index
end
index = index + 1
itm.free()
end
return nil, nil
end;
free = function()
if self.data == nil then
return false
end
obslua.obs_data_array_release(self.data)
self.data = nil
return true
end;insert = obs.expect(function(value)
if value == nil or type(value) ~= "userdata" then
obslua.script_log("FAILED TO INSERT OBJECT INTO [ArrayStack]")
return false
end
obslua.obs_data_array_push_back(self.data, value)
return self
end); size = obs.expect(function()
if self.data == nil then
return 0
end
return obslua.obs_data_array_count(self.data);
end); rm= obs.expect(function(idx)
if idx < 0 or self.size() <=0 or idx > self.size() then
obslua.script_log("FAILED TO RM DATA FROM [ArrayStack] (INVALID INDEX)")
return false
end
obslua.obs_data_array_erase(self.data, idx)
return self
end)
}
if stack and name then
self.data = obslua.obs_data_get_array(stack, name)
elseif not stack and fallback then
self.data = obslua.obs_data_array_create()
else
self.data = stack
end
table.insert(obs.utils.expect_wrapper, self)
return self
end
-- pair stack used to manage memory stuff :)
function obs.PairStack(stack, name, fallback)
if fallback == nil then
fallback=true
end
local self = nil; self = {
free = function()
if self.data == nil then
return false
end
obslua.obs_data_release(self.data)
self.data = nil
return true
end; str = obs.expect(function(name, value, def)
if name and value == nil then
return self.get_str(name)
end
if (name == nil or type(name) ~= "string" or name == "") or (self.data == nil or type(self.data) ~= "userdata") or (value == nil or type(value) ~="string") then
obslua.script_log(obslua.LOG_ERROR,"FAILED TO INSERT STR INTO [PairStack] " .. "FOR [" .. tostring(name) .. "] " .. " OF VALUE [" .. tostring(value) .. "] TYPE: " .. tostring(type(value)))
return false
end
if def then
obslua.obs_data_set_default_string(self.data, name, value)
else
obslua.obs_data_set_string(self.data, name, value)
end
return self
end);int = obs.expect(function(name, value, def)
if name and value == nil then
return self.get_int(name)
end
if (name == nil or type(name) ~= "string" or name == "") or (self.data == nil or type(self.data) ~= "userdata") or (value == nil or type(value) ~="number") then
obslua.script_log(obslua.LOG_ERROR,"FAILED TO INSERT INT INTO [PairStack] " .. "FOR [" .. tostring(name) .. "] " .. " OF VALUE [" .. tostring(value) .. "] TYPE: " .. tostring(type(value)))
return false
end
if def then
obslua.obs_data_set_default_int(self.data, name, value)
else
obslua.obs_data_set_int(self.data, name, value)
end
return self
end);dbl=obs.expect(function(name, value, def)
if name and value == nil then
return self.get_dbl(name)
end
if (name == nil or type(name) ~= "string" or name == "") or (self.data == nil or type(self.data) ~= "userdata") or (value == nil or type(value) ~="number") then
obslua.script_log(obslua.LOG_ERROR,"FAILED TO INSERT INT INTO [PairStack] " .. "FOR [" .. tostring(name) .. "] " .. " OF VALUE [" .. tostring(value) .. "] TYPE: " .. tostring(type(value)))
return nil
end
if def then
obslua.obs_data_set_default_double(self.data, name, value)
else
obslua.obs_data_set_double(self.data, name, value)
end
return self
end);bul = obs.expect(function(name, value, def)
if name and value == nil then
return self.get_bul(name)
end
if (name == nil or type(name) ~= "string" or name == "") or (self.data == nil or type(self.data) ~= "userdata") or (type(value) == "nil" or type(value) ~="boolean") then
obslua.script_log(obslua.LOG_ERROR,"FAILED TO INSERT BUL [PairStack] " .. "FOR [" .. tostring(name) .. "] " .. " OF VALUE [" .. tostring(value) .. "] TYPE: " .. tostring(type(value)))
return nil
end
if def then
obslua.obs_data_set_default_bool(self.data, name, value)
else
obslua.obs_data_set_bool(self.data, name, value)
end
return self
end); arr = obs.expect(function(name, value, def)
if name and value == nil then
return self.get_arr(name)
end
if (name == nil or type(name) ~= "string" or name == "") or (self.data == nil or type(self.data) ~= "userdata") or (type(value) ~="userdata") then
obslua.script_log(obslua.LOG_ERROR,"FAILED TO INSERT ARR INTO [PairStack] " .. "FOR [" .. tostring(name) .. "] " .. " OF VALUE [" .. tostring(value) .. "] TYPE: " .. tostring(type(value)))
return nil
end
if def then
obslua.obs_data_set_default_array(self.data, name, value)
else
obslua.obs_data_set_array(self.data, name, value)
end
return self
end); obj = obs.expect(function(name, value, def)
if name and value == nil then
return self.get_obj(name)
end
if (name == nil or type(name) ~= "string" or name == "") or (type(self.data) ~= "userdata") or (type(value) ~="userdata") then
obslua.script_log(obslua.LOG_ERROR,"FAILED TO INSERT OBJ INTO [PairStack] " .. "FOR [" .. tostring(name) .. "] " .. " OF VALUE [" .. tostring(value) .. "] TYPE: " .. tostring(type(value)))
return nil
end
if def then
obslua.obs_data_set_default_obj(self.data, name, value)
else
obslua.obs_data_set_obj(self.data, name, value)
end
return self
end);
-- getter
get_str = obs.expect(function(name, def)
if (name == nil or type(name) ~= "string" or name == "") or (type(self.data) ~= "userdata")then
obslua.script_log(obslua.LOG_ERROR,"FAILED TO GET STR FROM [PairStack] " .. "FOR [" .. tostring(name) .. "] " .. " OF VALUE [" .. tostring(value) .. "] TYPE: " .. tostring(type(value)))
return nil
end
if not def then
return obslua.obs_data_get_string(self.data, name)
else
return obslua.obs_data_get_default_string(self.data, name)
end
end);get_int = obs.expect(function(name, def)
if (name == nil or type(name) ~= "string" or name == "") or (type(self.data) ~= "userdata")then
obslua.script_log(obslua.LOG_ERROR,"FAILED TO GET INT FROM [PairStack] " .. "FOR [" .. tostring(name) .. "] " .. " OF VALUE [" .. tostring(value) .. "] TYPE: " .. tostring(type(value)))
return nil
end
if not def then
return obslua.obs_data_get_int(self.data, name)
else
return obslua.obs_data_get_default_int(self.data, name)
end
end);get_dbl = obs.expect(function(name, def)
if (name == nil or type(name) ~= "string" or name == "") or (type(self.data) ~= "userdata")then
obslua.script_log(obslua.LOG_ERROR,"FAILED TO GET DBL FROM [PairStack] " .. "FOR [" .. tostring(name) .. "] " .. " OF VALUE [" .. tostring(value) .. "] TYPE: " .. tostring(type(value)))
return nil
end
if not def then
return obslua.obs_data_get_double(self.data, name)
else
return obslua.obs_data_get_default_double(self.data, name)
end
end);get_obj = obs.expect(function(name, def)
if (name == nil or type(name) ~= "string" or name == "") or (type(self.data) ~= "userdata")then
obslua.script_log(obslua.LOG_ERROR,"FAILED TO GET OBJ FROM [PairStack] " .. "FOR [" .. tostring(name) .. "] " .. " OF VALUE [" .. tostring(value) .. "] TYPE: " .. tostring(type(value)))
return nil
end
if not def then
return obs.PairStack(
obslua.obs_data_get_obj(self.data, name),nil, false
)
else
return obs.PairStack(
obslua.obs_data_get_default_obj(self.data, name),nil, false
)
end
end);get_arr = obs.expect(function(name, def)
if (name == nil or type(name) ~= "string" or name == "") or (type(self.data) ~= "userdata")then
obslua.script_log(obslua.LOG_ERROR,"FAILED TO GET ARR FROM [PairStack] " .. "FOR [" .. tostring(name) .. "] " .. " OF VALUE [" .. tostring(value) .. "] TYPE: " .. tostring(type(value)))
return nil
end
if not def then
return obs.ArrayStack(
obslua.obs_data_get_array(self.data, name),nil, false
)
else
return obs.ArrayStack(obslua.obs_data_get_default_array(self.data, name),nil, false)
end
end);get_bul = obs.expect(function(name, def)
if (name == nil or type(name) ~= "string" or name == "") or (type(self.data) ~= "userdata") then
obslua.script_log(obslua.LOG_ERROR,"FAILED TO GET BUL FROM [PairStack] " .. "FOR [" .. tostring(name) .. "] " .. " OF VALUE [" .. tostring(value) .. "] TYPE: " .. tostring(type(value)))
return nil
end
if not def then
return obslua.obs_data_get_bool(self.data, name)
else
return obslua.obs_data_get_default_bool(self.data, name)
end
end); del= obs.expect(function(name)
obslua.obs_data_erase(self.data, name)
return true
end);
}
if stack and name then
self.data = obslua.obs_data_get_obj(stack, name)
elseif not stack and fallback then
self.data = obslua.obs_data_create()
else
self.data = stack
end
table.insert(obs.utils.expect_wrapper, self)
return self
end
-- [[ MEMORY MANAGE API END ]]
-- [[ OBS REGISTER CUSTOM API]]
function obs.register.hotkey(unique_id, title, callback)
local script_path_value= script_path()
unique_id= tostring(script_path_value) .. "_" .. tostring(unique_id)
local hotkey_id= obslua.obs_hotkey_register_frontend(
unique_id, title, callback
)
-- load from data
local hotkey_load_data= obs.utils.settings.get_arr(unique_id);
if hotkey_load_data and hotkey_load_data.data ~= nil then
obslua.obs_hotkey_load(hotkey_id, hotkey_load_data.data)
hotkey_load_data.free()
end
obs.register.hotkey_id_list[unique_id]= {
id= hotkey_id, title= title, callback= callback,
remove=function(rss)
if rss == nil then
rss= false
end
-- obs.utils.settings.del(unique_id)
if rss then
if obs.register.hotkey_id_list[unique_id] and type(obs.register.hotkey_id_list[unique_id].callback) == "function" then
obslua.obs_hotkey_unregister(
obs.register.hotkey_id_list[unique_id].callback
)
end
end
obs.register.hotkey_id_list[unique_id]= nil
end
}
return obs.register.hotkey_id_list[unique_id]
end
function obs.register.get_hotkey(unique_id)
unique_id= tostring(script_path()) .. "_" .. tostring(unique_id)
if obs.register.hotkey_id_list[unique_id] then
return obs.register.hotkey_id_list[unique_id]
end
return nil
end
function obs.register.event(unique_id, callback)
if not callback and unique_id and type(unique_id) == "function" then
callback= unique_id
unique_id= tostring(script_path()) .. "_" .. obs.utils.get_unique_id(3) .. "_event"
else
unique_id= tostring(script_path()) .. "_" .. tostring(unique_id) .. "_event"
end
if type(callback) ~= "function" then
obslua.script_log(obslua.LOG_ERROR, "[OBS REGISTER EVENT] Invalid callback provided")
return nil
end
local event_id= obslua.obs_frontend_add_event_callback(callback)
obs.register.event_id_list[unique_id]= {
id= event_id,callback= callback,
unique_id= unique_id,
remove= function(rss)
if rss == nil then
rss= false
end
if rss then obslua.obs_frontend_remove_event_callback(callback) end
obs.register.event_id_list[unique_id]= nil
end
};
end
function obs.register.get_event(unique_id)
unique_id= tostring(script_path()) .. "_" .. tostring(unique_id) .. "_event"
if obs.register.event_id_list[unique_id] then
return obs.register.event_id_list[unique_id]
end
return nil
end
-- [[ OBS REGISTER CUSTOM API END]]
-- [[ OBS FILTER CUSTOM API]]
function obs.script.filter(filter)
local self;self={
id= filter and filter.id or obs.utils.get_unique_id(3),
type= filter and filter.type or obslua.OBS_SOURCE_TYPE_FILTER,
output_flags= filter and filter.output_flags or bit.bor(
obslua.OBS_SOURCE_VIDEO
),
get_height=function(src)
return src and src.height or 0
end, get_width= function(src)
return src and src.width or 0
end,update= function(_, settings)
if filter and type(filter) == "table" and
filter["update"] and type(filter["update"]) == "function" then
return filter.update(_, obs.PairStack(settings))
end
end, create= function(settings, source)
function get_sceneitem()
local a_source=obslua.obs_filter_get_target(source)
while true do
local temp= obslua.obs_filter_get_target(a_source)
if temp == nil then break end
a_source= temp
end
if not a_source then return nil end
local source_name= obslua.obs_source_get_name(a_source)
local a_scene= obs.scene:get_scene()
if not a_scene then
return nil
end
local a_scene_item= a_scene.get(source_name)
a_scene.free()
return a_scene_item
end
if filter and type(filter) == "table" and filter["create"]
and type(filter["create"]) == "function" then
local __a_sceneitem=get_sceneitem()
local src=filter.create(
obs.PairStack(settings),
__a_sceneitem
)
if __a_sceneitem and __a_sceneitem.data then __a_sceneitem.free() end
if src ~= nil and type(src) == "table" then
src.sceneitem=get_sceneitem
self.src=src;src.filter=source
src.is_custom=true
if filter and filter["setup"] and type(filter["setup"]) == "function" then
filter.setup(src)
end
return src
end
end
-- default creation
local src= {
source= source,filter=source,
params=nil,height=nil,isAlive=true,
width=nil,settings=obs.PairStack(settings),
sceneitem= get_sceneitem, item=get_sceneitem()
}
-- get width and height of source
if source ~= nil then
local target= obslua.obs_filter_get_target(source)
if target ~= nil then
src.width= obslua.obs_source_get_base_width(target)
src.height= obslua.obs_source_get_base_height(target)
end
else
src.width= 0;src.height= 0
end
shader = [[
uniform float4x4 ViewProj;
uniform texture2d image;
uniform int width;
uniform int height;
sampler_state textureSampler {
Filter = Linear;
AddressU = Border;
AddressV = Border;
BorderColor = 00000000;
};
struct VertData
{
float4 pos : POSITION;
float2 uv : TEXCOORD0;
};
float4 ps_get(VertData v_in) : TARGET
{
return image.Sample(textureSampler, v_in.uv.xy);
}
VertData VSDefault(VertData v_in)
{
VertData vert_out;
vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj);
vert_out.uv = v_in.uv;
return vert_out;
}
technique Draw
{
pass
{
vertex_shader = VSDefault(v_in);
pixel_shader = ps_get(v_in);
}
}
]]
obslua.obs_enter_graphics()
src.shader= obslua.gs_effect_create(shader, nil, nil)
obslua.obs_leave_graphics()
if src.shader ~= nil then
src.params= {
width= obslua.gs_effect_get_param_by_name(src.shader, "width"),
height= obslua.gs_effect_get_param_by_name(src.shader, "height"),
image= obslua.gs_effect_get_param_by_name(src.shader, "image"),
}
else
return self.destroy()
end
if filter and filter["setup"] and type(filter["setup"]) == "function" then
filter.setup(src)
end
self.src=src
return src
end, destroy= function(src)
src.isAlive= false
if filter and type(filter) == "table" and filter["destroy"]
and type(filter["destroy"]) == "function" then
for i, v in pairs(obs.utils.filters) do
if v == self then
table.remove(obs.utils.filters, i)
break
end
end
local result= filter.destroy(src)
if src and src.item and src.item.data then
src.item.free()
end
if src and src.is_custom then
return result
end
end
-- default destruction
if src and src.item and src.item.data then
src.item.free()
end
if src and type(src) == "table" and src.shader then
obslua.obs_enter_graphics()
obslua.gs_effect_destroy(src.shader)
obslua.obs_leave_graphics()
end
for i, v in pairs(obs.utils.filters) do
if v == self then
table.remove(obs.utils.filters, i)
break
end
end
end,video_tick=function(src, fps)
-- get width and height of source
if src.source ~= nil then
local target= obslua.obs_filter_get_target(src.source)
if target ~= nil then
src.width= obslua.obs_source_get_base_width(target)
src.height= obslua.obs_source_get_base_height(target)
else
src.width= 0;src.height= 0
end
else
src.width= 0;src.height= 0
end
-- call user-defined video_tick function
if filter and type(filter) == "table" and filter["video_tick"]
and type(filter["video_tick"]) == "function" then
filter.video_tick(src, fps)
end
end,
video_render= function(src)
if filter and type(filter) == "table" and filter["video_render"] and
type(filter["video_render"]) == "function" then
local result = filter.video_render(src)
if src.is_custom then
return result
end
end
-- default render
if not src or not src.source or not src.shader or not src.params then
return
end
if not src.item or not src.item.data then
local target= obslua.obs_filter_get_target(src.source)
if target ~= nil then
src.item= src.sceneitem()
src.width= obslua.obs_source_get_base_width(target)
src.height= obslua.obs_source_get_base_height(target)
end
end
local width= src.width;local height= src.height
if not obslua.obs_source_process_filter_begin(
src.source,obslua.GS_RGBA, obslua.OBS_NO_DIRECT_RENDERING
) then
obslua.obs_source_skip_video_filter(src.source)
return nil
end
if not src.params then
obslua.obs_source_process_filter_end(src.source, src.shader, width, height)
return nil
end
if type(width) == "number" then
obslua.gs_effect_set_int(src.params.width, width)
end
if type(height) == "number" then
obslua.gs_effect_set_int(src.params.height, height)
end
obslua.gs_blend_state_push()
obslua.gs_blend_function(
obslua.GS_BLEND_ONE, obslua.GS_BLEND_INVSRCALPHA
)
if width and height then
obslua.obs_source_process_filter_end(src.source, src.shader, width, height)
end
obslua.gs_blend_state_pop()
return true
end,
get_name=function()
return filter and filter.name or "Custom Filter"
end,get_defaults=function(settings)
local defaults=nil
if filter and type(filter) == "table" and filter["get_defaults"]
and type(filter["get_defaults"]) == "function" then
defaults = filter.get_defaults
end
if filter and type(filter) == "table" and filter["defaults"] and type(filter["defaults"]) == "function" then
defaults = filter.defaults
end
if defaults and type(defaults) == "function" then
return defaults(obs.PairStack(settings))
end
end,get_properties=function(src)
local properties= nil
if filter and type(filter) == "table" and filter["get_properties"]
and type(filter["get_properties"]) == "function" then
properties = filter.get_properties
end
if filter and type(filter) == "table" and filter["properties"] and type(filter["properties"]) == "function" then
properties = filter.properties
end
if properties and type(properties) == "function" then
return properties(src)
end
return nil
end
}
table.insert(obs.utils.filters, self)
if not filter or not type(filter) == "table" then
filter={}
end
filter.get_name= self.get_name
if not filter.id then
filter.id= self.id
end
filter.get_width= self.get_width
filter.get_height= self.get_height
filter.type= self.type
filter.output_flags= self.output_flags
--filter.destroy= self.destroy
return filter
end
-- [[ OBS FILTER CUSTOM API END]]
--[[ OBS SCENE API CUSTOM ]]
function obs.scene:get_source(source_name)
if not source_name or not type(source_name) == "string" then
return nil
end
local source = obslua.obs_get_source_by_name(source_name)
if not source then
return nil
end
return obs.wrap(source, obs.utils.OBS_SRC_TYPE)
end
function obs.scene:get_scene(scene_name)
local scene;local source_scene;
if not scene_name or not type(scene_name) == "string" then
source_scene=obslua.obs_frontend_get_current_scene()
if not source_scene then
return nil
end
scene= obslua.obs_scene_from_source(source_scene)
else
source_scene= obslua.obs_get_source_by_name(scene_name)
if not source_scene then
return nil
end
scene=obslua.obs_scene_from_source(source_scene)
end
local obj_scene_t;obj_scene_t= {
group_names=function()
local scene_items_list = obs.wrap(
obslua.obs_scene_enum_items(scene),
obs.utils.OBS_SCENEITEM_LIST_TYPE
)
if scene_items_list == nil or scene_items_list.data == nil then
return nil
end
local list={}
for _, item in ipairs(scene_items_list.data) do
local source = obslua.obs_sceneitem_get_source(item)
if source ~= nil then
local sourceName = obslua.obs_source_get_name(source)
if obslua.obs_sceneitem_is_group(item) then
table.insert(list, sourceName)
end
end
end
scene_items_list.free()
return list
end;source_names=function(source_id_type)
local scene_nodes_name_list= {}
local scene_items_list = obs.wrap(
obslua.obs_scene_enum_items(scene),
obs.utils.OBS_SCENEITEM_LIST_TYPE
)
for _, item in ipairs(scene_items_list.data) do
local source = obslua.obs_sceneitem_get_source(item)
if source ~= nil then
local sourceName = obslua.obs_source_get_name(source)
if source_id_type == nil or type(source_id_type) ~= "string" or source_id_type == "" then
table.insert(scene_nodes_name_list, sourceName)
else
local sourceId = obslua.obs_source_get_id(source)
if sourceId == source_id_type then
table.insert(scene_nodes_name_list, sourceName)
end
end
source= nil
end
end
scene_items_list.free()
return scene_nodes_name_list
end;get= function(source_name)
if not scene then
return nil
end
local c=1
local scene_item;local scene_items_list = obs.wrap(
obslua.obs_scene_enum_items(scene),
obs.utils.OBS_SCENEITEM_LIST_TYPE
)
if scene_items_list == nil or scene_items_list.data == nil then
return nil
end
for _, item in ipairs(scene_items_list.data) do
c = c + 1
local src= obslua.obs_sceneitem_get_source(item)
local src_name= obslua.obs_source_get_name(src)
if src ~= nil and src_name == source_name then
obslua.obs_sceneitem_addref(item)
scene_item= obs.wrap(item, obs.utils.OBS_SCENEITEM_TYPE)
break
end
end
scene_items_list.free()
if scene_item == nil or scene_item.data == nil then
return nil
end
local obj_source_t;
obj_source_t={
free=scene_item.free;
item=scene_item.data;
data=scene_item.data;
get_source=function()
return obslua.obs_sceneitem_get_source(scene_item.data)
end;get_name= function()
return obslua.obs_source_get_name(obj_source_t.get_source())
end;
}
return obj_source_t
end;add=function(source)
if not source then return false end
local sceneitem= obslua.obs_scene_add(scene, source)
if sceneitem == nil then return nil end
obslua.obs_sceneitem_addref(sceneitem)
return obs.wrap(sceneitem, obs.utils.OBS_SCENEITEM_TYPE)
end;get_label=function(name, source)
if (source == nil or source.data == nil) and name ~= nil and type(name) == "string" and name ~= "" then
source= obj_scene_t.get(name)
end
if not source or not source.data then
return nil
end
local obj_label_t;obj_label_t={
remove= function()
if obj_label_t.data == nil then return true end
obslua.obs_sceneitem_remove(obj_label_t.data)
source.free(); obj_label_t.data=nil;obj_label_t.item=nil
return true
end;
hide= function()
return obslua.obs_sceneitem_set_visible(obj_label_t.data, false)
end;show = function()
return obslua.obs_sceneitem_set_visible(obj_label_t.data, true)
end;
font= {
size= function(font_size)
local src= obs.PairStack(
obslua.obs_source_get_settings(source.get_source()),
nil,true
)
if not src or not src.data then
src= obs.PairStack()
end
local font= src.get_obj("font")
if not font or not font.data then
font= obs.PairStack()
--font.str("face","Arial")
end
if font_size == nil or not type(font_size) == "number" or font_size <= 0 then
font_size=font.get_int("size")
font.free();src.free();
return font_size
else
font.int("size", font_size)
end
font.free();
obslua.obs_source_update(source.get_source(), src.data)
src.free()
return true
end;face= function(face_name)
end
};text=function(txt)
local src= obs.PairStack(
obslua.obs_source_get_settings(source.get_source()),
nil,true
)
if not src or not src.data then
src= obs.PairStack()
end
local res=true
if txt == nil or txt == "" or type(txt) ~= "string" then
res=src.get_str("text")
if not res == nil then
res= ""
end
else
src.str("text", txt)
end
obslua.obs_source_update(source.get_source(), src.data)
src.free()
return res
end;free=function()
source.free()
obj_label_t=nil
return true
end;data=source.data;item=source.data;size={
width= function(w)
--local default_transform= obslua.obs_transform_info()
--local default_source_info=obslua.obs_source_info()
--obslua.obs_source_get_info(source.get_source(), default_source_info)
--obslua.obs_sceneitem_get_info2(source.data, default_transform)
local default_width= obslua.obs_source_get_width(source.get_source())
--local default_scale_x= default_transform.scale.x;
if w == nil then return default_width end
return w
end;
height= function(h)
local default_height= obslua.obs_source_get_height(source.get_source())
if h == nil then return default_height end
return h
end;
};pos = {
x=function(val)
local default_transform= obslua.obs_transform_info()
obslua.obs_sceneitem_get_info2(source.data, default_transform)
if val == nil then return default_transform.pos.x end
default_transform.pos.x= val
obslua.obs_sceneitem_set_info(source.data, default_transform)
return true
end;
y=function(val)
local default_transform= obslua.obs_transform_info()
obslua.obs_sceneitem_get_info2(source.data, default_transform)
if val == nil then return default_transform.pos.y end
default_transform.pos.y= val
obslua.obs_sceneitem_set_info(source.data, default_transform)
return true
end;
}
}
return obj_label_t
end;
add_label= function(name, text)
local src= obs.PairStack()
if not text then
text= "Text - Label"
end
src.str("text", text)
local source_label=obslua.obs_source_create("text_gdiplus", name, src.data, nil)
src.free()
local obj= obj_scene_t.get_label(
nil, obj_scene_t.add(source_label)
)
if not obj or not obj.data then
if source_label then obslua.obs_source_release(source_label) end
return nil
end
-- re-write the release function
-- [[SEEM LIKE THIS LEADS TO CRUSHES?]]
local free_func= obj.free;
obj.free= function()
obslua.obs_source_release(source_label)
return free_func()
end
return obj
end;add_group= function(name, refresh)
if refresh == nil then
refresh=true
end
local obj=obj_scene_t.get_group(nil, obslua.obs_scene_add_group2(scene, name, refresh))
if not obj or obj.data == nil then return nil end
-- overwrite the free function to prevent crush/bugs
obj.free=function() end
return obj
end;get_group= function(name, gp)
local obj;if not gp and name ~= nil then
obj= obs.wrap(obslua.obs_scene_get_group(scene, name), obs.utils.OBS_SCENEITEM_TYPE)
elseif gp ~= nil then
obj= obs.wrap(gp, obs.utils.OBS_SCENEITEM_TYPE)
else
return nil
end
obj["add"]= function(sceneitem)
if not sceneitem then
return false
end
obslua.obs_sceneitem_group_add_item(obj.data, sceneitem)
return true
end
obj["release"]= function()
return obj.free()
end;obj["item"]= obj.data
return obj
end;free= function()
obslua.obs_source_release(source_scene)
scene=nil
end;release= function()
return obj_scene_t.free()
end;get_width= function()
return obslua.obs_source_get_base_width(source_scene)
end;get_height = function()
return obslua.obs_source_get_base_height(source_scene)
end;data=scene;item=scene;source=source_scene
};
return obj_scene_t
end
function obs.scene:name()
source_scene=obslua.obs_frontend_get_current_scene()
if not source_scene then
return nil
end
local source_name= obslua.obs_source_get_name(source_scene)
obslua.obs_source_release(source_scene)
return source_name
end
function obs.scene:add_to_scene(source)
if not source then
return false
end
local current_source_scene= obslua.obs_frontend_get_current_scene()
if not current_source_scene then
return false
end
local current_scene= obslua.obs_scene_from_source(current_source_scene)
if not current_scene then
obslua.obs_source_release(current_source_scene)
return false
end
obslua.obs_scene_add(current_scene, source)
obslua.obs_source_release(current_source_scene)
return true
end
function obs.scene:names()
local scenes= obs.wrap(
obslua.obs_frontend_get_scenes(),
obs.utils.OBS_SRC_LIST_TYPE
);
local obj_table_t= {}
for _, a_scene in pairs(scenes.data) do
if a_scene then
local scene_source_name= obslua.obs_source_get_name(a_scene)
table.insert(obj_table_t, scene_source_name)
end
end
scenes.free()
return obj_table_t
end
--[[ OBS SCENE API CUSTOM END ]]
-- [[ OBS FRONT API ]]
function obs.front.source_names()
local list={}
local all_sources= obs.wrap(
obslua.obs_enum_sources(),
obs.utils.OBS_SRC_LIST_TYPE
);
for _, source in pairs(all_sources.data) do
if source then
local source_name= obslua.obs_source_get_name(source)
table.insert(list, source_name)
end
end
all_sources.free()
return list
end
function obs.front.source(source_name)
if not source_name or not type(source_name) == "string" then
return nil
end
local source = obslua.obs_get_source_by_name(source_name)
if not source then
return nil
end
return obs.wrap(source, obs.utils.OBS_SRC_TYPE)
end
-- [[ OBS FRONT API END ]]
-- [[ OBS SCRIPT PROPERTIES CUSTOM API]]
function obs.script.create(settings)
local p= obslua.obs_properties_create()
if type(settings) == "userdata" then
settings= obs.PairStack(settings)
end
obs.utils.properties[p]= settings or nil
return p
end
function obs.script.options(p, unique_id, desc, enum_type_id, enum_format_id)
if not desc or type(desc) ~= "string" then
desc=""
end
if not unique_id or type(unique_id) ~= "string" or unique_id == "" then
unique_id= obs.utils.get_unique_id(20)
end
if enum_format_id == nil then
enum_format_id= obs.enum.options.string;
end
if enum_type_id == nil then
enum_type_id= obs.enum.options.default;
end
local obj=obslua.obs_properties_add_list(p, unique_id, desc, enum_type_id, enum_format_id);
if not obj then
obslua.script_log(obslua.LOG_ERROR, "[obsapi_custom.lua] Failed to create list property: " .. tostring(unique_id) .. " description: " .. tostring(desc) .. " enum_type_id: " .. tostring(enum_type_id) .. " enum_format_id: " .. tostring(enum_format_id))
return nil
end
obs.utils.properties.options[unique_id]= {
enum_format_id= enum_format_id;
enum_type_id= enum_type_id;type=enum_format_id
}
return obs.utils.obs_api_properties_patch(obj, p)
end
function obs.script.button(p, unique_id, label, callback)
if not label or type(label) ~= "string" then
label="button"
end
if not unique_id or type(unique_id) ~= "string" or unique_id == "" then
unique_id= obs.utils.get_unique_id(20)
end
if type(callback)~="function" then callback=function() end end
obs.utils.properties[unique_id]=obs.utils.obs_api_properties_patch(
obslua.obs_properties_add_button(p, unique_id, label, function(properties_t, property_t, obs_data_t)
return callback(properties_t, property_t, obs.PairStack(obs_data_t))
end)
,p)
return obs.utils.properties[unique_id]
end
function obs.script.label(p, unique_id, text, enum_type)
if not text or type(text) ~= "string" then
text=""
end
if not unique_id or type(unique_id) == nil or unique_id == "" or type(unique_id) ~= "string" then
unique_id= obs.utils.get_unique_id(20)
end
local default_enum_type= obslua.OBS_TEXT_INFO;
if(enum_type == nil) then
enum_type= default_enum_type
end
local obj= obs.utils.obs_api_properties_patch(obslua.obs_properties_add_text(p, unique_id, text, default_enum_type), p)
if enum_type == obs.enum.text.error then
obj.error(text)
elseif enum_type == obs.enum.text.warn then
obj.warn(text)
end
obj.type= enum_type;
obs.utils.properties[unique_id]= obj
return obj;
end
function obs.script.group(p, unique_id, desc, op, enum_type)
if not desc or type(desc) ~= "string" then
desc=""
end
if not unique_id or type(unique_id) ~= "string" or unique_id == "" then
unique_id= obs.utils.get_unique_id(20)
end
if enum_type == nil then
enum_type= obs.enum.group.normal;
end
obs.utils.properties[unique_id]= obs.utils.obs_api_properties_patch(obslua.obs_properties_add_group(p, unique_id, desc, enum_type, op), op)
return obs.utils.properties[unique_id]
end
function obs.script.bool(p, unique_id, desc)
if not desc or type(desc) ~= "string" then
desc=""
end
if not unique_id or type(unique_id) ~= "string" or unique_id == "" then
unique_id= obs.utils.get_unique_id(20)
end
obs.utils.properties[unique_id]=obs.utils.obs_api_properties_patch(obslua.obs_properties_add_bool(p, unique_id, desc), p)
return obs.utils.properties[unique_id]
end
function obs.script.path(p, unique_id, desc, enum_type_id, filter_string, default_path_string)
if not unique_id or type(unique_id) ~= "string" or unique_id == "" then
unique_id= obs.utils.get_unique_id(20)
end
if not desc or type(desc) ~= "string" then
desc= ""
end
if enum_type_id == nil or type(enum_type_id) ~= "number" then
enum_type_id= obs.enum.path.read
end
if filter_string == nil or type(filter_string) ~= "string" then
filter_string=""
end
if default_path_string == nil or type(default_path_string) ~= "string" then
default_path_string= ""
end
obs.utils.properties[unique_id]= obs.utils.obs_api_properties_patch(obslua.obs_properties_add_path(p, unique_id, desc, enum_type_id, filter_string, default_path_string), p)
return obs.utils.properties[unique_id]
end
function obs.script.form(properties, title, unique_id)
local pp= obs.script.create();local __exit_click_callback__=nil;local __onexit_type__=1;
local __cancel_click_callback__=nil;local __oncancel_type__=1;
if unique_id == nil then
unique_id=obs.utils.get_unique_id(20)
end
local group_form= obs.script.group(properties,unique_id, "", pp, obs.enum.group.normal)
local label= obs.script.label(pp, unique_id .. "_label", title, obslua.OBS_TEXT_INFO);
obs.script.label(pp,"form_tt","
", obslua.OBS_TEXT_INFO);
local ipp= obs.script.create()
local group_inner= obs.script.group(pp, unique_id .. "_inner", "", ipp, obs.enum.group.normal)
local exit= obs.script.button(pp, unique_id .. "_exit", "Confirm",function(pp, s, ss)
if __exit_click_callback__ and type(__exit_click_callback__) == "function" then
__exit_click_callback__(pp,s, ss)
end
if __onexit_type__ == -1 then
group_form.free()
elseif __onexit_type__ == 1 then
group_form.hide()
end
return true
end)
local cancel= obs.script.button(pp, unique_id .. "_cancel", "Cancel", function(pp, s, ss)
if __cancel_click_callback__ and type(__cancel_click_callback__) == "function" then
__cancel_click_callback__(pp,s, ss)
end
if __oncancel_type__ == -1 then
group_form.free()
elseif __oncancel_type__ == 1 then
group_form.hide()
end
return true
end)
local obj_t;obj_t={
add={
button= function(...)
return obs.script.button(ipp, ...)
end;options= function(...)
return obs.script.options(ipp,...)
end;label= function(...)
return obs.script.label(ipp,...)
end;group= function(...)
return obs.script.group(ipp, ...)
end;bool= function(...)
return obs.script.bool(ipp, ...)
end;path=function(...)
return obs.script.path(ipp,...)
end;input= function(...)
return obs.script.input(ipp, ...)
end;number=function(...)
return obs.script.number(ipp, ...)
end
};get= function(name)
return obs.script.get(ipp,name)
end;free= function()
group_form.free();
obslua.obs_properties_destroy(ipp);ipp=nil
obslua.obs_properties_destroy(pp);pp=nil
return true
end;data=ipp;item=ipp;confirm={};onconfirm={};oncancel={};cancel={}
}
function obj_t.confirm:click(clb)
__exit_click_callback__=clb
return obj_t
end;function obj_t.confirm:text(title_value)
if not title_value or type(title_value) ~= "string" or title_value == "" then
return false
end
exit.text(title_value)
return true
end
function obj_t.onconfirm:hide()
__onexit_type__= 1
return obj_t
end;function obj_t.onconfirm:remove()
__onexit_type__=-1
return obj_t
end;function obj_t.onconfirm:idle()
__onexit_type__= 0
return obj_t
end
function obj_t.cancel:click(clb)
__cancel_click_callback__=clb
return obj_t
end;function obj_t.cancel:text(txt)
if not txt or type(txt) ~= "string" or txt == "" then
return false
end
cancel.text(txt)
return true
end
function obj_t.oncancel:idle()
__oncancel_type__= 0
return obj_t
end;function obj_t.oncancel:remove()
__oncancel_type__= -1
return obj_t
end;function obj_t.oncancel:hide()
__oncancel_type__= 1
return obj_t
end
function obj_t.show()
return group_form.show();
end;function obj_t.hide()
return group_form.hide();
end;function obj_t.remove()
return obj_t.free()
end
obs.utils.properties[unique_id]= obj_t
return obj_t
end
function obs.script.fps(properties_t, unique_id, title)
if not title or type(title) ~= "string" then
title=""
end
if not unique_id or type(unique_id) ~= "string" or unique_id == "" then
unique_id= obs.utils.get_unique_id(20)
end
obs.utils.properties[unique_id]=obs.utils.obs_api_properties_patch(
obslua.obs_properties_add_frame_rate(properties_t, unique_id, title),
properties_t
)
return obs.utils.properties[unique_id]
end
function obs.script.list(properties_t, unique_id, title, enum_type_id, filter_string, default_path_string)
if not filter_string or type(filter_string) ~= "string" then
filter_string=""
end
if not default_path_string or type(default_path_string) ~= "string" then
default_path_string= ""
end
if not enum_type_id or type(enum_type_id) ~= "number" or (
enum_type_id ~= obs.enum.list.string
and enum_type_id ~= obs.enum.list.file and
enum_type_id ~= obs.enum.list.url
) then
enum_type_id= obs.enum.list.string
end
if not title or type(title) ~= "string" then
title=""
end
if not unique_id or type(unique_id) ~= "string" or unique_id == "" then
unique_id= obs.utils.get_unique_id(20)
end
obs.utils.properties[unique_id]=obs.utils.obs_api_properties_patch(
obslua.obs_properties_add_editable_list(properties_t, unique_id, title, enum_type_id, filter_string, default_path_string),
properties_t)
return obs.utils.properties[unique_id]
end
function obs.script.input(p, unique_id, title, enum_type_id, callback)
if not title or type(title) ~= "string" then
title=""
end
if not unique_id or type(unique_id) ~= "string" or unique_id == "" then
unique_id= obs.utils.get_unique_id(20)
end
if not enum_type_id == nil or (
enum_type_id ~= obs.enum.text.input and enum_type_id ~= obs.enum.text.textarea and
enum_type_id ~= obs.enum.text.password) then
enum_type_id= obs.enum.text.input
end
obs.utils.properties[unique_id]=obs.utils.obs_api_properties_patch(
obslua.obs_properties_add_text(p, unique_id, title, enum_type_id), p
)
return obs.utils.properties[unique_id]
end
function obs.script.color(properties_t, unique_id, title)
if not title or type(title) ~= "string" then
title=""
end
if not unique_id or type(unique_id) ~= "string" or unique_id == "" then
unique_id= obs.utils.get_unique_id(20)
end
obs.utils.properties[unique_id]=obs.utils.obs_api_properties_patch(obslua.obs_properties_add_color_alpha(properties_t, unique_id, title), properties_t)
return obs.utils.properties[unique_id]
end
function obs.script.number(properties_t, min, max,steps, unique_id, title, enum_number_type_id, enum_type_id)
if not enum_number_type_id then
enum_number_type_id= obs.enum.number.int
end
if not enum_type_id then
enum_type_id= obs.enum.number.input
end
if not unique_id or type(unique_id) ~= "string" or unique_id == "" then
unique_id= obs.utils.get_unique_id(20)
end
local obj;if enum_type_id == obs.enum.number.slider then
if enum_number_type_id == obs.enum.number.float then
obj= obs.utils.obs_api_properties_patch(obslua.obs_properties_add_float(
properties_t, unique_id, title, min, max,steps
))
else
obj= obs.utils.obs_api_properties_patch(obslua.obs_properties_add_int_slider(
properties_t, unique_id, title, min, max, steps
))
end
else
if enum_number_type_id == obs.enum.number.float then
obj= obs.utils.obs_api_properties_patch(obslua.obs_properties_add_float(
properties_t, unique_id, title, min, max,steps
))
else
obj= obs.utils.obs_api_properties_patch(obslua.obs_properties_add_int(
properties_t, unique_id, title, min, max, steps
))
end
end
if obj then
obj["type"]= enum_number_type_id
end
obs.utils.properties[unique_id]=obj
return obj
end
function obs.script.get(name)
return obs.utils.properties[name]
end
-- [[ OBS SCRIPT PROPERTIES CUSTOM API END ]]
-- [[ API UTILS ]]
function obs.utils.obs_api_properties_patch(pp,pp_t, cb)
-- if pp_t ~= nil and not obs.utils.properties[pp] then
-- obs.utils.properties[pp]=pp_t;
-- end
local pp_unique_name= obslua.obs_property_name(pp)
local obs_pp_t=pp; -- extra
-- onchange [Event Handler]
local __onchange_list={}
local item=nil;local objText;local objInput;local objGlobal;objGlobal={
cb=cb;disable=function()
obslua.obs_property_set_disabled(pp, true)
return nil
end;enable=function()
obslua.obs_property_set_disabled(obs_pp_t, false)
return nil
end;onchange=function(callback)
if type(callback) ~= "function" then
return false
end
table.insert(__onchange_list, callback)
return true
end;hide= function()
obslua.obs_property_set_visible(obs_pp_t, false)
end;show = function()
obslua.obs_property_set_visible(obs_pp_t, true)
return nil
end;get= function()
return obs_pp_t
end;hint= function(txt)
if txt == nil or type(txt) ~= "string" or txt == "" then
return obs_property_get_long_description(obs_pp_t)
end
item=obslua.obs_property_set_long_description(obs_pp_t, txt)
return nil
end;free= function()
obs.utils.properties[pp_unique_name]=nil
obslua.obs_properties_remove_by_name(pp_t, pp_unique_name)
return true
end;remove=function()
return objGlobal.free()
end;data=pp;item=pp;title=function(txt)
if txt == nil or type(txt) ~= "string" then
return obslua.obs_property_get_description(pp)
end
obslua.obs_property_set_description(pp, txt)
return objGlobal
end;parent=pp_t
};objText={
error=function(txt)
if txt == nil or type(txt) ~= "string" then
return obslua.obs_property_description(pp)
end
obslua.obs_property_text_set_info_type(pp, obslua.OBS_TEXT_INFO_ERROR)
obslua.obs_property_set_description(pp, txt)
return objText
end;
text=function(txt)
local id_name= obslua.obs_property_name(pp)
objText.type=obs.enum.text.default
obslua.obs_property_text_set_info_type(pp, objText.type)
if txt ~= nil and type(txt) == "string" then obslua.obs_property_set_description(pp, txt) end
return objText
end;warn=function(txt)
local id_name= obslua.obs_property_name(pp)
local textarea_id= id_name .. "_obsapi_hotfix_textarea"
local input_id= id_name .. "_obsapi_hotfix_input"
local property= obs.script.get(pp_t, id_name)
local textarea_property= obs.script.get(pp_t, textarea_id)
local input_property= obs.script.get(pp_t, input_id)
objText.type=obs.enum.text.input
property.show();input_property.hide();textarea_property.hide()
objText.type=obs.enum.text.warn
obslua.obs_property_text_set_info_type(pp, objText.type)
if txt ~= nil and type(txt) == "string" then obslua.obs_property_set_description(pp, txt) end
return objText
end;
-- textarea=obs.expect(function(txt)
-- local id_name= obslua.obs_property_name(pp)
-- local textarea_id= id_name .. "_obsapi_hotfix_textarea"
-- local input_id= id_name .. "_obsapi_hotfix_input"
-- local property= obs.script.get(pp_t, id_name)
-- local textarea_property= obs.script.get(pp_t, textarea_id)
-- obs_pp_t=textarea_property.get()
-- local input_property= obs.script.get(pp_t, input_id)
-- objText.type=obs.enum.text.textarea
-- property.hide();input_property.hide();textarea_property.show()
-- if txt ~= nil and type(txt) == "string" then
-- local settings=nil;
-- if pp_t and obs.utils.properties[pp_t] then
-- settings= obs.utils.properties[pp_t]
-- else
-- settings= obs.utils.settings
-- end
-- if settings then
-- settings.str(textarea_id, txt)
-- end
-- end
-- return objText
-- end);
type=-1
};objInput={
value=obs.expect(function(txt)
if txt ~= nil and type(txt) == "string" then
local settings=nil;
if pp_t and obs.utils.properties[pp_t] then
settings= obs.utils.properties[pp_t]
else
settings= obs.utils.settings
end
if settings then
settings.str(pp_unique_name, txt)
end
end
return objInput
end);type=-1
};
local objOption;objOption={
item=nil;clear= function()
objOption.item=obslua.obs_property_list_clear(pp)
return objOption
end;add={
str= function(title, id)
if id == nil or type(id) ~= "string" or id == "" then
--id= obs.utils.get_unique_id(20)
obslua.script_log(obslua.LOG_INFO, "[obs.script.options.str] id is nil or invalid!")
return objOption
end
objOption.item=obslua.obs_property_list_add_string(pp, title, id)
return objOption
end;int= function(title, id)
if id == nil or type(id) ~= "number" then
--id= obs.utils.get_unique_id(20)
obslua.script_log(obslua.LOG_INFO, "[obs.script.options.int] id is nil or invalid!")
return objOption
end
objOption.item=obslua.obs_property_list_add_int(pp, title, id)
return objOption
end;dbl=function(title, id)
if id == nil or type(id) ~= "number" then
--id= obs.utils.get_unique_id(20)
obslua.script_log(obslua.LOG_INFO, "[obs.script.options.dbl] id is nil or invalid!")
return objOption
end
objOption.item=obslua.obs_property_list_add_float(pp, title, id)
return objOption
end;bul=function(title, id)
if id == nil or type(id) ~= "boolean" then
id= obs.utils.get_unique_id(20)
end
objOption.item=obslua.obs_property_list_add_bool(pp, title, id)
return objOption
end
};cursor = function(index)
if index == nil or type(index) ~= "number" or index < 0 then
if type(index) == "string" then -- find the index by the id value
for i=0, obslua.obs_property_list_item_count(pp)-1 do
if obslua.obs_property_list_item_string(pp, i) == index then
index= i
break
end
end
if type(index) ~= "number" then
return nil
end
else
index= objOption.item;if type(index) ~= "number" or index < 0 then
index=obslua.obs_property_list_item_count(pp)-1
end
end
end
local info_title;local info_id
info_title=obslua.obs_property_list_item_name(pp, index)
if obs.utils.properties.options[pp_unique_name] and obs.utils.properties.options[pp_unique_name].enum_format_id == obs.enum.options.string then
info_id= obslua.obs_property_list_item_string(pp, index)
elseif obs.utils.properties.options[pp_unique_name] and obs.utils.properties.options[pp_unique_name].enum_format_id == obs.enum.options.int then
info_id= obslua.obs_property_list_item_int(pp, index)
elseif obs.utils.properties.options[pp_unique_name] and obs.utils.properties.options[pp_unique_name].enum_format_id == obs.enum.options.float then
info_id= obslua.obs_property_list_item_float(pp, index)
elseif obs.utils.properties.options[pp_unique_name] and obs.utils.properties.options[pp_unique_name].enum_format_id == obs.enum.options.bool then
info_id= obslua.obs_property_list_item_bool(pp, index)
else
info_id= nil
end
local nn_obj=nil;nn_obj={
disable= function()
obslua.obs_property_list_item_disable(pp, index, true)
return nn_obj
end; enable= function()
obslua.obs_property_list_item_disable(pp, index, false)
return nn_obj
end;remove=function()
obslua.obs_property_list_item_remove(pp, index)
return true
end;title=info_title;value=info_id;index=index;
ret=function()
return objOption
end;isDisabled=function()
return obslua.obs_property_list_item_disabled(pp, index)
end
}
return nn_obj;
end;current=function()
local current_selected_option=nil
local settings=nil;
if pp_t and obs.utils.properties[pp_t] then
settings= obs.utils.properties[pp_t]
else
settings= obs.utils.settings
end
if obs.utils.properties.options[pp_unique_name] and obs.utils.properties.options[pp_unique_name].enum_format_id == obs.enum.options.string then
current_selected_option= settings.str(pp_unique_name)
elseif obs.utils.properties.options[pp_unique_name] and obs.utils.properties.options[pp_unique_name].enum_format_id == obs.enum.options.int then
current_selected_option= settings.int(pp_unique_name)
elseif obs.utils.properties.options[pp_unique_name] and obs.utils.properties.options[pp_unique_name].enum_format_id == obs.enum.options.float then
current_selected_option= settings.float(pp_unique_name)
elseif obs.utils.properties.options[pp_unique_name] and obs.utils.properties.options[pp_unique_name].enum_format_id == obs.enum.options.bool then
current_selected_option= settings.bool(pp_unique_name)
end
return objOption.cursor(current_selected_option)
end
};local fr_rt= false
local objButton;objButton={
item=nil;click= function(callback)
if type(callback) ~= "function" then
obslua.script_log(obslua.LOG_ERROR, "[button.click] invalid callback type " .. type(callback) .. " expected function")
return objButton
end
objButton.item=obslua.obs_property_set_modified_callback(pp,function(properties_t, property_t, obs_data_t)
return callback(properties_t, property_t, obs.PairStack(obs_data_t))
end)
return objButton
end;text= function(txt)
if txt == nil or type(txt) ~= "string" or txt == "" then
return obslua.obs_property_description(pp)
end
obslua.obs_property_set_description(pp, txt)
return objButton
end;url=function(url)
if not url or type(url) ~= "string" or url == "" then
obslua.script_log(obslua.LOG_ERROR, "[button.url] invalid url type, expected string, got " .. type(url))
return objButton --obslua.obs_property_button_get_url(pp)
end
obslua.obs_property_button_set_url(pp, url)
return objButton
end;type=function(button_type)
if button_type == nil or (button_type ~= obs.enum.button.url and button_type ~= obs.enum.button.default) then
obslua.script_log(obslua.LOG_ERROR, "[button.type] invalid type, expected obs.enum.button.url | obs.enum.button.default, got " .. type(button_type))
return objButton --obslua.obs_property_button_get_type(pp)
end
obslua.obs_property_button_set_type(pp, button_type)
return objButton
end
};
local objGroup;objGroup={
};local objBool;objBool={
checked=function(bool_value)
local settings=nil;
if pp_t and obs.utils.properties[pp_t] then
settings= obs.utils.properties[pp_t]
else
settings= obs.utils.settings
end
if not settings then
obslua.script_log(obslua.LOG_ERROR, "[obs.utils.settings] is not set, please use 'script_load' to set it")
return nil
end
local property_id=obslua.obs_property_name(pp)
if bool_value == nil or type(bool_value) ~= "boolean" then
return settings.get_bul(property_id)
end
settings.bul(property_id, bool_value)
return objBool
end;
};local objColor;objColor={
value= obs.expect(function(r_color, g_color, b_color, alpha_value)
local settings=nil;
if pp_t and obs.utils.properties[pp_t] then
settings= obs.utils.properties[pp_t]
else
settings= obs.utils.settings
end
if r_color == nil then
return settings.int(pp_unique_name)
end
if type(r_color) ~= "number" or type(g_color) ~= "number" or type(b_color) ~= "number" then
return false
end
if alpha_value == nil then
alpha_value=1
end
local color_value = bit.bor(
bit.lshift(alpha_value * 255, 24),
bit.lshift(b_color, 16),
bit.lshift(g_color, 8),
r_color
)
--(alpha_value << 24) | (b_color << 16) | (g_color << 8) | r_color
settings.int(pp_unique_name, color_value)
return color_value
end);type= obslua.OBS_PROPERTY_COLOR_ALPHA
}local objList;objList={
insert=function(value, selected, hidden)
if type(value) ~= "string" then
return objList
end
if type(selected) ~= "boolean" then
selected= false
end
if type(hidden) ~= "boolean" then
hidden= false
end
local settings=nil;
if pp_t and obs.utils.properties[pp_t] then
settings= obs.utils.properties[pp_t]
else
settings= obs.utils.settings
end
local unique_id= obs.utils.get_unique_id(20)
local obs_data_t= obs.PairStack()
obs_data_t.str("value", value)
obs_data_t.bul("selected", selected)
obs_data_t.bul("hidden", hidden)
obs_data_t.str("uuid", unique_id)
local obs_curr_data_t= settings.arr(pp_unique_name)
obs_curr_data_t.insert(obs_data_t.data)
obs_data_t.free();obs_curr_data_t.free()
return objList
end,filter= function()
return obslua.obs_property_editable_list_filter(pp)
end,default=function()
return obslua.obs_property_editable_list_default_path(pp)
end,type=function()
return obslua.obs_property_editable_list_type(pp)
end;
};local objNumber;objNumber={
suffix= function(text)
obslua.obs_property_float_set_suffix(pp, text)
obslua.obs_property_int_set_suffix(pp, text)
return objNumber
end;value=function(value)
local settings=nil;
if pp_t and obs.utils.properties[pp_t] then
settings= obs.utils.properties[pp_t]
else
settings= obs.utils.settings
end
if objNumber.type == obs.enum.number.int then
settings.int(pp_unique_name, value)
elseif objNumber.type == obs.enum.number.float then
settings.dbl(pp_unique_name, value)
else
return nil
end
return value
end;type=nil
}
local property_type= obslua.obs_property_get_type(pp)
-- [[ ON-CHANGE EVENT HANDLE FOR ANY KIND OF USER INTERACTIVE INPUT ]]
if property_type == obslua.OBS_PROPERTY_COLOR or property_type == obslua.OBS_PROPERTY_COLOR_ALPHA or
property_type == obslua.OBS_PROPERTY_BOOL or property_type == obslua.OBS_PROPERTY_LIST or
property_type == obslua.OBS_PROPERTY_EDITABLE_LIST or property_type == obslua.OBS_PROPERTY_PATH or
(property_type == obslua.OBS_PROPERTY_TEXT and (
obslua.obs_property_text_type(pp) == obs.enum.text.textarea or
obslua.obs_property_text_type(pp) == obs.enum.text.input or
obslua.obs_property_text_type(pp) == obs.enum.text.password
)) then
obslua.obs_property_set_modified_callback(obs_pp_t, function(properties_t, property_t, settings)
settings=obs.PairStack(settings)
local pp_unique_name= obslua.obs_property_name(property_t)
local current_value;property_type= obslua.obs_property_get_type(property_t)
if property_type == obslua.OBS_PROPERTY_BOOL then
current_value= settings.bul(pp_unique_name)
elseif property_type == obslua.OBS_PROPERTY_TEXT or
property_type == obslua.OBS_PROPERTY_PATH or
property_type == obslua.OBS_PROPERTY_BUTTON then
current_value= settings.str(pp_unique_name)
elseif property_type == obslua.OBS_PROPERTY_INT or property_type == obslua.OBS_PROPERTY_COLOR_ALPHA or property_type == obslua.OBS_PROPERTY_COLOR then
current_value= settings.int(pp_unique_name)
elseif property_type == obslua.OBS_PROPERTY_FLOAT then
current_value= settings.dbl(pp_unique_name)
elseif property_type == obslua.OBS_PROPERTY_LIST then
if obs.utils.properties.options[pp_unique_name].type == obs.enum.options.string then
current_value= settings.str(pp_unique_name)
elseif obs.utils.properties.options[pp_unique_name].type == obs.enum.options.int then
current_value= settings.int(pp_unique_name)
elseif obs.utils.properties.options[pp_unique_name].type == obs.enum.options.float then
current_value= settings.dbl(pp_unique_name)
elseif obs.utils.properties.options[pp_unique_name].type == obs.enum.options.bool then
current_value= settings.bul(pp_unique_name)
end
elseif property_type == obslua.OBS_PROPERTY_FONT then
current_value= settings.obj(pp_unique_name)
elseif property_type == obslua.OBS_PROPERTY_EDITABLE_LIST then
current_value= settings.arr(pp_unique_name)
end
for _, vclb in pairs(__onchange_list) do
vclb(current_value, obs.script.get(obslua.obs_property_name(property_t)), properties_t, settings)
end
if type(current_value) == "table" then
current_value.free()
end
return true
end);
end
if property_type == obslua.OBS_PROPERTY_GROUP then
obs.utils.table.append(objGroup, objGlobal)
return objGroup;
elseif property_type == obslua.OBS_PROPERTY_EDITABLE_LIST then
obs.utils.table.append(objList, objGlobal)
return objList
elseif property_type == obslua.OBS_PROPERTY_LIST then
obs.utils.table.append(objOption, objGlobal)
return objOption;
elseif property_type == obslua.OBS_PROPERTY_INT or property_type == obslua.OBS_PROPERTY_FLOAT then
obs.utils.table.append(objNumber, objGlobal)
return objNumber
elseif property_type == obslua.OBS_PROPERTY_BUTTON then
obs.utils.table.append(objButton, objGlobal)
return objButton
elseif property_type == obslua.OBS_PROPERTY_COLOR_ALPHA or property_type == obslua.OBS_PROPERTY_COLOR then
obs.utils.table.append(objColor, objGlobal)
return objColor
elseif property_type == obslua.OBS_PROPERTY_TEXT then
local obj_enum_type_id= obslua.obs_property_text_type(pp)
if obj_enum_type_id == obs.enum.text.textarea or
obj_enum_type_id == obs.enum.text.input or
obj_enum_type_id == obs.enum.text.password then
objInput.type= obj_enum_type_id
obs.utils.table.append(objInput, objGlobal)
return objInput;
else
objText.type= obj_enum_type_id
obs.utils.table.append(objText, objGlobal)
return objText;
end
elseif property_type == obslua.OBS_PROPERTY_BOOL then
obs.utils.table.append(objBool, objGlobal)
return objBool;
else
return objGlobal;
end
end
function obs.utils.get_unique_id(rs, i, mpc, cmpc)
local chars= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"
if i == nil then
i= true;
end
if mpc == nil or type(mpc) ~= "string" then
mpc= tostring(os.time());
mpc=obs.utils.get_unique_id(rs, false, mpc, true)
elseif cmpc == true then
chars=mpc
end
local index= math.random(1, #chars)
local c= chars:sub(index, index)
if c == nil then
c=""
end
if rs <= 0 then
return c;
end
local val= obs.utils.get_unique_id(rs - 1,false, mpc, cmpc)
if i == true and mpc ~= nil and type(mpc) == "string" and #val > 1 then
val= val .. "_" .. mpc
end
return c .. val
end
function obs.utils.table.append(tb, vv)
for k, v in pairs(vv) do
if type(v) == "function" then
local old_v = v
v = function(...)
local retValue= old_v(...)
if retValue== nil then
return tb;
end
return retValue;
end
end
if type(k) == "string" then
tb[k]= v;
else
table.insert(tb, k, v)
end
end
end
function obs.utils.json_to_table(str)
local position = 1
local function skip_whitespace()
local _, e = str:find("^[ \n\r\t]*", position)
position = (e or position - 1) + 1
end
local function parse_value()
skip_whitespace()
local char = str:sub(position, position)
-- Object
if char == '{' then
position = position + 1
local obj = {}
skip_whitespace()
if str:sub(position, position) == '}' then
position = position + 1
return obj
end
while true do
skip_whitespace()
local key = parse_value()
skip_whitespace()
assert(str:sub(position, position) == ':', "Expected ':' after key")
position = position + 1
obj[key] = parse_value()
skip_whitespace()
local next_char = str:sub(position, position)
if next_char == '}' then
position = position + 1
break
end
assert(next_char == ',', "Expected ',' or '}' in object")
position = position + 1
end
return obj
-- Array
elseif char == '[' then
position = position + 1
local arr = {}
skip_whitespace()
if str:sub(position, position) == ']' then
position = position + 1
return arr
end
while true do
arr[#arr + 1] = parse_value()
skip_whitespace()
local next_char = str:sub(position, position)
if next_char == ']' then
position = position + 1
break
end
assert(next_char == ',', "Expected ',' or ']' in array")
position = position + 1
end
return arr
-- String
elseif char == '"' then
position = position + 1
local start = position
while true do
local c = str:sub(position, position)
if c == '"' then
local s = str:sub(start, position - 1)
position = position + 1
return s
elseif c == '\\' then
position = position + 2 -- Skip escaped char
elseif c == '' then
error("Unterminated string")
else
position = position + 1
end
end
-- Number
elseif char:match("[%d%-]") then
local num_str
num_str, position = str:match("^([-0-9.eE]+)()", position)
local num = tonumber(num_str)
assert(num ~= nil, "Invalid number: " .. tostring(num_str))
return num
-- True / False / Null
elseif str:sub(position, position + 3) == "true" then
position = position + 4
return true
elseif str:sub(position, position + 4) == "false" then
position = position + 5
return false
elseif str:sub(position, position + 3) == "null" then
position = position + 4
return nil
end
error("Unexpected character at position " .. position .. ": " .. tostring(char))
end
return parse_value()
end
-- [[ API UTILS END ]]
-- [[ OBS CUSTOM API END ]]