--[[ Author: iixisii Date: 6.19.24 Ver. 1.0.0-rc Description: OK ]] obs = obslua __dy_data = nil __setting__ = nil __DY_DICT__ = {} function welcomeIndex() return [[

upstream X

Ver. 1-rc - Made by iixisii

You can learn more about this script by watching a tutorial video


]] end function script_description() return welcomeIndex() end function script_properties() local p = obs.obs_properties_create() local output_list = obs.obs_properties_add_list(p, "output-list", "output:", obs.OBS_COMBO_TYPE_LIST, obs.OBS_COMBO_FORMAT_STRING) obs.obs_property_list_add_string(output_list,"","stream"); obs.obs_property_list_add_string(output_list,"record","record"); obs.obs_property_list_add_string(output_list,"both (stream & record)","both"); obs.obs_property_list_add_string(output_list,"Dynamic","dc"); local source_target_list = obs.obs_properties_add_list(p, "source-target", "uptime source:", obs.OBS_COMBO_TYPE_LIST, obs.OBS_COMBO_FORMAT_STRING) local info = obs.obs_properties_add_text(p, "info", "", obs.OBS_TEXT_INFO) obs.obs_property_set_visible(info, false) --obs.obs_property_list_add_string(source_target_list,"","def"); obs.obs_properties_add_text(p, "hr1", [[
]], obs.OBS_TEXT_INFO) local event_time = obs.obs_properties_add_text(p, "event-time", "event time:", obs.OBS_TEXT_DEFAULT); local event_source = obs.obs_properties_add_list(p, "event-source", "event source:", obs.OBS_COMBO_TYPE_LIST, obs.OBS_COMBO_FORMAT_STRING) --obs.obs_property_list_add_string(event_source,"","def"); show_sources(source_target_list, ""); show_sources(event_source, ""); -- execute local action_btn = obs.obs_properties_add_button(p, "execute","Execute", function(properties, prop, settings) local t =__setting__.get_str("event-time"); local s =__setting__.get_str("event-source") if s == nil or strTrim(s) == "" or t == nil or strTrim(t) == "" or s == "def" or s == "def2" then return end local timer,m = xparser(t); if(timer==nil) then return nil end local timer_obj = PairStack() for key, value in pairs(timer) do if type(value) == "number" then timer_obj.int(key, value) elseif type(value) == "boolean" then timer_obj.bul(key, value) else timer_obj.str(key, tostring(value)) end end local events;if __dy_data == nil or __dy_data.data == nil then events = __setting__.get_arr(current_scene_name() .. "-event-list") if events == nil or events.data == nil then events = ArrayStack() __setting__.arr(current_scene_name() .. "-event-list", events.data) end else events = __dy_data.get_arr("event-list"); end timer_obj.str("source", s) timer_obj.bul("iscoolingdown",false) timer_obj.int("cooldown",5) timer_obj.str("output",__setting__.get_str("output-list")) timer_obj.bul("isActive",true) timer_obj.int("currTime",0) local src = obs_wrap_source(obs.obs_get_source_by_name(s), OBS_SRC_TYPE) if src and src.data ~= nil then if obs.obs_source_get_unversioned_id(src.data) == "ffmpeg_source" then timer_obj.str("type","video") else timer_obj.str("type","source") end src.free() else timer_obj.str("type","source") end events.insert(timer_obj.data) timer_obj.free() events.free() update_screen(properties); show_events(delete_list, "") return true end) -- information obs.obs_properties_add_text(p, "hr2", [[
]], obs.OBS_TEXT_INFO) obs.obs_properties_add_text(p, "label", "DISPLAY & SCREEN " .. "(showing for " .. tostring(current_scene_name()) .. ")", obs.OBS_TEXT_INFO) obs.obs_properties_add_text(p, "screen", [[
No data ...
]], obs.OBS_TEXT_INFO) -- operation (delete stuff) obs.obs_properties_add_text(p, "hr3", [[
]], obs.OBS_TEXT_INFO) local delete_list = obs.obs_properties_add_list(p, "delete-list", "delete:", obs.OBS_COMBO_TYPE_LIST, obs.OBS_COMBO_FORMAT_STRING) show_events(delete_list, "") -- description for source-target obs.obs_property_set_long_description(source_target_list,"Select the source you want to show the uptime of the current stream.\nYou can select multiple sources, when selected it will display it down below! (do not have to execute to add the target)") -- desc for event time obs.obs_property_set_long_description(event_time,"(Optional)\nThe format is Hh:Mm:Ss stands for (hours, minutes, seconds)\nThe time of an event should execute. This by default is set to recursive, if you want it to execute the event one time only the after it insert (!)"); -- callback function obs.obs_property_set_modified_callback(source_target_list, function(properties, property, settings) local s = PairStack(settings, nil, true) if s == nil or s.data == nil then return end local source_name = s.get_str("source-target") if source_name == "" or source_name == nil or source_name == "def" or source_name == "def2" then return end local obj = PairStack() obj.str("name", source_name) local list;if __dy_data == nil or __dy_data.data == nil then list = s.get_arr(current_scene_name() .. "-source-target-list") if list == nil or list.data == nil then list = ArrayStack(); s.arr(current_scene_name() .. "-source-target-list", list.data); end else list = __dy_data.get_arr("source-list") end local info = obs.obs_properties_get(properties, "info") if list.size() <= 0 then list.insert(obj.data) obs.obs_property_set_visible(info, true) obs.obs_property_set_description(info, "New source added (" .. tostring(source_name) .. ")") else local is_already_added = false while(list.index < list.size()) do local data = list.next() local name = "" if type(data) == "table" then name = data.get_str("name") data.free() else name = data; end if name == source_name then is_already_added = true break end end -- add if not (is_already_added) if not is_already_added then list.insert(obj.data) obs.obs_property_set_visible(info, true) obs.obs_property_set_description(info, "New source added (" .. tostring(source_name) .. ")") else obs.obs_property_set_visible(info, false) end end --s.arr(current_scene_name() .. "-source-target-list", list.data); obj.free();list.free() s.str("source-target","def") update_screen(properties); show_events(delete_list, "") return true end); -- delete list obs.obs_property_set_modified_callback(delete_list, function(properties, property, settings) local delete_id = __setting__.get_str("delete-list"); if delete_id == nil or strTrim(delete_id) == "" or delete_id == "def" or delete_id == "def2" then return end local delete_index = nil; local __s = PairStack(settings, nil, true) -- remove dynamic local __dy_arr = __s.get_arr(current_scene_name() .. "-dy-arr"); if __dy_arr and __dy_arr.data ~= nil then local reset = false while(__dy_arr and __dy_arr.data ~= nil and __dy_arr.index < __dy_arr.size()) do local __curr = __dy_arr.next() if __curr and __curr.data ~= nil then local id = __curr.get_str("id") if id == delete_id then -- remove (hotkey) all dynamics data delete_index = __dy_arr.index - 1; --pcall(function() for _, iter in pairs(__DY_DICT__) do iter.stop(true); obs.obs_hotkey_unregister(iter.start) obs.obs_hotkey_unregister(iter.stop) obs.obs_hotkey_unregister(iter.reset) end --end) if __dy_data and __dy_data.data ~= nil then local d_id = __dy_data.get_str("id") if d_id == id then __setting__.str("output-list","def"); __dy_data.free();__dy_data=nil; end end offsetIndex = delete_index __dy_arr.remove(delete_index) reset = true; delete_index = nil break; else local __events = __curr.get_arr("event-list") local __sources = __curr.get_arr("source-list") -- remove dynamic events while(__events.index < __events.size()) do local __ev = __events.next() if __ev ~= nil and __ev.data ~= nil then local del_id = id .. "-" .. tostring(__ev.get_str("source")) .. "-" .. tostring(__ev.get_str("str")) __ev.free() if del_id == delete_id then delete_index = __events.index - 1 break end end end if delete_index ~= nil then __events.remove(delete_index) end delete_index = nil -- remove dynamic sources while(__sources.index < __sources.size()) do local __src = __sources.next() if __src ~= nil and __src.data ~= nil then local del_id = id .. "-" .. tostring(__src.get_str("name")) __src.free() if del_id == delete_id then delete_index = __sources.index - 1 break end end end if delete_index ~= nil then __sources.remove(delete_index) end __sources.free();__events.free(); end __curr.free() end end if reset then __dy_arr.index = 0;while(__dy_arr and __dy_arr.data ~= nil and __dy_arr.index < __dy_arr.size()) do local __curr = __dy_arr.next() local oldId = __curr.get_str("id") local oldKey = __curr.get_str("hotkey") local newId = tostring((__dy_arr.index)) __curr.str("id","DYNAMIC " .. newId) local newKey = "upstreamX (DYNAMIC " .. tostring(newId) .. ")" __curr.str("hotkey", newKey) __curr.free() end register_hotkeys() end __dy_arr.free() end -- global local events = __setting__.get_arr(current_scene_name() .. "-event-list") if events ~= nil and events.data ~= nil then while(events.index < events.size()) do local curr = events.next() if curr ~= nil and curr.data ~= nil then local id = tostring(curr.get_str("source")) .. "-" .. tostring(curr.get_str("str")) curr.free() if id == delete_id then delete_index = events.index - 1 break end end end if delete_index ~= nil then events.remove(delete_index) --__setting__.arr(current_scene_name() .. "-event-list", events.data) end events.free() end -- check sources local list = __setting__.get_arr(current_scene_name() .. "-source-target-list") if list and list.data ~= nil then while(list.index < list.size()) do local curr = list.next() if curr ~= nil and curr.data ~= nil then local id = tostring(curr.get_str("name")) curr.free() if id == delete_id then delete_index = list.index - 1 break end end end if delete_index ~= nil then list.remove(delete_index) end list.free() end local delete_list = obs.obs_properties_get(properties, "delete-list") __setting__.str("delete-list","def") show_events(delete_list,"") update_screen(properties) return true end); obs.obs_property_set_modified_callback(output_list, function(properties, property, settings) local current = __setting__.get_str("output-list") if current == "dc" then if __dy_data ~= nil then __dy_data.free(); __dy_data = nil; end __dy_data = PairStack() local __dy_arr = __setting__.get_arr(current_scene_name() .. "-dy-arr") if __dy_arr == nil or __dy_arr.data == nil then __dy_arr = ArrayStack() __setting__.arr(current_scene_name() .. "-dy-arr", __dy_arr.data); end local hotkey = "upstreamX (DYNAMIC " .. tostring(__dy_arr.size() + 1) .. ")" local __sources = ArrayStack(); local __events = ArrayStack(); local id = "DYNAMIC " .. tostring(__dy_arr.size() + 1) __dy_data.str("id",id); __dy_data.str("hotkey", hotkey);__dy_data.arr("source-list", __sources.data); __dy_data.arr("event-list", __events.data);__dy_data.int("time",0) __dy_arr.insert(__dy_data.data); __sources.free();__events.free();__dy_arr.free(); dyInit(hotkey, id);update_screen(properties); show_events(delete_list, ""); else if __dy_data ~= nil then __dy_data.free(); __dy_data = nil; end end return true; end); update_screen(p) return p end function script_defaults(settings) __setting__ = PairStack(settings, nil, true) __setting__.str("event-time","",true) __setting__.str("event-source","def",true) __setting__.str("source-target","def",true) __setting__.str("output-list", "stream",true) end function script_unload() if __dy_data ~= nil and __dy_data.data ~= nil then __dy_data.free() end end function script_load(settings) __setting__ = PairStack(settings, nil, true) __setting__.str("output-list", "stream") if __setting__.get_int("recordTime") == nil then __setting__.int("recordTime", 0) end if __setting__.get_int("streamTime") == nil then __setting__.int("streamTime", 0) end local events;if(__setting__) then events = __setting__.get_arr(current_scene_name() .. "-event-list") end while(events and events.data ~= nil and events.index < events.size()) do local curr = events.next() if curr and curr.data ~= nil then curr.bul("iscoolingdown", false) curr.free() end end if events and events.data ~= nil then events.free() end register_hotkeys() -- front events obs.obs_frontend_add_event_callback(frontend_event_handle) end function register_hotkeys() -- load hotkeys (DYNAMIC) local __dy_arr = __setting__.get_arr(current_scene_name() .. "-dy-arr"); if __dy_arr and __dy_arr.data ~= nil then while(__dy_arr and __dy_arr.data ~= nil and __dy_arr.index < __dy_arr.size()) do local __curr = __dy_arr.next() if __curr and __curr.data ~= nil then local hotkey = __curr.get_str("hotkey") local id = __curr.get_str("id") dyInit(hotkey, id) __curr.free() end end end __dy_arr.free(); end function __get_dynamic_by_id(id) local __dy_arr = __setting__.get_arr(current_scene_name() .. "-dy-arr"); if __dy_arr and __dy_arr.data ~= nil then while(__dy_arr and __dy_arr.data ~= nil and __dy_arr.index < __dy_arr.size()) do local __curr = __dy_arr.next() if __curr and __curr.data ~= nil then local curr_id = __curr.get_str("id") if curr_id == id then __dy_arr.free(); return __curr; end __curr.free() end end end __dy_arr.free(); return nil end function dyInit(hotkey, id) local curr = nil;local stop_hotkey_id = nil; local start_hotkey_id = nil;local reset_hotkey_id = nil; table.insert(__DY_DICT__, { id = id;key=hotkey;init_start = false;init_stop=false;init_reset=false;start = function(bool) if not bool or curr.isActive then return end local __dy = __get_dynamic_by_id(id); local __hotkey = ArrayStack(obs.obs_hotkey_save(start_hotkey_id), nil, true) if __hotkey.data ~= nil then __dy.arr("reg_hotkey_start", __hotkey.data); __dy.free() end __hotkey.free() curr.isActive = true; local function InterMain() if curr.isResetting then curr.isResetting = false; return end if not curr.isActive then return obs.timer_remove(InterMain) end -- curr.time = curr.time + 1 local __dy = __get_dynamic_by_id(id); local time = 0; if __dy ~= nil and __dy.data ~= nil then time = __dy.get_int("time") + 1; __dy.int("time",time); -- display time local __sources = __dy.get_arr("source-list") local __source_name;if __sources.size() > 0 then __source_name = __sources.next(); if __source_name and __source_name.data ~= nil then local source_obj = obs_wrap_source(obs.obs_get_source_by_name(__source_name.get_str("name")), OBS_SRC_TYPE) if source_obj and source_obj.data ~= nil then local update_text = PairStack() update_text.str("text", format_time(time)) obs.obs_source_update(source_obj.data, update_text.data); update_text.free() source_obj.free() end __source_name.free() end end __sources.free() -- trigger events local __events = __dy.get_arr("event-list") while(__events and __events.data ~= nil and __events.index < __events.size()) do local curr = __events.next() if curr and curr.data ~= nil and curr.get_bul("isActive") == true and curr.get_bul("iscoolingdown") == false then curr.int("currTime",curr.get_int("currTime")+1) local curr_time = curr.get_int("currTime") if curr.get_int("time") and curr_time >= curr.get_int("time") then if(not curr.get_bul("cr") == true) then curr.bul("isActive", false) end curr.int("currTime", 0) -- execute event local source_name = curr.get_str("source") local _sceneitem = __GET_SCENE_ITEM__(source_name) if _sceneitem and _sceneitem.data ~= nil then obs.obs_source_set_enabled(obs.obs_sceneitem_get_source(_sceneitem.data), true) obs.obs_sceneitem_set_visible(_sceneitem.data, true) if curr.get_str("type") == "video" then obs.obs_source_media_restart(obs.obs_sceneitem_get_source(_sceneitem.data)) obs.obs_source_media_play_pause(obs.obs_sceneitem_get_source(_sceneitem.data), true) end _sceneitem.free() end curr.bul("iscoolingdown",true) local curr_index = __events.index - 1 -- reset cooldown scheduler(curr.get_int("cooldown") * 1000).after(function() local __dy = __get_dynamic_by_id(id); if __dy == nil or __dy.data == nil then return end local __events = __dy.get_arr("event-list") if __events and __events.data ~= nil then local iter = __events.get(curr_index) if iter and iter.data ~= nil then local source_name = iter.get_str("source") local _sceneitem = __GET_SCENE_ITEM__(source_name) if _sceneitem and _sceneitem.data ~= nil then if iter.get_str("type") == "video" then obs.obs_source_media_play_pause(obs.obs_sceneitem_get_source(_sceneitem.data), false) obs.obs_source_media_restart(obs.obs_sceneitem_get_source(_sceneitem.data)) end obs.obs_sceneitem_set_visible(_sceneitem.data, false) _sceneitem.free() end iter.bul("iscoolingdown", false) iter.free() end end __events.free();__dy.free() end) end curr.free() end end __events.free() __dy.free(); end end return obs.timer_add(InterMain,1000) end;stop = function(bool) if not bool then return end curr.isActive=false; local __dy = __get_dynamic_by_id(id); local __hotkey = ArrayStack(obs.obs_hotkey_save(stop_hotkey_id), nil, true) if __hotkey.data ~= nil then __dy.arr("reg_hotkey_stop", __hotkey.data); __dy.free() end __hotkey.free() end;reset = function(bool) if not bool then return end reset_initial_dynamic_operation() local __dy = __get_dynamic_by_id(id); local __hotkey = ArrayStack(obs.obs_hotkey_save(reset_hotkey_id), nil, true) if __hotkey.data ~= nil then __dy.arr("reg_hotkey_reset", __hotkey.data); end __hotkey.free() if __dy ~= nil and __dy.data ~= nil then __dy.int("time",0); -- display time local __sources = __dy.get_arr("source-list") local __source_name;if __sources.size() > 0 then __source_name = __sources.next(); if __source_name and __source_name.data ~= nil then local source_obj = obs_wrap_source(obs.obs_get_source_by_name(__source_name.get_str("name")), OBS_SRC_TYPE) if source_obj and source_obj.data ~= nil then local update_text = PairStack() update_text.str("text", format_time(0)) obs.obs_source_update(source_obj.data, update_text.data); update_text.free() source_obj.free() end __source_name.free() end end __sources.free() __dy.free(); end end;isActive=false; });curr = __DY_DICT__[#__DY_DICT__]; start_hotkey_id = obs.obs_hotkey_register_frontend(script_path(),hotkey .. " START", curr.start); reset_hotkey_id = obs.obs_hotkey_register_frontend(script_path(),hotkey .. " RESET", curr.reset); stop_hotkey_id = obs.obs_hotkey_register_frontend(script_path(),hotkey .. " STOP", curr.stop); local __dy = __get_dynamic_by_id(id); if __dy and __dy.data ~= nil then local __start_hotkey = __dy.get_arr("reg_hotkey_start") local __stop_hotkey = __dy.get_arr("reg_hotkey_stop") local __reset_hotkey = __dy.get_arr("reg_hotkey_reset") -- load reset (HOTKEY) if __reset_hotkey.data ~= nil then obs.obs_hotkey_load(reset_hotkey_id, __reset_hotkey.data) __reset_hotkey.free() end -- load start (HOTKEY) if __start_hotkey.data ~= nil then obs.obs_hotkey_load(start_hotkey_id, __start_hotkey.data) __start_hotkey.free() end -- load stop (HOTKEY) if __stop_hotkey.data ~= nil then obs.obs_hotkey_load(stop_hotkey_id, __stop_hotkey.data) __stop_hotkey.free() end __dy.free() end end function splitBy(string_value, op) if not string_value then return nil end local pipes = {} local whole = "" for char in string.gmatch(string_value,".") do if op == char then if whole ~= "" then table.insert(pipes, whole) end whole = "" else whole = whole .. char end end if whole ~= "" then table.insert(pipes, whole) end return pipes; end function xparser(str) local result = {} local sl = splitBy(str,":") if sl == nil or #sl <= 2 or type(tonumber(sl[1])) ~= "number" or type(tonumber(sl[2])) ~= "number" then return nil, "Invalid time format" end local h,m, s = str:match("(%d+):(%d+):(%d+)") if (h and m and s) then result.hh = tonumber(h) * 3600000 -- 1 hour = 3600000 milliseconds result.mm = tonumber(m) * 60000 -- 1 minute = 60000 milliseconds result.ss = tonumber(s) * 1000 -- 1 second = 1000 milliseconds result.h = tonumber(h) result.m = tonumber(m) result.s = tonumber(s) if result.h <= 0 and result.m <= 0 and result.s <= 0 then return nil, "Invalid time format" end result.time = (result.h * 3600) + (result.m * 60) + result.s; else return nil, "Invalid time format" end if str:find("!") then result.cr = false else result.cr = true end result.str = str return result end function fixLog(iter, ib,idn) local v = "" if type(iter) == "table" then for x, y in pairs(iter) do if type(x) == "string" then v = v .. tostring(x) .. ": " .. fixLog(y, ib,idn) .. " " elseif ib then v = v .. tostring(x) .. ": " .. fixLog(y,ib,idn) .. " " else v = v .. fixLog(y) .. " " end end return "{" .. v .. "}" elseif type(iter) == "function" then return "(function)"; else return tostring(iter) end end function current_scene_name() -- get the current scene name; local f_obj = obs.obs_frontend_get_current_scene() local name = obs.obs_source_get_name(f_obj) obs.obs_source_release(f_obj); if name == nil then name = "" end return name end -- returns an array that holds all the sources(items/objects) in the scene! function get_all_current_sources_from_scene(ignoreSource) local source_name_list = nil -- Get the current scene local currentSource = obs.obs_frontend_get_current_scene() local currentScene = obs.obs_scene_from_source(currentSource) if currentScene ~= nil and currentSource ~= nil then -- Enumerate the items in the current scene local sceneItems = obs.obs_scene_enum_items(currentScene) if sceneItems ~= nil then source_name_list = {} for _, sceneItem in ipairs(sceneItems) do local source = obs.obs_sceneitem_get_source(sceneItem) if source ~= nil then local sourceName = obs.obs_source_get_name(source) if obs.obs_sceneitem_is_group(sceneItem) then local __ls = obs.obs_sceneitem_group_enum_items(sceneItem) if __ls ~= nil then -- iterate through all the items in the group; for _, it in ipairs(__ls) do local s = obs.obs_sceneitem_get_source(it) if s ~= nil then local sN = obs.obs_source_get_name(s) if not ignoreSource then table.insert(source_name_list, sN) elseif ignoreSource ~= sN then table.insert(source_name_list, sN) end end end obs.sceneitem_list_release(__ls) end end if not ignoreSource then table.insert(source_name_list, sourceName) elseif ignoreSource ~= sourceName then table.insert(source_name_list, sourceName) end end end end -- Release the scene items list obs.sceneitem_list_release(sceneItems) end -- Release the current scene source obs.obs_source_release(currentSource) return source_name_list end -- show all the events in a list function show_events(prop, def) if __setting__ == nil or __setting__.data == nil then return end if not def then def = "Select options" end obs.obs_property_list_clear(prop) obs.obs_property_list_add_string(prop, def, "def") local total = 0; -- show DYNAMICS local __dy_arr = __setting__.get_arr(current_scene_name() .. "-dy-arr"); if __dy_arr and __dy_arr.data ~= nil then while(__dy_arr and __dy_arr.data ~= nil and __dy_arr.index < __dy_arr.size()) do local __curr = __dy_arr.next() if __curr and __curr.data ~= nil then total = total + 1 local id = __curr.get_str('id'); obs.obs_property_list_add_string(prop,"(Hotkey) " .. id, id) -- events local __events = __curr.get_arr("event-list") if __events and __events.data ~= nil then while(__events.index < __events.size()) do local _next = __events.next() if _next ~= nil and _next.data ~= nil then total = total + 1 local str = _next.get_str("str") local source_name = _next.get_str('source') obs.obs_property_list_add_string(prop, id .. " => " .. tostring(str) .. " | " .. tostring(source_name), id .. "-" .. tostring(source_name) .. "-" .. tostring(str)) _next.free() end end __events.free() end -- sources local __sources = __curr.get_arr("source-list") if __sources ~= nil and __sources.data ~= nil then while(__sources.index < __sources.size()) do local _next = __sources.next() if _next ~= nil and _next.data ~= nil then total = total + 1 local str = _next.get_str("str") local source_name = _next.get_str('name') obs.obs_property_list_add_string(prop, id .. " => " .. "(Source) " .. tostring(source_name), id .. "-" .. source_name) _next.free() end end __sources.free() end __curr.free(); end end __dy_arr.free() end local events = __setting__.get_arr(current_scene_name() .. "-event-list") local c = 0 if events and events.data ~= nil then while(events.index < events.size()) do local _next = events.next() if _next ~= nil and _next.data ~= nil then total = total + 1 local str = _next.get_str("str") local source_name = _next.get_str('source') obs.obs_property_list_add_string(prop, tostring(str) .. " | " .. tostring(source_name), tostring(source_name) .. "-" .. tostring(str)) _next.free() end end events.free() end local list = __setting__.get_arr(current_scene_name() .. "-source-target-list") if list and list.data ~= nil then while(list.index < list.size()) do local _next = list.next() if _next ~= nil and _next.data ~= nil then total = total + 1 local str = _next.get_str("str") local source_name = _next.get_str('name') obs.obs_property_list_add_string(prop, "(Source) " .. tostring(source_name), source_name) _next.free() end end list.free() end if total <= 0 then obs.obs_property_list_add_string(prop, "(NO DATA ARE AVALIABLE)", "def2") end return true end function show_sources(prop, def) if not def then def = "Select options" end obs.obs_property_list_clear(prop) obs.obs_property_list_add_string(prop, def, "def") -- local scene_source = obs_wrap_source(obs.obs_frontend_get_current_scene(), OBS_SRC_TYPE) local sources_names = get_all_current_sources_from_scene() for _, name in pairs(sources_names) do obs.obs_property_list_add_string(prop, name, name); end if #sources_names <= 0 then obs.obs_property_list_add_string(prop, "(NO SOURCES ARE AVALIABLE)", "def2") end return true end function strTrim(str) if not str then return "" end return str:gsub("^%s*(.-)%s*$", "%1"); end -- draws the screen function update_screen(properties) if properties == nil then return end if __setting__ == nil or __setting__.data == nil then return end local screen = obs.obs_properties_get(properties, "screen") local desc = ""; local list = __setting__.get_arr(current_scene_name() .. "-source-target-list") local events = __setting__.get_arr(current_scene_name() .. "-event-list") -- show all the events local function write_events(iter, tabs) if not tabs then tabs = "" end while(iter.index < iter.size()) do local _next = iter.next() if _next ~= nil and _next.data ~= nil then local source_name = _next.get_str("source") local h = _next.get_int("h") local m = _next.get_int("m") local s = _next.get_int("s") local cr = _next.get_bul("cr") desc = desc .. "
" .. "(Event)" if cr == true then desc = desc .. [[ At every: ]] else desc = desc .. [[ At: ]] end local _t = "" if h > 0 and h > 1 then _t = tostring(h) .. " hours " elseif h == 1 then _t = tostring(h) .. " hour " end if m > 0 and m > 1 then if strTrim(_t) ~= "" then _t = _t .. ", and " .. tostring(m) .. " minutes " else _t = tostring(m) .. " minutes " end elseif m == 1 then if strTrim(_t) ~= "" then _t = _t .. ", and " .. tostring(m) .. " minute " else _t = tostring(m) .. " minute " end end if s > 0 and s > 1 then if strTrim(_t) ~= "" then _t = _t .. "and " .. tostring(s) .. " seconds " else _t = tostring(s) .. " seconds " end elseif s == 1 then if strTrim(_t) ~= "" then _t = _t .. "and " .. tostring(s) .. " second " else _t = tostring(s) .. " second " end end local act = ""; local _source = obs_wrap_source(obs.obs_get_source_by_name(source_name), OBS_SRC_TYPE) local source_id = obs.obs_source_get_unversioned_id(_source.data) if source_id == "ffmpeg_source" then act = "play" else act = "show" end desc = desc .."" .. _t .. " "; desc = desc .. "Do: " .. act .. " " desc = desc .."For: " .. tostring(source_name) .. '
'; _source.free() _next.free() end end end -- show all the sources local function write_sources(iter, tabs) if not tabs then tabs = "" end while(iter.index < iter.size()) do local data = iter.next() local name = "" if type(data) == "table" then name = data.get_str("name") data.free() else name = data; end desc = desc .. "
" .. "(SOURCE) " .. tostring(name) .. "
"; end end -- dynamic events & sources local __dy_arr = __setting__.get_arr(current_scene_name() .. "-dy-arr") if __dy_arr ~= nil and __dy_arr.data ~= nil then while(__dy_arr and __dy_arr.data ~= nil and __dy_arr.index < __dy_arr.size()) do desc = desc .. "
(Dynamic " .. tostring(__dy_arr.index + 1) .. ")
"; local __dy_data = __dy_arr.next() if __dy_data ~= nil and __dy_data.data ~= nil then local __currEvents = __dy_data.get_arr("event-list") if __currEvents and __currEvents.data ~= nil and __currEvents.size() > 0 then write_events(__currEvents, " ") --desc = desc .. "
" __currEvents.free() end local __currList = __dy_data.get_arr("source-list"); if __currList and __currList.data ~= nil and __currList.size() > 0 then write_sources(__currList, " "); --desc = desc .. "
" __currList.free() end __dy_data.free() end end __dy_arr.free() end -- global events desc = desc .. "
(default global)
"; if events ~= nil and events.data ~= nil then write_events(events, " ") events.free() end -- show all the added source targets if list ~= nil and list.data ~= nil then write_sources(list, " ") list.free() end if strTrim(desc) == "" then obs.obs_property_set_description(screen,"
No data ...
") else obs.obs_property_set_description(screen, desc) end return true end scheduled_events = {} function 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() obs.timer_remove(interval) if type(scheduler_callback) ~= "function" then return end return 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 obs.script_log(obslua.LOG_ERROR, "[Scheduler] invalid callback/timeout " .. type(callback)) return false end obs.timer_add(interval, timeout) end;push = function(callback) if callback == nil or type(callback) ~= "function" then obs.script_log(obslua.LOG_WARNING, "[Scheduler] invalid callback at {push} " .. type(callback)) return false end obs.timer_add(callback, timeout) table.insert(scheduled_events, callback) return { clear = function() if callback == nil or type(callback) ~= "function" then return nil end return obs.timer_remove(callback) end; } end; clear = function() if scheduler_callback ~= nil then obs.timer_remove(scheduler_callback) end for _, clb in pairs(scheduled_events) do obs.timer_remove(clb) end scheduled_events = {}; scheduler_callback = nil end } return self end 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 obs_wrap_source = {}; function obs_wrap_source(object, object_type) local self = nil self = { type = object_type, data = object;free = function() if self.type == OBS_SCENE_TYPE then obs.obs_scene_release(self.data) elseif self.type == OBS_SRC_TYPE then obs.obs_source_release(self.data) elseif self.type == OBS_ARR_TYPE then obs.obs_data_array_release(self.data) elseif self.type == OBS_OBJ_TYPE then obs.obs_data_release(self.data) elseif self.type == OBS_SCENEITEM_TYPE then obs.obs_sceneitem_release(self.data) elseif self.type == OBS_SCENEITEM_LIST_TYPE then obs.sceneitem_list_release(self.data) elseif self.type == OBS_SRC_LIST_TYPE then obs.source_list_release(self.data) elseif self.type == OBS_UN_IN_TYPE then self.data = nil return else self.data = nil end end } table.insert(error_wrapper, self) return self end error_freed = 0 error_wrapper = {};function 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 obs.script_log(obs.LOG_ERROR, "[ErrorWrapper ERROR] => " .. tostring(result)) end return data end end -- array handle function ArrayStack(stack, name, ignoreStack) if not ignoreStack then if type(stack) ~= "userdata" then stack = nil elseif stack and (type(name) ~= "string" or name == "") then stack = nil obs.script_log(obs.LOG_ERROR, "FAILED TO LOAD AN [ArrayStack] INVALID NAME GIVEN") return nil end end local self = nil self = { index = 0;get = function(index) if type(index) ~= "number" or index < 0 then return nil end if index > self.size() then return nil end return obs_wrap_source(obs.obs_data_array_item(self.data, index),OBS_OBJ_TYPE) end;next = function() if self.data == nil then return nil end if type(self.index) ~= "number" or self.index < 0 or self.index > self.size() then return nil end local temp = self.index;self.index = self.index + 1 return PairStack(obs.obs_data_array_item(self.data, temp), nil, true) end;get = function(index) if self.data == nil then return nil end if type(index) ~= "number" or index< 0 or index > self.size() then return nil end return PairStack(obs.obs_data_array_item(self.data, index), nil, true) end;free = function() if self.data == nil then return false end obs.obs_data_array_release(self.data) self.data = nil return true end;insert = error_wrapper_handler(function(value) if self.data == nil then return false end if value == nil or type(value) ~= "userdata" then obs.script_log("FAILED TO INSERT OBJECT INTO [ArrayStack]") return false end obs.obs_data_array_push_back(self.data, value) return true end); size = error_wrapper_handler(function() if self.data == nil then return 0 end return obs.obs_data_array_count(self.data); end);remove = error_wrapper_handler(function(index) if self.data == nil or self.size() <= 0 or type(index) ~= "number" or self.size() < index or index < 0 then return false end obs.obs_data_array_erase(self.data, index) return true end); } if not ignoreStack then if stack and name then self.data = obs.obs_data_get_array(stack, name) else self.data = obs.obs_data_array_create() end else self.data = stack end table.insert(error_wrapper, self) return self end -- pair stack used to manage memory stuff :) function PairStack(stack, name, ignoreStack) if not ignoreStack then if type(stack) ~= "userdata" then stack = nil elseif stack and (type(name) ~= "string" or name == "")then stack = nil obs.script_log(obs.LOG_ERROR, "FAILED TO LOAD AN [PairStack] INVALID NAME GIVEN") return nil end end local self = nil; self = { free = function() if self.data == nil then return false end obs.obs_data_release(self.data) self.data = nil return true end; str = error_wrapper_handler(function(name, value, def) if self.data == nil then return false 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 obs.script_log(obs.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 obs.obs_data_set_default_string(self.data, name, value) else obs.obs_data_set_string(self.data, name, value) end return true end);int = error_wrapper_handler(function(name, value, def) 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 obs.script_log(obs.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 obs.obs_data_set_default_int(self.data, name, value) else obs.obs_data_set_int(self.data, name, value) end return true end);dbl=error_wrapper_handler(function(name, value, def) 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 obs.script_log(obs.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 obs.obs_data_set_default_double(self.data, name, value) else obs.obs_data_set_double(self.data, name, value) end return true end);bul = error_wrapper_handler(function(name, value, def) 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 obs.script_log(obs.LOG_ERROR,"FAILED TO INSERT BUL [PairStack] " .. "FOR [" .. tostring(name) .. "] " .. " OF VALUE [" .. tostring(value) .. "] TYPE: " .. tostring(type(value))) return false end if def then obs.obs_data_set_default_bool(self.data, name, value) else obs.obs_data_set_bool(self.data, name, value) end return true end); arr = error_wrapper_handler(function(name, value, def) if (name == nil or type(name) ~= "string" or name == "") or (self.data == nil or type(self.data) ~= "userdata") or (type(value) ~="userdata") then obs.script_log(obs.LOG_ERROR,"FAILED TO INSERT ARR INTO [PairStack] " .. "FOR [" .. tostring(name) .. "] " .. " OF VALUE [" .. tostring(value) .. "] TYPE: " .. tostring(type(value))) return false end if def then obs.obs_data_set_default_array(self.data, name, value) else obs.obs_data_set_array(self.data, name, value) end return true end); obj = error_wrapper_handler(function(name, value, def) if (name == nil or type(name) ~= "string" or name == "") or (type(self.data) ~= "userdata") or (type(value) ~="userdata") then obs.script_log(obs.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 obs.obs_data_set_default_obj(self.data, name, value) else obs.obs_data_set_obj(self.data, name, value) end return true end); -- getter get_str = error_wrapper_handler(function(name, def) if (name == nil or type(name) ~= "string" or name == "") or (type(self.data) ~= "userdata")then obs.script_log(obs.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 obs.obs_data_get_string(self.data, name) else return obs.obs_data_get_default_string(self.data, name) end end);get_int = error_wrapper_handler(function(name, def) if (name == nil or type(name) ~= "string" or name == "") or (type(self.data) ~= "userdata")then obs.script_log(obs.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 obs.obs_data_get_int(self.data, name) else return obs.obs_data_get_default_int(self.data, name) end end);get_dbl = error_wrapper_handler(function(name, def) if (name == nil or type(name) ~= "string" or name == "") or (type(self.data) ~= "userdata")then obs.script_log(obs.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 obs.obs_data_get_double(self.data, name) else return obs.obs_data_get_default_double(self.data, name) end end);get_obj = error_wrapper_handler(function(name, def) if (name == nil or type(name) ~= "string" or name == "") or (type(self.data) ~= "userdata")then obs.script_log(obs.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 PairStack(obs.obs_data_get_obj(self.data, name), nil, true) else return PairStack(obs.obs_data_get_default_obj(self.data, name), nil, true) end end);get_arr =error_wrapper_handler(function(name, def) if (name == nil or type(name) ~= "string" or name == "") or (type(self.data) ~= "userdata")then obs.script_log(obs.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 ArrayStack(obs.obs_data_get_array(self.data, name), nil, true) else return ArrayStack(obs.obs_data_get_default_array(self.data, name), nil, true) end end);get_bul = error_wrapper_handler(function(name, def) if (name == nil or type(name) ~= "string" or name == "") or (type(self.data) ~= "userdata") then obs.script_log(obs.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 obs.obs_data_get_bool(self.data, name) else return obs.obs_data_get_default_bool(self.data, name) end end); } if not ignoreStack then if stack and name then self.data = obs.obs_data_get_obj(stack, name) else self.data = obs.obs_data_create() end else self.data = stack end table.insert(error_wrapper, self) return self end __LIST_SCENE_ITEMS__ = {} function __GroupList() -- Get the current scene local currentSource = obs.obs_frontend_get_current_scene() local currentScene = obs.obs_scene_from_source(currentSource) -- local list; if currentScene ~= nil and currentSource ~= nil then -- Enumerate the items in the current scene local sceneItems = obs.obs_scene_enum_items(currentScene) if sceneItems ~= nil then list = {} for _, sceneItem in ipairs(sceneItems) do local source = obs.obs_sceneitem_get_source(sceneItem) if source ~= nil then local sourceName = obs.obs_source_get_name(source) if obs.obs_sceneitem_is_group(sceneItem) then table.insert(list, sourceName) end end end obs.sceneitem_list_release(sceneItems) end end obs.obs_source_release(currentSource) return list; end function __GET_SCENE_ITEM__(item_name) local sourceObject = obs.obs_get_source_by_name(item_name) local currentSource = obs.obs_frontend_get_current_scene() local currentScene = obs.obs_scene_from_source(currentSource) if currentScene ~= nil then local scene_item = obs.obs_scene_sceneitem_from_source(currentScene, sourceObject) -- check in groups if the current item doesn't exist; if not scene_item then for _, gN in ipairs(__GroupList()) do local groupSource = obs.obs_get_source_by_name(gN) if groupSource then local groupItem = obs.obs_scene_sceneitem_from_source(currentScene, groupSource) obs.obs_source_release(groupSource) if groupItem then -- iterate through the items in the group and check for (item_name); local hasItem = false local __ls = obs.obs_sceneitem_group_enum_items(groupItem) if __ls ~= nil then for _, it in ipairs(__ls) do local s = obs.obs_sceneitem_get_source(it) if s ~= nil then local sN = obs.obs_source_get_name(s) if sN == item_name then obs.obs_sceneitem_addref(it) scene_item = it hasItem = true; break end end end obs.sceneitem_list_release(__ls) end obs.obs_sceneitem_release(groupItem) if hasItem then break end end end end end obs.obs_source_release(currentSource) obs.obs_source_release(sourceObject) local item_obj = { index = (#__LIST_SCENE_ITEMS__) + 1; data = scene_item } item_obj["free"] = function() if item_obj.data ~= nil then obs.obs_sceneitem_release(item_obj.data) item_obj.data = nil table.remove(__LIST_SCENE_ITEMS__, item_obj.index) return true end table.remove(__LIST_SCENE_ITEMS__, item_obj.index) return false end table.insert(__LIST_SCENE_ITEMS__, item_obj) return __LIST_SCENE_ITEMS__[#__LIST_SCENE_ITEMS__] end obs.obs_source_release(currentSource) obs.obs_source_release(sourceObject) return nil end function format_time(seconds) -- Calculate days, hours, minutes, and remaining seconds local days = math.floor(seconds / (24 * 3600)) seconds = seconds % (24 * 3600) local hours = math.floor(seconds / 3600) seconds = seconds % 3600 local minutes = math.floor(seconds / 60) seconds = seconds % 60 -- Build the formatted string local formatted_time = "" if days > 0 then formatted_time = formatted_time .. days .. " day" .. (days > 1 and "s and " or " and ") end formatted_time = formatted_time .. string.format("%02d:%02d:%02d", hours, minutes, seconds) return formatted_time end function reset_initial_operation() local events;if(__setting__) then events = __setting__.get_arr(current_scene_name() .. "-event-list") end while(events and events.data ~= nil and events.index < events.size()) do local curr = events.next() if curr and curr.data ~= nil then curr.int("currTime",0) curr.bul("isActive", true) curr.bul("iscoolingdown", false) curr.free() end end if events and events.data ~= nil then events.free() end end function reset_initial_dynamic_operation() -- dynamic option reset local __dy_arr = __setting__.get_arr(current_scene_name() .. "-dy-arr") if __dy_arr ~= nil and __dy_arr.data ~= nil then while(__dy_arr and __dy_arr.data ~= nil and __dy_arr.index < __dy_arr.size()) do local __dy_data = __dy_arr.next() if __dy_data ~= nil and __dy_data.data ~= nil then local __currEvents = __dy_data.get_arr("event-list") if __currEvents and __currEvents.data ~= nil then while(__currEvents and __currEvents.data ~= nil and __currEvents.index < __currEvents.size()) do local curr = __currEvents.next() if curr and curr.data ~= nil then curr.int("currTime",0) curr.bul("isActive", true) curr.bul("iscoolingdown", false) curr.free() end end __currEvents.free() end __dy_data.free() end end __dy_arr.free() end end -- listen to events from the frontend function frontend_event_handle(id, caller) if id == obs.OBS_FRONTEND_EVENT_RECORDING_STARTING or id == obs.OBS_FRONTEND_EVENT_RECORDING_STOPPED then __setting__.int("recordTime", 0) reset_initial_operation() elseif id == obs.OBS_FRONTEND_EVENT_STREAMING_STARTING or id == obs.OBS_FRONTEND_EVENT_STREAMING_STOPPING then __setting__.int("streamTime", 0) reset_initial_operation(); -- exit elseif id == obs.OBS_FRONTEND_EVENT_SCRIPTING_SHUTDOWN then reset_initial_operation() reset_initial_dynamic_operation() elseif id == obs.OBS_FRONTEND_EVENT_SCENE_CHANGED then -- remove all the hotkeys! reset_initial_dynamic_operation() for _, iter in pairs(__DY_DICT__) do obs.obs_hotkey_unregister(iter.stop) obs.obs_hotkey_unregister(iter.start) obs.obs_hotkey_unregister(iter.reset) end __DY_DICT__ = {}; register_hotkeys() end end --- main do stuff function main() local is_recording = obs.obs_frontend_recording_active() local is_streaming = obs.obs_frontend_streaming_active() local sources;if(__setting__) then sources = __setting__.get_arr(current_scene_name() .. "-source-target-list") end if is_streaming then if __setting__ and __setting__.get_int("streamTime") ~= nil then __setting__.int("streamTime", __setting__.get_int("streamTime") + 1) end if sources ~= nil and sources.data ~= nil and sources.size() > 0 then local labelobj = sources.next() if labelobj and labelobj.data ~= nil then local source_obj = obs_wrap_source(obs.obs_get_source_by_name(labelobj.get_str("name")), OBS_SRC_TYPE) if source_obj and source_obj.data ~= nil then local update_text = PairStack() update_text.str("text", format_time(__setting__.get_int("streamTime"))) obs.obs_source_update(source_obj.data, update_text.data); update_text.free() source_obj.free() end labelobj.free() end end end if is_recording then if __setting__ and __setting__.get_int("recordTime") ~= nil then __setting__.int("recordTime", __setting__.get_int("recordTime") + 1) end if sources ~= nil and sources.data ~= nil and sources.size() > 0 then local labelobj = sources.next() if labelobj and labelobj.data ~= nil then local source_obj = obs_wrap_source(obs.obs_get_source_by_name(labelobj.get_str("name")), OBS_SRC_TYPE) if source_obj and source_obj.data ~= nil then local update_text = PairStack() update_text.str("text", format_time(__setting__.get_int("recordTime"))) obs.obs_source_update(source_obj.data, update_text.data); update_text.free() source_obj.free() end labelobj.free() end end end if sources and sources.data ~= nil then sources.free() end if is_recording or is_streaming then local events;if(__setting__) then events = __setting__.get_arr(current_scene_name() .. "-event-list") end while(events and events.data ~= nil and events.index < events.size()) do local curr = events.next() if curr and curr.data ~= nil and curr.get_bul("isActive") == true and curr.get_bul("iscoolingdown") == false then curr.int("currTime",curr.get_int("currTime")+1) local curr_time = curr.get_int("currTime") if curr.get_int("time") and curr_time >= curr.get_int("time") then if(not curr.get_bul("cr") == true) then curr.bul("isActive", false) end curr.int("currTime", 0) -- execute event local source_name = curr.get_str("source") local _sceneitem = __GET_SCENE_ITEM__(source_name) if _sceneitem and _sceneitem.data ~= nil then obs.obs_source_set_enabled(obs.obs_sceneitem_get_source(_sceneitem.data), true) obs.obs_sceneitem_set_visible(_sceneitem.data, true) if curr.get_str("type") == "video" then obs.obs_source_media_restart(obs.obs_sceneitem_get_source(_sceneitem.data)) obs.obs_source_media_play_pause(obs.obs_sceneitem_get_source(_sceneitem.data), true) end _sceneitem.free() end curr.bul("iscoolingdown",true) local curr_index = events.index - 1 -- reset cooldown scheduler(curr.get_int("cooldown") * 1000).after(function() local events = __setting__.get_arr(current_scene_name() .. "-event-list") if events and events.data ~= nil then local iter = events.get(curr_index) if iter and iter.data ~= nil then local source_name = iter.get_str("source") local _sceneitem = __GET_SCENE_ITEM__(source_name) if _sceneitem and _sceneitem.data ~= nil then if iter.get_str("type") == "video" then obs.obs_source_media_play_pause(obs.obs_sceneitem_get_source(_sceneitem.data), false) obs.obs_source_media_restart(obs.obs_sceneitem_get_source(_sceneitem.data)) end obs.obs_sceneitem_set_visible(_sceneitem.data, false) _sceneitem.free() end iter.bul("iscoolingdown", false) iter.free() end events.free() end end) end curr.free() end end if events and events.data ~= nil then events.free() end end end obs.timer_add(main, 1000)