-- [[ Hide2Show 5.4.0 BLOCK ]] -- [[ variable data ]] os = require("os") bit = require("bit") APP = { SHOW = 1; HIDE = 2; QK = 50; VF = 1; NR = 100; STEPS = 10;ACT = { ACT1 = "act1";ACT2 = "act2"; ACT3 = "act3" } } on_exit=false local __settings__ = nil local animation_list = { {id = "lr";name = "From left to right";opt_out = { "act1","act2","act3" },ignore_opt = { "anime_rt_list","anime_rt_cst" }},{id = "rl";name = "From right to left";opt_out = { "act1","act2","act3" },ignore_opt = { "anime_rt_list","anime_rt_cst" }}, {id = "tb";name = "From top to bottom";opt_out = { "act1","act2","act3" },ignore_opt = { "anime_rt_list","anime_rt_cst" }},{id = "bt";name = "From bottom to top";opt_out = { "act1","act2","act3" }, ignore_opt = { "anime_rt_list","anime_rt_cst" }},{id = "fl";name = "Flicker";ignore_opt = { "anime_velcity_value","anime_rt_list","anime_rt_cst" }},{id = "bnc";name = "Hover";ignore_opt = { "anime_velcity_value","anime_rt_list","anime_rt_cst" }},{id = "pls";name = "Pulsing";ignore_opt = { "anime_velcity_value","anime_rt_list","anime_rt_cst" }} } local rotation_list = { {id = "rt_90"; name = "90°"}, {id = "rt_180"; name = "180°"}, {id ="rt_360"; name = "360°"}, {id= "cst", name= "Custom"} } -- [[ Functionality ]] function setup(settings) local Hide2Show= obs.script.filter({ name="Hide2Show", id="hns-filter-iixisii-vs5.4.0", }); function Hide2Show.setup(src) src.currTime=os.clock() src.action=APP.HIDE src.init=false src.timer=nil src.anime_target="def" src.anime_rt=nil src.anime_rt_value=nil end function Hide2Show.defaults(settings) settings.bul("action_hide_group",true, true) settings.str("hide_interval_type","mis", true) settings.int("hide_time",1, true) settings.bul("hide_time_random",false, true) -- settings.bul("action_show_group",true, true) settings.str("show_interval_type","mis", true) settings.int("show_time",1, true) settings.bul("show_time_random",false, true) -- settings.int("defAction",-1, true) settings.int("anime_velcity_value", 10, true) end function Hide2Show.properties(src) local ui= obs.script.create() local ui_action= obs.script.create() -- [[ Hide ]] local ui_hide_action= obs.script.create() obs.script.group(ui, "action_hide_group","Hide Time", ui_hide_action) local hide_interval_type= obs.script.options(ui_hide_action, "hide_interval_type","") hide_interval_type.onchange(function(value) obs.script.get("hide_time").suffix(" " .. tostring(value)) return true end) hide_interval_type.add.str( "milliseconds", "mis" ).add.str("seconds", "sc").add.str( "minutes", "ms" ).add.str("hours", "hr") obs.script.number(ui_hide_action, 1, 999999999,1, "hide_time","") obs.script.bool(ui_hide_action, "hide_time_random","Randomness") -- [[ Show ]] local ui_show_action= obs.script.create() obs.script.group(ui, "action_show_group","Show Time", ui_show_action) local show_interval_type= obs.script.options(ui_show_action, "show_interval_type","") show_interval_type.onchange(function(value) obs.script.get("show_time").suffix(" " .. tostring(value)) return true end) show_interval_type.add.str( "milliseconds", "mis" ).add.str("seconds", "sc").add.str( "minutes", "ms" ).add.str("hours", "hr") obs.script.number(ui_show_action, 1, 999999999,1, "show_time","") obs.script.bool(ui_show_action, "show_time_random","Randomness") -- [[ Animation ]] local ui_anime= obs.script.create() obs.script.group(ui, "animation_group","Animation", ui_anime) -- [[ Animation List ]] -- [[ Animation Style ]] local anime_style_list= obs.script.options(ui_anime, "anime_style_list","Animation: ") anime_style_list.onchange(function(value, p, pp, settings) if value ~= "def" then obs.script.get("anime_speed_list").show() obs.script.get("anime_velcity_value").show() else obs.script.get("anime_speed_list").hide() obs.script.get("anime_velcity_value").hide() end return true end) anime_style_list.add.str( "View animations ( optional )","def" ) for _, iter in pairs(animation_list) do if iter and iter.opt_out ~= nil then local isvalid = true for _, itemValue in pairs(iter.opt_out) do if itemValue == value then isvalid = false break end end if isvalid then anime_style_list.add.str(iter.name, iter.id) if iter.id == current_anime then is_listed = true end end else anime_style_list.add.str(iter.name, iter.id) if iter.id == current_anime then is_listed = true end end end -- [[ Animation Rotation ]] -- [[ Custom Rotation ]] -- [[ Animation Speed ]] local anime_speed_list= obs.script.options(ui_anime, "anime_speed_list","Interval: ") anime_speed_list.add.str( "Normal ( system default )", "nr" ).add.str("Quick","qk").add.str( "Very fast","vf" ).add.str("Slow", "sl").hide() -- [[ Animation Velocity ]] local anime_velcity_value= obs.script.number( ui_anime, 1, 1000,1, "anime_velcity_value","Velocity: ", obs.enum.number.int,obs.enum.number.slider ).hide() return ui end function Hide2Show.update(src) if src.timer ~= nil then obslua.timer_remove(src.timer) src.init=false;src.is_loading=false src.action= src.defAction src.timer=nil reset_to_default(src) end -- Call a default setup function for the source -- src.currTime= os.clock() src.hide_time = src.settings.get_int("hide_time") src.hide_time_random = src.settings.get_bul("hide_time_random") local hide_time_type= src.settings.str("hide_interval_type") if hide_time_type == "sc" then src.hide_time = src.hide_time * 1000 elseif hide_time_type == "ms" then src.hide_time= src.hide_time * 60000 elseif hide_time_type == "hr" then src.hide_time= src.hide_time * 3600000 end src.hide_time_max= src.hide_time if src.hide_time_random then src.hide_time= math.random(1, src.hide_time_max) end -- src.show_time = src.settings.get_int("show_time") src.show_time_random = src.settings.get_bul("show_time_random") local show_time_type= src.settings.str("show_interval_type") if show_time_type == "sc" then src.show_time = src.show_time * 1000 elseif show_time_type == "ms" then src.show_time= src.show_time * 60000 elseif show_time_type == "hr" then src.show_time= src.show_time * 3600000 end src.show_time_max= src.show_time if src.show_time_random then src.show_time= math.random(1, src.show_time_max) end -- src.steps = src.settings.get_int("anime_velcity_value") src.anime_opt = src.settings.get_str("anime_style_list") src.anime_tim = src.settings.get_str("anime_speed_list") src.anime_target = src.settings.get_str("anime_list") local defPos= src.settings.obj("defPos") local defScale= src.settings.obj("defScale") if defPos and defPos.data ~= nil then src.defPos= obslua.vec2() src.defPos.x= defPos.int("x") src.defPos.y= defPos.int("y") if src.item and src.item.data ~= nil then obslua.obs_sceneitem_set_pos( src.item.data, src.defPos ) end end if defScale and defScale.data then src.defScale= obslua.vec2() src.defScale.x= defScale.int("x") src.defScale.y= defScale.int("y") if src.item and src.item.data then obslua.obs_sceneitem_set_scale( src.item.data,src.defScale ) end end defPos.free() defScale.free() end function Hide2Show.video_tick(src) if src.source ~= nil and obslua.obs_source_enabled(src.source) == false then if not src.enabled_checked then src.enabled_checked= true if src.timer then obslua.timer_remove(src.timer) src.timer= nil end reset_to_default(src) if src.item and src.item.data ~= nil then src.item.free() src.item=nil end src.settings.int("defAction",-1) src.is_loading=false src.defPos=nil;src.defScale=nil src.defRot=nil;src.init=false end return false else src.enabled_checked=false end if not src.init then src.init= true local source= obslua.obs_filter_get_target(src.source) if source ~= nil then local source_name= obslua.obs_source_get_name(source) local scene= obs.scene:get_scene() src.item= scene.get(source_name) if src.item and src.item.data ~= nil then src.currTime= os.clock() if obslua.obs_sceneitem_visible(src.item.data) then src.action= APP.HIDE else src.action = APP.SHOW end src.defAction= src.settings.int("defAction") if src.defAction ~= APP.HIDE and src.defAction ~= APP.SHOW then if src.action == APP.HIDE then src.defAction= APP.SHOW else src.defAction= APP.HIDE end src.settings.int("defAction", src.defAction) end if src.defPos == nil then src.defPos= obslua.vec2() obslua.obs_sceneitem_get_pos(src.item.data, src.defPos) local pos= obs.utils.PairStack() pos.int("x", src.defPos.x).int( "y", src.defPos.y ) src.settings.obj("defPos",pos.data) pos.free() end if src.defRot == nil then src.defRot= obslua.obs_sceneitem_get_rot( src.item.data ) src.settings.int("defRot", src.defRot) end if src.defScale == nil then src.defScale= obslua.vec2() obslua.obs_sceneitem_get_scale( src.item.data,src.defScale ) local scale= obs.utils.PairStack() scale.int("x", src.defScale.x) scale.int("y", src.defScale.y) src.settings.obj("defScale", scale.data) scale.free() end end src.base_width= scene.get_width() src.base_height= scene.get_height() scene.free() end end if not src.is_loading then if src.currTime == nil then return end local currTick= (os.clock() - src.currTime) * 1000 -- [[ HANDLE HIDE ]] if currTick ~= nil and src.hide_time ~= nil and src.action == APP.HIDE and currTick >= src.hide_time then local function init_hide() obslua.obs_sceneitem_set_visible(src.item.data, false) src.currTime= os.clock() src.action= APP.SHOW if src.hide_time_random == true then src.hide_time= math.random(1, src.hide_time_max) else src.hide_time= src.hide_time_max end src.is_loading=false end if src.anime_opt ~= nil and src.anime_opt ~= "" and src.anime_opt ~= "def" then src.is_loading=true local pos = obslua.vec2() obslua.obs_sceneitem_get_pos(src.item.data, pos) local anime_config = nil local speed = APP.NR if src.anime_tim == "qk" then speed = APP.QK elseif src.anime_tim == "vf" then speed = APP.VF end if true then if src.anime_opt == "lr" then -- left to right animate(src, speed).left_to_right( src.base_width + src.width ).after(init_hide) elseif src.anime_opt == "rl" then -- right to left local p=obslua.vec2() p.x= src.base_width - src.width p.y= src.defPos.y obslua.obs_sceneitem_set_pos(src.item.data, p) animate(src,speed).right_to_left(-(src.width * 2)).after(init_hide) elseif src.anime_opt == "tb" then -- top to bottom local p= obslua.vec2() p.y=0;p.x=src.defPos.x obslua.obs_sceneitem_set_pos(src.item.data, p) animate(src, speed).top_to_bottom(src.base_height + src.height).after(init_hide) elseif src.anime_opt == "bt" then -- bottom to top local p= obslua.vec2() p.y=src.base_height-src.height;p.x=src.defPos.x obslua.obs_sceneitem_set_pos(src.item.data, p) animate(src, speed).bottom_to_top(-(src.height)).after(init_hide) elseif src.anime_opt == "fl" then animate(src, speed).flicker().after(init_hide) elseif src.anime_opt == "bnc" then -- hover animate(src, speed).bounce().after(init_hide) elseif src.anime_opt == "pls" then -- pulsing animate(src, speed).pulsing().after(init_hide) else init_hide() end else init_hide() end else init_hide() end -- [[ HANDLE SHOW ]] elseif currTick ~= nil and src.show_time ~= nil and src.action == APP.SHOW and currTick >= src.show_time then local function init_show() obslua.obs_sceneitem_set_visible(src.item.data, true) src.currTime = os.clock() src.action = APP.HIDE if src.show_time_random == true then src.show_time = math.random(1, src.show_time_max) end src.is_loading = false end if src.anime_opt ~= nil and src.anime_opt ~= "def" then src.is_loading = true local pos = obslua.vec2() obslua.obs_sceneitem_get_pos(src.item.data, pos) local speed = APP.NR if src.anime_tim == "qk" then speed = APP.QK elseif src.anime_tim == "vf" then speed = APP.VF end if true then obslua.obs_sceneitem_set_pos(src.item.data, src.defPos) obslua.obs_sceneitem_set_scale(src.item.data, src.defScale) obslua.obs_sceneitem_set_visible(src.item.data, true) if src.anime_opt == "lr" then -- left to right local p= obslua.vec2() p.y=src.defPos.y;p.x=-(src.width * 2) obslua.obs_sceneitem_set_pos(src.item.data, p) animate(src, speed).left_to_right(0).after(init_show) elseif src.anime_opt == "rl" then -- right to left local p=obslua.vec2() p.x= src.base_width + src.width p.y= src.defPos.y obslua.obs_sceneitem_set_pos(src.item.data, p) animate(src,speed).right_to_left(src.base_width - src.width).after(init_show) elseif src.anime_opt == "tb" then -- top to bottom local p= obslua.vec2() p.y=-(src.height*2);p.x=src.defPos.x obslua.obs_sceneitem_set_pos(src.item.data, p) animate(src, speed).top_to_bottom(0).after(init_show) elseif src.anime_opt == "bt" then -- bottom to top local p= obslua.vec2() p.y=src.base_height+src.height;p.x=src.defPos.x obslua.obs_sceneitem_set_pos(src.item.data, p) animate(src, speed).bottom_to_top(src.base_height-src.height).after(init_show) elseif src.anime_opt == "fl" then animate(src, speed).flicker().after(init_show) elseif src.anime_opt == "bnc" then -- hover animate(src, speed).bounce().after(init_show) elseif src.anime_opt == "pls" then -- pulsing animate(src, speed).pulsing().after(init_show) else init_show() end else end else init_show() end end end end function Hide2Show.destroy(src) obslua.timer_remove(src.timer) reset_to_default(src) if src.item then src.item.free() end end end function reset_to_default(src) if src == nil then return false end if src.item ~=nil and src.item.data ~= nil then if src.defRot ~= nil then obslua.obs_sceneitem_set_rot(src.item.data, src.defRot) end if src.defPos ~= nil then obslua.obs_sceneitem_set_pos(src.item.data, src.defPos) end if src.defAction == APP.HIDE then obslua.obs_sceneitem_set_visible(src.item.data, false) elseif src.defAction == APP.SHOW then obslua.obs_sceneitem_set_visible(src.item.data, true) end if src.defScale ~= nil then obslua.obs_sceneitem_set_scale(src.item.data, src.defScale) end return true end return false end function animate(src, speed) return { growth = function() if src.timer~= nil then -- add the animation to the queue return end local scale = obslua.vec2() scale.x = 0.15 scale.y = 0.15 obslua.obs_sceneitem_set_scale(src.item.data, scale) local __calback_back__ = nil src.timer= function() if src.timer== nil then return end if not src.defScale or (scale.x >= src.defScale.x and scale.y >= src.defScale.y) then obslua.timer_remove(src.timer) src.is_loading = false if type(__calback_back__) == "function" then __calback_back__() end src.timer= nil return else scale.x = scale.x + 0.1--src.steps scale.y = scale.y + 0.1--src.steps if scale.x >= src.defScale.x then scale.x = src.defScale.x end if scale.y >= src.defScale.y then scale.y = src.defScale.y end obslua.obs_sceneitem_set_scale(src.item.data, scale) -- position settings local currScale = obslua.vec2() obslua.obs_sceneitem_get_scale(src.item.data, currScale) local currPos = obslua.vec2() obslua.obs_sceneitem_get_pos(src.item.data, currPos) local scale_down = obslua.vec2() scale_down.x = currScale.x - scale.x scale_down.y = currScale.y - scale.y if src.defPos ~= nil then local newPos = obslua.vec2() local w = obslua.obs_source_get_base_width(obslua.obs_sceneitem_get_source(src.item.data)) local h = obslua.obs_source_get_base_height(obslua.obs_sceneitem_get_source(src.item.data)) newPos.x = currPos.x + (scale_down.x * w / 2) newPos.y = currPos.y + (scale_down.y * h / 2) obslua.obs_sceneitem_set_pos(src.item.data, newPos) end end end obslua.timer_add(src.timer, speed) return { after = function(callback) __calback_back__ = callback end;returnValue = true } end;rotate = function() if src.timer~= nil then -- add the animation to the queue return end -- Function to convert degrees to radians -- local function deg2rad(degrees) -- return degrees * math.pi / 180 -- end -- Function to set the rotation of a source while keeping it in the same position function setRot(rotation) -- Get the current position local pos = obslua.vec2() obslua.obs_sceneitem_get_pos(src.item.data, pos) -- Get the width and height of the source local source = obslua.obs_sceneitem_get_source(src.item.data) local width = obslua.obs_source_get_base_width(source) local height = obslua.obs_source_get_base_height(source) -- Calculate the center of the source local center_x = pos.x + width / 2 local center_y = pos.y + height / 2 -- Set the new rotation obslua.obs_sceneitem_set_rot(src.item.data, rotation) -- Calculate the new position after rotation -- This is a simplified example, you might need to adjust this based on your rotation local new_pos_x = center_x - width / 2 local new_pos_y = center_y - height / 2 -- Set the new position local new_pos = obslua.vec2() new_pos.x = new_pos_x new_pos.y = new_pos_y obslua.obs_sceneitem_set_pos(src.item.data, new_pos) end local __calback_back__ = nil local limit = 0 local current = 0 if src.anime_rt == "rt_90" then limit = 90 elseif src.anime_rt == "rt_180" then limit = 180 elseif src.anime_rt == "rt_360" then limit = 360 elseif src.anime_rt == "cst" then limit = src.anime_rt_value end if limit == nil or type(limit) ~= "number" or limit < 0 then limit = 0 end src.timer= function() if src.timer== nil then return end if current >= limit then obslua.timer_remove(src.timer) if type(__calback_back__) == "function" then __calback_back__() end src.is_loading = false src.timer= nil return end current = current + src.steps setRot(current) end obslua.timer_add(src.timer, speed) return { after = function(callback) __calback_back__ = callback end;returnValue = true } end;pulsing = function() if src.timer~= nil then -- add the animation to the queue return end local scale = obslua.vec2() obslua.obs_sceneitem_get_scale(src.item.data, scale) local __calback_back__ = nil local debounce = 10 local gp = -1 src.timer= function() if src.timer== nil then return end if debounce <= 0 then obslua.timer_remove(src.timer) if type(__calback_back__) == "function" then __calback_back__() end src.is_loading = false src.timer= nil return else if gp == -1 then scale.x = scale.x - 0.1 scale.y = scale.y - 0.1 gp = 1;debounce=debounce-1 else scale.x = scale.x + 0.1 scale.y = scale.y + 0.1 gp = -1;debounce=debounce-1 end local currScale = obslua.vec2() obslua.obs_sceneitem_get_scale(src.item.data, currScale) local currPos = obslua.vec2() obslua.obs_sceneitem_get_pos(src.item.data, currPos) local scale_down = obslua.vec2() scale_down.x = currScale.x - scale.x scale_down.y = currScale.y - scale.y obslua.obs_sceneitem_set_scale(src.item.data, scale); if src.defPos ~= nil then local newPos = obslua.vec2() local w = obslua.obs_source_get_base_width(obslua.obs_sceneitem_get_source(src.item.data)) local h = obslua.obs_source_get_base_height(obslua.obs_sceneitem_get_source(src.item.data)) newPos.x = currPos.x + (scale_down.x * w / 2) newPos.y = currPos.y + (scale_down.y * h / 2) obslua.obs_sceneitem_set_pos(src.item.data, newPos) end end end obslua.timer_add(src.timer, speed) return { after = function(callback) __calback_back__ = callback end;returnValue = true } end;bounce = function() if src.timer~= nil then -- add the animation to the queue return end local bounce_limit = 20 local debounce = 3 local __calback_back__ = nil local currPos = obslua.vec2() obslua.obs_sceneitem_get_pos(src.item.data, currPos) local old_pos = obslua.vec2() obslua.obs_sceneitem_get_pos(src.item.data, old_pos) local gp = -1 src.timer= function() if src.timer== nil then return end if debounce <= 0 then obslua.timer_remove(src.timer) if type(__calback_back__) == "function" then __calback_back__() end src.is_loading = false src.timer= nil obslua.obs_sceneitem_set_pos(src.item.data, old_pos) return end if gp == -1 then currPos.y = currPos.y - 1 else currPos.y = currPos.y + 1 end if bounce_limit <= 10 then gp = 1 else gp = -1 end if bounce_limit <= 0 then debounce = debounce - 1 bounce_limit = 20 gp = -1 end obslua.obs_sceneitem_set_pos(src.item.data, currPos) bounce_limit = bounce_limit - 1 end obslua.timer_add(src.timer, speed) return { after = function(callback) __calback_back__ = callback end;returnValue = true } end;flicker = function() if src.timer~= nil then -- add the animation to the queue return end local flicker_limit = 5 local __calback_back__ = nil src.timer= function() if src.timer== nil then return end if flicker_limit <= 0 then obslua.timer_remove(src.timer) if type(__calback_back__) == "function" then __calback_back__() end src.is_loading = false src.timer= nil return end local hidden = obslua.obs_sceneitem_visible(src.item.data) if hidden then hidden = false else hidden = true end obslua.obs_sceneitem_set_visible(src.item.data, hidden) flicker_limit = flicker_limit - 1 end obslua.timer_add(src.timer, speed) return { after = function(callback) __calback_back__ = callback end;returnValue = true } end;resize = function() if src.timer~= nil then -- add the animation to the queue return end local scale = obslua.vec2() obslua.obs_sceneitem_get_scale(src.item.data, scale) local __calback_back__ = nil src.timer= function() if src.timer== nil then return end if scale.x <= 0.25 and scale.y <= 0.25 then obslua.timer_remove(src.timer) if type(__calback_back__) == "function" then __calback_back__() end src.is_loading = false src.timer= nil return else if scale.x > 0.25 then scale.x = scale.x - 0.15 end if scale.y > 0.25 then scale.y = scale.y - 0.15 end local currScale = obslua.vec2() obslua.obs_sceneitem_get_scale(src.item.data, currScale) local currPos = obslua.vec2() obslua.obs_sceneitem_get_pos(src.item.data, currPos) local scale_down = obslua.vec2() scale_down.x = currScale.x - scale.x scale_down.y = currScale.y - scale.y obslua.obs_sceneitem_set_scale(src.item.data, scale); if src.defPos ~= nil then local newPos = obslua.vec2() local w = obslua.obs_source_get_base_width(obslua.obs_sceneitem_get_source(src.item.data)) local h = obslua.obs_source_get_base_height(obslua.obs_sceneitem_get_source(src.item.data)) newPos.x = currPos.x + (scale_down.x * w / 2) newPos.y = currPos.y + (scale_down.y * h / 2) obslua.obs_sceneitem_set_pos(src.item.data, newPos) end end end obslua.timer_add(src.timer, speed) return { after = function(callback) __calback_back__ = callback end;returnValue = true } end;left_to_right = function(limit) if src.timer~= nil then -- add the animation to the queue return end local pos = obslua.vec2() obslua.obs_sceneitem_get_pos(src.item.data, pos) local __calback_back__ = nil src.timer= function() if src.timer== nil then return end if pos.x >= limit then obslua.timer_remove(src.timer) if type(__calback_back__) == "function" then __calback_back__() end src.is_loading = false src.timer= nil return else pos.x = pos.x + src.steps if pos.x > limit then pos.x = limit end obslua.obs_sceneitem_set_pos(src.item.data, pos) end end obslua.timer_add(src.timer, speed) return { after = function(callback) __calback_back__ = callback end;returnValue = true } end; right_to_left = function(limit) if src.timer~= nil then -- add the animation to the queue return end local pos = obslua.vec2() obslua.obs_sceneitem_get_pos(src.item.data, pos) local __calback_back__ = nil src.timer= function() if src.timer== nil then return end if pos.x <= limit then obslua.timer_remove(src.timer) src.is_loading = false if type(__calback_back__) == "function" then __calback_back__() end src.timer= nil return else pos.x = pos.x - src.steps if pos.x < limit then pos.x = limit end obslua.obs_sceneitem_set_pos(src.item.data, pos) end end obslua.timer_add(src.timer, speed) return { after = function(callback) __calback_back__ = callback end;returnValue = true } end; top_to_bottom = function(limit) if src.timer~= nil then -- add the animation to the queue return end local __calback_back__ = nil local pos = obslua.vec2() obslua.obs_sceneitem_get_pos(src.item.data, pos) src.timer= function() if src.timer== nil then return end if pos.y >= limit then obslua.timer_remove(src.timer) src.is_loading = false if type(__calback_back__) == "function" then __calback_back__() end src.timer= nil return else pos.y = pos.y + src.steps if pos.y > limit then pos.y = limit end obslua.obs_sceneitem_set_pos(src.item.data, pos) end end obslua.timer_add(src.timer, speed) return { after = function(callback) __calback_back__ = callback return true end;returnValue = true } end; bottom_to_top = function(limit) if src.timer~= nil then -- add the animation to the queue return end local pos = obslua.vec2() local __calback_back__ = nil obslua.obs_sceneitem_get_pos(src.item.data, pos) src.timer= function() if src.timer== nil then return end if pos.y <= limit then obslua.timer_remove(src.timer) src.is_loading = false if type(__calback_back__) == "function" then __calback_back__() end src.timer= nil return else pos.y = pos.y - src.steps if pos.y < limit then pos.y = limit end obslua.obs_sceneitem_set_pos(src.item.data, pos) end end obslua.timer_add(src.timer, speed) return { after= function(callback) __calback_back__ = callback return true end;returnValue = true } end; to_left = function(limit) if src.timer~= nil then -- add the animation to the queue return end local pos = obslua.vec2() local __calback_back__ = nil obslua.obs_sceneitem_get_pos(src.item.data, pos) src.timer= function() if src.timer== nil then return end if pos.x <= limit then obslua.timer_remove(src.timer) src.is_loading = false if type(__calback_back__) == "function" then __calback_back__() end src.timer= nil return else pos.x = pos.x - src.steps if pos.x < limit then pos.x = limit end obslua.obs_sceneitem_set_pos(src.item.data, pos) end end obslua.timer_add(src.timer, speed) return { after = function(callback) __calback_back__ = callback end;returnValue = true } end; to_right = function(limit) if src.timer~= nil then -- add the animation to the queue return end local pos = obslua.vec2() local __calback_back__ = nil obslua.obs_sceneitem_get_pos(src.item.data, pos) src.timer= function() if src.timer== nil then return end if pos.x >= limit then obslua.timer_remove(src.timer) src.is_loading = false if type(__calback_back__) == "function" then __calback_back__() end src.timer= nil return else pos.x = pos.x + src.steps if pos.x > limit then pos.x = limit end obslua.obs_sceneitem_set_pos(src.item.data, pos) end end obslua.timer_add(src.timer, speed) return { after = function(callback) __calback_back__ = callback end;returnValue = true } end; to_top = function(limit) if src.timer~= nil then -- add the animation to the queue return end local pos = obslua.vec2() local __calback_back__ = nil obslua.obs_sceneitem_get_pos(src.item.data, pos) src.timer= function() if src.timer== nil then return end if pos.y <= limit then obslua.timer_remove(src.timer) src.is_loading = false if type(__calback_back__) == "function" then __calback_back__() end src.timer= nil return else pos.y = pos.y - src.steps if pos.y < limit then pos.y = limit end obslua.obs_sceneitem_set_pos(src.item.data, pos) end end obslua.timer_add(src.timer, speed) return { after = function(callback) __calback_back__ = callback end;returnValue = true } end; to_bottom = function(limit) if src.timer~= nil then -- add the animation to the queue return end local pos = obslua.vec2() local __calback_back__ = nil obslua.obs_sceneitem_get_pos(src.item.data, pos) src.timer= function() if src.timer== nil then return end if pos.y >= limit then obslua.timer_remove(src.timer) src.is_loading = false if type(__calback_back__) == "function" then __calback_back__() end src.timer= nil return else pos.y = pos.y + src.steps if pos.y > limit then pos.y = limit end obslua.obs_sceneitem_set_pos(src.item.data, pos) end end obslua.timer_add(src.timer, speed) return { after = function(callback) __calback_back__ = callback end;returnValue = true } end } end function IndexPage() return [[
Select a source and add a filter called 'Hide2Show'.
]] end function script_properties() local p = obs.script.create() obs.script.label(p,"label", IndexPage()) return p; end -- [[ END OF Hide2Show BLOCK ]] -- [[ OBS LUA SCRIPT API BLOCK ]] --[[ Author: iixisii contact: @iixisii ]] function script_load(settings) settings= obs.utils.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={};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.utils.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(error_wrapper, self) return self end error_freed = 0 error_wrapper = {};function obs.utils.error_wrapper_handler (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) if not success then error_freed = 0 for _, iter in pairs(error_wrapper) do if iter and type(iter.free) == "function" then local s, r = pcall(function() iter.free() end) if s then error_freed = error_freed + 1 end end end obslua.script_log(obslua.LOG_ERROR, "[ErrorWrapper ERROR] => " .. tostring(result)) end return data end end -- array handle function obs.utils.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.utils.PairStack(obslua.obs_data_array_item(self.data, index), nil, true) end;next = obs.utils.error_wrapper_handler(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.utils.PairStack( obslua.obs_data_array_item(self.data, i), nil, false )) end end) -- local temp = self.index;self.index = self.index + 1 -- return obs.utils.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.utils.error_wrapper_handler(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.utils.error_wrapper_handler(function() if self.data == nil then return 0 end return obslua.obs_data_array_count(self.data); end); rm= obs.utils.error_wrapper_handler(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(error_wrapper, self) return self end -- pair stack used to manage memory stuff :) function obs.utils.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.utils.error_wrapper_handler(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.utils.error_wrapper_handler(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.utils.error_wrapper_handler(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.utils.error_wrapper_handler(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.utils.error_wrapper_handler(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.utils.error_wrapper_handler(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.utils.error_wrapper_handler(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.utils.error_wrapper_handler(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.utils.error_wrapper_handler(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.utils.error_wrapper_handler(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.utils.PairStack( obslua.obs_data_get_obj(self.data, name),nil, false ) else return obs.utils.PairStack( obslua.obs_data_get_default_obj(self.data, name),nil, false ) end end);get_arr = obs.utils.error_wrapper_handler(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.utils.ArrayStack( obslua.obs_data_get_array(self.data, name),nil, false ) else return obs.utils.ArrayStack(obslua.obs_data_get_default_array(self.data, name),nil, false) end end);get_bul = obs.utils.error_wrapper_handler(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.utils.error_wrapper_handler(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(error_wrapper, self) return self end -- [[ OBS FILTER CUSTOM API]] function obs.script.filter(filter) local 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.utils.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.utils.PairStack(settings), obs.utils.wrap(source, obs.utils.OBS_SRC_TYPE) ) if filter and filter["setup"] and type(filter["setuup"]) == "function" then filter.setup(src) end 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.utils.PairStack(settings) } -- 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 -- 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["setuup"]) == "function" then filter.setup(src) end return src end, destroy= function(src) if filter and type(filter) == "table" and filter["destroy"] and type(filter["destroy"]) == "function" then return filter.destroy(src) end -- default destruction 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 return filter.video_render(src) end -- default render if not src or not src.source or not src.shader or not src.params then return 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.utils.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.utils.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.utils.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.utils.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.utils.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.utils.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.utils.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.utils.PairStack( obslua.obs_source_get_settings(source.get_source()), nil,true ) if not src or not src.data then src= obs.utils.PairStack() end local font= src.get_obj("font") if not font or not font.data then font= obs.utils.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.utils.PairStack( obslua.obs_source_get_settings(source.get_source()), nil,true ) if not src or not src.data then src= obs.utils.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.utils.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.utils.wrap(obslua.obs_scene_get_group(scene, name), obs.utils.OBS_SCENEITEM_TYPE) elseif gp ~= nil then obj= obs.utils.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.utils.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.utils.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","
", 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 -- [[ 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 };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.utils.error_wrapper_handler(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 obs.utils.settings.str(textarea_id, txt) end return objText end);type=-1 };objInput={ value=obs.utils.error_wrapper_handler(function(txt) if txt ~= nil and type(txt) == "string" then obs.utils.settings.str(pp_unique_name, txt) 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; ret=function() return objOption end } return nn_obj; end;current=function() local current_selected_option; 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= obs.utils.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= obs.utils.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= obs.utils.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= obs.utils.settings.bul(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.utils.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) if not obs.utils.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 obs.utils.settings.get_bul(property_id) end obs.utils.settings.bul(property_id, bool_value) return objBool end; };local objColor;objColor={ value= obs.utils.error_wrapper_handler(function(r_color, g_color, b_color, alpha_value) if r_color == nil then return obs.utils.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 obs.utils.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 unique_id= obs.utils.get_unique_id(20) local obs_data_t= obs.utils.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= obs.utils.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) if objNumber.type == obs.enum.number.int then obs.utils.settings.int(pp_unique_name, value) elseif objNumber.type == obs.enum.number.float then obs.utils.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.utils.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 objOption.type == obs.enum.options.string then current_value= settings.str(pp_unique_name) elseif objOption.type == obs.enum.options.int then current_value= settings.int(pp_unique_name) elseif objOption.type == obs.enum.options.float then current_value= settings.dbl(pp_unique_name) elseif objOption.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, 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 -- [[ OTHER UTILS ]] 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