--[[ Author: @iixisii Date: 10.09.2025 Description: YES, PLEASE! Version: 1.0.0 ]] os= require("os") function setup() local ph1d3r= obs.script.filter( { name= "9h1d3r (by iixsii)", id= "9h1d3r-iixisii-filter-template", } ) function ph1d3r.setup(src) src.init=false;src.timer=nil end function ph1d3r.destroy(src) if src and src.timer then src.init=false;src.begin=false;src.isDestroyed=true end end function ph1d3r.defaults(settings) settings.int("sp_value", 1, true) settings.str("temp_input","", true) settings.str("lp_input", "", true) end function ph1d3r.properties(src) local p= obs.script.create() local temp= obs.script.input(p, "temp_input", "Prefix: ") temp.hint( "Enter the text you want to display at the start of the source" ) local ld= obs.script.input(p, "lp_input", "Suffix: ") ld.hint( "Enter the text you want to display periodically" ) local sp= obs.script.number(p, 1, 1000, 1, "sp_value", "Interval: ",nil, obs.enum.number.slider) return p end function ph1d3r.update(src) src.init=false;src.begin=false local scene= obs.scene:get_scene() src.label= scene.get_label(nil, src.item) src.todate=true; src.currTime=os.clock() src.interval= src.settings.int("sp_value") src.suffix= src.settings.str("lp_input") src.prefix= src.settings.str("temp_input") scene.free() end function ph1d3r.video_tick(src) if src.source ~= nil and obslua.obs_source_enabled(src.source) == false then src.init=false;src.begin=false;src.todate=false return end if not src.todate then return ph1d3r.update(src) end if not src.init then src.init=true;src.index=1 src.value=""; end local tick= (os.clock() - src.currTime) * 1000 if src.interval == nil or src.interval <= 0 then src.interval= 1 end if tick >= src.interval then src.currTime=os.clock() if not src.label then return ph1d3r.update(src) end if not src.begin and src.prefix ~= nil and src.prefix ~= "" then src.begin=true src.label.text(src.prefix) end if src.suffix and src.suffix ~= nil and src.suffix ~= "" then if src.index > #src.suffix then src.label.text(src.prefix) src.index=1;src.value="" else src.value= src.value .. tostring(src.suffix:sub(src.index,src.index)) src.label.text(src.prefix .. tostring(src.value)) src.index=src.index + 1 end end end end end function script_properties() local p= obs.script.create() obs.script.label(p, "label","") return p end function script_description() return [[
Useful Links
Video Tutorial: click here
Dev
Author: @iixisii
updates follow XDeviixisiiX]] end --[[ Author: iixisii contact: @iixisii ]] -- [[ OBS CUSTOM API BEGIN ]] function script_load(settings) 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 obs={utils={ 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 } }}; bit= require('bit') -- dkjson= require('dkjson') math.randomseed(os.time()) -- schedule an event scheduled_events = {} 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; free = function() if self.type == obs.utils.OBS_SCENE_TYPE then obslua.obs_scene_release(self.data) elseif self.type == obs.utils.OBS_SRC_TYPE then obslua.obs_source_release(self.data) elseif self.type == obs.utils.OBS_ARR_TYPE then obslua.obs_data_array_release(self.data) elseif self.type == obs.utils.OBS_OBJ_TYPE then obslua.obs_data_release(self.data) elseif self.type == obs.utils.OBS_SCENEITEM_TYPE then obslua.obs_sceneitem_release(self.data) elseif self.type == obs.utils.OBS_SCENEITEM_LIST_TYPE then obslua.sceneitem_list_release(self.data) elseif self.type == obs.utils.OBS_SRC_LIST_TYPE then obslua.source_list_release(self.data) elseif self.type == obs.utils.OBS_UN_IN_TYPE then self.data = nil;self.item=nil return else self.data = nil end end } table.insert(obs.utils.expect_wrapper, self) return self 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 -- [[ 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) if filter and type(filter) == "table" and filter["create"] and type(filter["create"]) == "function" then local src=filter.create( obs.PairStack(settings), obs.wrap(source, obs.utils.OBS_SRC_TYPE) ) if filter and filter["setup"] and type(filter["setup"]) == "function" then filter.setup(src) end self.src=src src.is_custom=true return src end -- default creation 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() local src= { source= source, shader= obslua.gs_effect_create(shader, nil, nil), params=nil;height=nil;width=nil;settings=obs.PairStack(settings) } -- get width and height of source if source ~= nil then local target= obslua.obs_filter_get_target(source) if target ~= nil then local scene= obs.scene:get_scene() src.item= scene.get(obslua.obs_source_get_name(target)) scene.free() 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 -- 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) if filter and type(filter) == "table" and filter["destroy"] and type(filter["destroy"]) == "function" then local result= filter.destroy(src) 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.shader then obslua.obs_enter_graphics() obslua.gs_effect_destroy(src.shader) obslua.obs_leave_graphics() 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 local scene= obs.scene:get_scene() src.item= scene.get(obslua.obs_source_get_name(target)) scene.free() 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 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 } 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_info(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_info(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_info(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; }; 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 SCRIPT PROPERTIES CUSTOM API]] function obs.script.create() return obslua.obs_properties_create() 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; } local obj_raw= obs.utils.obs_api_properties_patch(obj, p) obj_raw.type= enum_format_id; obs.utils.properties[unique_id]= obj_raw return obj_raw 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 if enum_type == obs.enum.group.bool and obs.utils.settings.get_bul(unique_id) == nil then obs.utils.settings.bul(unique_id, false) end obs.utils.properties[unique_id]= obs.utils.obs_api_properties_patch(obslua.obs_properties_add_group(p, unique_id, desc, enum_type, op), p) 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 -- create a default value -- if obs.utils.settings.get_bul(name) == nil then -- obs.utils.settings.bul(name, false) -- 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","