--[[ Author: @iixisii Date: 02.21.2025 Description: YES, PLEASE! Version: 2.0.1 ]] obs = obslua os = require 'os' bit = require 'bit' utils = {properties={};history={}}; filter= {}; event_list= { {id="fade"; name="will hide and show"}, {id="flicker"; name="will flicker"}, {id="hover";name="will hover up and down"}, {id="rotate";name="will rotate 360 degrees"}, {id="shake";name="will shake"}, } event_video_list= { {id="pause"; name = "will pause"}, {id="play"; name = "will play"}, } hotkey_key_list= { "a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z", "1","2","3","4","5","6","7","8","9","0", "num1","num2","num3","num4","num5","num6","num7","num8","num9","num0", "enter","esc","backspace","tab","space","minus","equals","leftbracket","rightbracket","backslash","semicolon","apostrophe","grave","comma","period","slash","capslock", "f1","f2","f3","f4","f5","f6","f7","f8","f9","f10","f11","f12", "printscreen","scrolllock","pause","insert","home","pageup","delete","end","pagedown","right","left","down","up", } filter.id= "filter-upstreamx-iixisii-v2.0.1" filter.type= obs.OBS_SOURCE_TYPE_FILTER filter.output_flags= bit.bor(obs.OBS_SOURCE_VIDEO) function get_filter_source(src) return obs.obs_filter_get_target(src.source) end filter.get_name= function() return "UpstreamX" end filter.get_height= function(src) if src == nil then return 0 end return src.height end filter.get_width= function(src) if src == nil then return 0 end return src.width end filter.get_properties= function(src) local p= obs.obs_properties_create() local mode= utils.obs_properties_add_list(p, "mode", "", obs.OBS_COMBO_TYPE_LIST, obs.OBS_COMBO_FORMAT_STRING) obs.obs_property_list_add_string(mode, "Time will activate when streaming or recording", "mode1") obs.obs_property_list_add_string(mode, "Time will activate when streaming", "mode2") obs.obs_property_list_add_string(mode, "Time will activate when recording", "mode3") obs.obs_property_list_add_string(mode, "Time will start/stop when you press specific hotkey", "mode4") -- [[ HOTKEY GROUP ]] local hotkey_start_op= obs.obs_properties_create() local hotkey_start_group= utils.obs_properties_add_group(p, "hotkey_start_group", "Start", obs.OBS_GROUP_NORMAL,hotkey_start_op) local hotkey_start_shift_key= utils.obs_properties_add_bool(hotkey_start_op, "hotkey_start_shift_key", "SHIFT") local hotkey_start_key_list= utils.obs_properties_add_list(hotkey_start_op, "hotkey_start_key_list", "", obs.OBS_COMBO_TYPE_LIST, obs.OBS_COMBO_FORMAT_STRING) obs.obs_property_list_add_string(hotkey_start_key_list, "SELECT KEY", "") local hotkey_stop_op= obs.obs_properties_create() local hotkey_stop_group= utils.obs_properties_add_group(p, "hotkey_stop_group", "Stop", obs.OBS_GROUP_NORMAL, hotkey_stop_op) local hotkey_stop_shift_key= utils.obs_properties_add_bool(hotkey_stop_op, "hotkey_stop_shift_key", "SHIFT") local hotkey_stop_key_list= utils.obs_properties_add_list(hotkey_stop_op, "hotkey_stop_key_list", "", obs.OBS_COMBO_TYPE_LIST, obs.OBS_COMBO_FORMAT_STRING) obs.obs_property_list_add_string(hotkey_stop_key_list, "SELECT KEY", "") local hotkey_reset_op= obs.obs_properties_create() local hotkey_reset_group= utils.obs_properties_add_group(p, "hotkey_reset_group", "Reset", obs.OBS_GROUP_NORMAL, hotkey_reset_op) local hotkey_reset_shift_key= utils.obs_properties_add_bool(hotkey_reset_op, "hotkey_reset_shift_key", "SHIFT") local hotkey_reset_key_list= utils.obs_properties_add_list(hotkey_reset_op, "hotkey_reset_key_list", "", obs.OBS_COMBO_TYPE_LIST, obs.OBS_COMBO_FORMAT_STRING) obs.obs_property_list_add_string(hotkey_reset_key_list, "SELECT KEY", "") for _, key in pairs(hotkey_key_list) do obs.obs_property_list_add_string(hotkey_start_key_list, string.upper(key), key) obs.obs_property_list_add_string(hotkey_stop_key_list, string.upper(key), key) obs.obs_property_list_add_string(hotkey_reset_key_list, string.upper(key), key) end obs.obs_property_set_visible(hotkey_start_group, false) obs.obs_property_set_visible(hotkey_stop_group, false) obs.obs_property_set_visible(hotkey_reset_group, false) local show_hotkeys_button= utils.obs_properties_add_button(p, "show_hotkeys_button", "SET HOTKEYS ⇶", function(props, p) utils.hide_all(props, src) local mode= src.setting.get_str("mode") if mode == "mode4" then utils.show_hotkeys(props, src) end local go_back= obs.obs_properties_get(props, "go_back") obs.obs_property_set_visible(go_back, true) return true end) local go_back= utils.obs_properties_add_button(p, "go_back", "↩ Back", function(props, p) utils.go_back(props, src) return true end) obs.obs_property_set_visible(go_back, false) obs.obs_property_set_visible(show_hotkeys_button, false) -- hotkey start callback obs.obs_property_set_modified_callback(hotkey_start_shift_key, function(props, p, settings) local _settings= src.setting if src.source_name ~= nil then local obj= utils.get_setting_from_source(_settings, src.source_name) local hotkey_start= obj.get_arr("hotkey_start") if hotkey_start == nil or hotkey_start.data == nil then -- handle error obj.free() return false end obs.obs_hotkey_unregister(src.start) local timer_mode= obj.get_str("mode") if timer_mode == "mode4" then local hotkey= hotkey_start.next() local is_shift = _settings.get_bul("hotkey_start_shift_key") hotkey.bul("shift", is_shift) hotkey.free(); end hotkey_start.free();obj.free() utils.update_hotkey(src) end return true end) obs.obs_property_set_modified_callback(hotkey_start_key_list, function(props, p, settings) local _settings= src.setting if src.source_name ~= nil then local obj= utils.get_setting_from_source(_settings, src.source_name) local hotkey_start= obj.get_arr("hotkey_start") if hotkey_start and hotkey_start.data == nil then -- handle error obj.free() return false end obs.obs_hotkey_unregister(src.start) local timer_mode= _settings.get_str("mode") if timer_mode == "mode4" then local hotkey= hotkey_start.next() local key= _settings.get_str("hotkey_start_key_list") if key == nil or key == "" or key == "" then key="none" end hotkey.str("key", "OBS_KEY_" .. string.upper(key)) hotkey.free(); end hotkey_start.free();obj.free() utils.update_hotkey(src) end return true end) -- hotkey stop callback obs.obs_property_set_modified_callback(hotkey_stop_shift_key, function(props, p, settings) local _settings= src.setting if src.source_name ~= nil then local obj= utils.get_setting_from_source(_settings, src.source_name) local hotkey_stop= obj.get_arr("hotkey_stop") if hotkey_stop == nil or hotkey_stop.data == nil then -- handle error obj.free() return false end obs.obs_hotkey_unregister(src.stop) local timer_mode= _settings.get_str("mode") if timer_mode == "mode4" then local hotkey= hotkey_stop.next() local is_shift = _settings.get_bul("hotkey_stop_shift_key") hotkey.bul("shift", is_shift) hotkey.free(); end hotkey_stop.free();obj.free() utils.update_hotkey(src) end end) obs.obs_property_set_modified_callback(hotkey_stop_key_list, function(props, p, settings) local _settings= src.setting if src.source_name ~= nil then local obj= utils.get_setting_from_source(_settings, src.source_name) local hotkey_stop= obj.get_arr("hotkey_stop") if hotkey_stop and hotkey_stop.data == nil then -- handle error obj.free() return false end local timer_mode= _settings.get_str("mode") if timer_mode == "mode4" then local hotkey= hotkey_stop.next() local key= _settings.get_str("hotkey_stop_key_list") if key == nil or key == "" or key == "" then key="none" end hotkey.str("key", "OBS_KEY_" .. string.upper(key)) hotkey.free(); end hotkey_stop.free();obj.free() utils.update_hotkey(src) end return true end) -- hotkey reset callback obs.obs_property_set_modified_callback(hotkey_reset_shift_key, function(props, p, settings) local _settings= src.setting if src.source_name ~= nil then local obj= utils.get_setting_from_source(_settings, src.source_name) local hotkey_reset= obj.get_arr("hotkey_reset") if hotkey_reset == nil or hotkey_reset.data == nil then -- handle error obj.free() return false end obs.obs_hotkey_unregister(src.reset) local timer_mode= _settings.get_str("mode") if timer_mode == "mode4" then local hotkey= hotkey_reset.next() local is_shift = _settings.get_bul("hotkey_reset_shift_key") hotkey.bul("shift", is_shift) hotkey.free(); end hotkey_reset.free();obj.free() utils.update_hotkey(src) end return true end) obs.obs_property_set_modified_callback(hotkey_reset_key_list, function(props, p, settings) local _settings= src.setting if src.source_name ~= nil then local obj= utils.get_setting_from_source(_settings, src.source_name) local hotkey_reset= obj.get_arr("hotkey_reset") if hotkey_reset and hotkey_reset.data == nil then -- handle error obj.free() return false end obs.obs_hotkey_unregister(src.reset) local timer_mode= _settings.get_str("mode") if timer_mode == "mode4" then local hotkey= hotkey_reset.next() local key= _settings.get_str("hotkey_reset_key_list") if key == nil or key == "" or key == "" then key="none" end hotkey.str("key", "OBS_KEY_" .. string.upper(key)) hotkey.free(); end hotkey_reset.free();obj.free() utils.update_hotkey(src) end return true end) -- local mode_time= utils.obs_properties_add_list(p, "mode_time", "", obs.OBS_COMBO_TYPE_LIST, obs.OBS_COMBO_FORMAT_STRING) obs.obs_property_list_add_string(mode_time, "Time will count upwards", "mode1") obs.obs_property_list_add_string(mode_time, "Time will count downwards", "mode2") local timer_mode2= utils.obs_properties_add_text(p, "timer_mode2", "Timer:", obs.OBS_TEXT_DEFAULT) obs.obs_property_set_visible(timer_mode2, false) obs.obs_property_set_long_description(timer_mode2,"
format is Hh:Mm:Ss stands for (hours, minutes, seconds)
") obs.obs_property_set_modified_callback(mode_time, function(props, prop, settings) local _settings= PairStack(settings, nil, true) local mode = _settings.get_str("mode_time") local timer_mode2= obs.obs_properties_get(props, "timer_mode2") if mode == nil or mode == "" or mode ~= "mode2" then _settings.str("timer_mode2","") obs.obs_property_set_visible(timer_mode2, false) -- if not src.isActive then src.raw_now= "";src.now=0 end else obs.obs_property_set_visible(timer_mode2, true) end utils.update_source_time(src) return true; end) -- [[ ACTION EVENT GROUP ]] UNDER - DEVELOPMENT COMMING SOON ... -- local actionOp= obs.obs_properties_create() -- local action_group = utils.obs_properties_add_group(p, "action_setup_group","Action Event", obs.OBS_GROUP_CHECKABLE , actionOp) -- local action_group_enable_label= utils.obs_properties_add_text(actionOp, "action_group_enable_label", "(is disabled)", obs.OBS_TEXT_INFO) -- obs.obs_property_set_visible(action_group_enable_label, false) -- local action_screen; -- local action_source= utils.obs_properties_add_list(actionOp, "action_source", "Source:",obs.OBS_COMBO_TYPE_LIST, obs.OBS_COMBO_FORMAT_STRING) -- obs.obs_property_set_visible(action_source, false) -- obs.obs_property_list_add_string(action_source, "(SELECT SOURCE)", "def") -- local action_event= utils.obs_properties_add_list(actionOp, "action_event", "",obs.OBS_COMBO_TYPE_LIST, obs.OBS_COMBO_FORMAT_STRING) -- obs.obs_property_set_visible(action_event, false) -- local action_event_timing= utils.obs_properties_add_list(actionOp, "action_event_timing", "",obs.OBS_COMBO_TYPE_LIST, obs.OBS_COMBO_FORMAT_STRING) -- obs.obs_property_set_visible(action_event_timing, false) -- obs.obs_property_list_add_string(action_event_timing, "(SELECT EVENT TIMING)", "") -- -- seconds, minutes, hours -- local ix=1 -- for i=5, 60, 5 do -- obs.obs_property_list_add_string(action_event_timing, "every " .. i .. " seconds", i.."s") -- obs.obs_property_list_add_string(action_event_timing, "every " .. i .. " minutes", i.."m") -- obs.obs_property_list_add_string(action_event_timing, "every " .. ix .. " hours", ix .."hr") -- ix =ix+1 -- end -- local action_execute= utils.obs_properties_add_button(actionOp, "action_execute", "Activate",function() end) -- obs.obs_property_set_visible(action_execute, false) -- obs.obs_property_set_modified_callback(action_event_timing, function(props, prop, settings) -- local _settings= PairStack(settings, nil, true) -- local event_timing = _settings.get_str("action_event_timing") -- if event_timing == nil or event_timing == "" or event_timing == "" then -- obs.obs_property_set_visible(action_execute, false) -- else -- obs.obs_property_set_visible(action_execute, true) -- end -- return true -- end) -- [[ MODE EVENT CALLBACK ]] obs.obs_property_set_modified_callback(mode, function(props, prop, settings) local _settings= PairStack(settings, nil, true) local mode = _settings.get_str("mode") local show_hotkeys_button= obs.obs_properties_get(props, "show_hotkeys_button") local previous_mode= src.setting.get_str("source_filter_time_mode") if mode ~= "mode4" then obs.obs_property_set_visible(show_hotkeys_button, false) else obs.obs_property_set_visible(show_hotkeys_button, true) end if previous_mode ~= mode then src.reset(true);src.stop(true); end src.setting.str("source_filter_time_mode", mode) utils.quick_update(src) return true; end) -- ACTION SOURCE CALLBACK -- obs.obs_property_set_modified_callback(action_source, function(props, prop, settings) -- local _settings= PairStack(settings, nil, true) -- local source_name = _settings.get_str("action_source") -- _settings.str("action_event", "") -- _settings.str("action_event_timing", "") -- obs.obs_property_set_visible(action_event_timing, false) -- obs.obs_property_set_visible(action_event, false) -- obs.obs_property_set_visible(action_execute, false) -- --_settings.str("action_source", "", true) -- local sceneitem= utils.get_sceneitem(source_name) -- obs.obs_property_list_clear(action_event) -- if not (source_name == nil or source_name == "" or source_name == "") then -- obs.obs_property_set_visible(action_event, true) -- obs.obs_property_list_add_string(action_event, "(SELECT EVENT)", "") -- if sceneitem and sceneitem.item ~= nil then -- local source_id= obs.obs_source_get_unversioned_id(sceneitem.source) -- if source_id== "ffmpeg_source" then -- for _, event in pairs(event_video_list) do -- obs.obs_property_list_add_string(action_event, event.name, event.id) -- end -- end -- sceneitem.release() -- end -- for _, event in pairs(event_list) do -- obs.obs_property_list_add_string(action_event, event.name, event.id) -- end -- end -- return true -- end) -- ACTION EVENT CALLBACK -- obs.obs_property_set_modified_callback(action_event, function(props, prop, settings) -- local _settings= PairStack(settings, nil, true) -- local event_name = _settings.get_str("action_event") -- if event_name == nil or event_name == "" or event_name == "" then -- _settings.str("action_event_timing", "") -- obs.obs_property_set_visible(action_event_timing, false) -- obs.obs_property_set_visible(action_execute, false) -- else -- obs.obs_property_set_visible(action_event_timing, true) -- end -- return true -- end) -- -- ACTION GROUP ACTIVE CALLBACK -- obs.obs_property_set_modified_callback(action_group, function(props, prop, settings) -- local _settings= PairStack(settings, nil, true) -- _settings.str("action_event", "") -- _settings.str("action_event_timing", "") -- _settings.str("action_source", "") -- --local action_group_enable_label= obs.obs_properties_get(actionOp, "action_group_enable_label") -- local active= false; -- if _settings.get_bul("action_setup_group") then -- active=true -- end -- obs.obs_property_set_visible(action_group_enable_label, not active) -- obs.obs_property_set_visible(action_source, active) -- obs.obs_property_list_clear(action_source) -- obs.obs_property_list_clear(action_event) -- obs.obs_property_set_visible(action_event, false) -- obs.obs_property_set_visible(action_event_timing, false) -- obs.obs_property_set_visible(action_execute, false) -- -- all the current source in the scene -- local sources_names= utils.get_all_source_names_from_scene() -- obs.obs_property_list_add_string(action_source, "(SELECT SOURCE)", "") -- for _, source in pairs(sources_names) do -- obs.obs_property_list_add_string(action_source, source, source) -- end -- return true; -- end) -- TIMER MODE2 CALLBACK obs.obs_property_set_modified_callback(timer_mode2, function(props, prop, settings) local _settings= PairStack(settings, nil, true) local tm_mode= _settings.get_str("mode_time") local tm_mode2= _settings.get_str("timer_mode2") local hours, minutes, seconds = string.match(tm_mode2, "(%d+):(%d+):(%d+)") if hours == nil or minutes == nil or seconds == nil then return end if tm_mode == "mode2" and src.raw_now ~= tm_mode2 then src.raw_now= tm_mode2 src.now = (tonumber(hours) * 3600) + (tonumber(minutes) * 60) + tonumber(seconds) src.setting.int("source_filter_time_value", src.now) src.setting.str("source_filter_time_raw_value", tm_mode2) utils.update_source_time(src) end return; end) return p end filter.get_defaults= function(settings) local _settings= PairStack(settings, nil, true) -- _settings.str("source-name", "none", true) _settings.str("mode", "mode1", true) _settings.str("mode_time", "mode1", true) _settings.bul("action_setup_group", false, true) _settings.str("action_source", "", true) _settings.str("action_event", "", true) local source_name= _settings.get_str("source_name") if source_name ~= "" and source_name ~= nil then -- utils.get_setting_from_source(_settings, source_name, true).free() end end filter.update= function(src, settings) utils.init_filter_source_size(src) end filter.create= function(setting, source) local _setting= PairStack(setting,nil, true) local source_name= _setting.get_str("source_name"); local time_mode= _setting.get_str("source_filter_time_mode") local time_value= _setting.get_int("source_filter_time_value") local time_raw_value= _setting.get_str("source_filter_time_raw_value") if time_value == nil or time_mode ~= "mode4" then time_value= 0; time_raw_value= "" end local src= {now= time_value;isActive=false;source_name=source_name; raw_now=time_raw_value;fx=nil;is_dead=false}; src.source= source; utils.init_filter_source_size(src) obs.obs_enter_graphics() src.fx= obs.gs_effect_create(shader, nil, nil) obs.obs_leave_graphics() if src.fx ~= mil then src.params= {width = obs.gs_effect_get_param_by_name(src.fx, "width"); height = obs.gs_effect_get_param_by_name(src.fx, "height")} else filter.destory(src) return nil end src.setting= PairStack(setting, nil, true) local function clock() if src.is_dead then obs.timer_remove(clock) obs.obs_hotkey_unregister(src.start) obs.obs_hotkey_unregister(src.stop) obs.obs_hotkey_unregister(src.reset) obs.obs_frontend_remove_event_callback(src.front_end_callback) return end -- get the filter's source target local tm_mode=src.setting.get_str("mode_time") if tm_mode == "mode2" then if src.now > 0 then src.now= src.now-1 else -- handle case if src.now is less than 0 -- obs.timer_remove(clock); src.isActive= false return utils.update_source_time(src) end else src.now= src.now+1 end return utils.update_source_time(src) end function src.start(isPressed) if not isPressed or src.isActive or src.is_dead then return end src.isActive= true obs.timer_remove(clock) obs.timer_add(clock, 1000) end function src.stop(isPressed) if not isPressed then return end obs.timer_remove(clock) src.isActive= false end function src.reset(isPressed) if not isPressed or src.is_dead then return end local tm_mode=src.setting.get_str("mode_time") local tm_mode2 = src.setting.get_str("timer_mode2") if tm_mode == "mode2" then local hours, minutes, seconds = string.match(tm_mode2, "(%d+):(%d+):(%d+)") if hours == nil or minutes == nil or seconds == nil then -- error else src.now = (tonumber(hours) * 3600) + (tonumber(minutes) * 60) + tonumber(seconds) end else src.now=0 end src.stop(true) utils.update_source_time(src) return src.start(true) end function src.front_end_callback(id) if src.is_dead then return end local mode= src.setting.get_str("mode") if mode == "mode1" then if id == obs.OBS_FRONTEND_EVENT_STREAMING_STARTING or id == obs.OBS_FRONTEND_EVENT_RECORDING_STARTING then src.reset(true) elseif id == obs.OBS_FRONTEND_EVENT_RECORDING_STOPPED or id == obs.OBS_FRONTEND_EVENT_STREAMING_STOPPING then src.stop(true) end elseif mode == "mode2" then if id == obs.OBS_FRONTEND_EVENT_STREAMING_STARTING then src.reset(true) elseif id == obs.OBS_FRONTEND_EVENT_STREAMING_STOPPING then src.stop(true) end elseif mode == "mode3" then if id == obs.OBS_FRONTEND_EVENT_RECORDING_STARTING then src.reset(true) elseif id == obs.OBS_FRONTEND_EVENT_RECORDING_STOPPED then src.stop(true) end end end obs.obs_frontend_add_event_callback(src.front_end_callback) utils.quick_update(src) utils.update_hotkey(src) return src end filter.video_tick= function(src, fps) utils.init_filter_source_size(src) end filter.destroy= function(src, d1,d2, d3) src.is_dead=true obs.remove_current_callback() if src.fx ~= nil then obs.obs_enter_graphics() obs.gs_effect_destroy(src.fx) obs.obs_leave_graphics() end -- obs.remove_current_callback() -- obs.obs_hotkey_unregister(src.start) -- obs.obs_hotkey_unregister(src.stop) -- obs.obs_hotkey_unregister(src.reset) --obs.obs_frontend_remove_event_callback(src.front_end_callback) src= nil end filter.video_render= function(src) if not src then return end local width= src.width;local height= src.height if not obs.obs_source_process_filter_begin(src.source, obs.GS_RGBA, obs.OBS_NO_DIRECT_RENDERING) then obs.obs_source_skip_video_filter(src.source) return end if src.params == nil then obs.obs_source_process_filter_end(src.source, src.fx, width, height) return end if src.source_name == nil or src.source_name == "" then src.source_name= obs.obs_source_get_name(obs.obs_filter_get_target(src.source)) src.setting.str("source_name", src.source_name) utils.get_setting_from_source(src.setting, src.source_name).free() utils.update_hotkey(src) end obs.gs_effect_set_int(src.params.width, width) obs.gs_effect_set_int(src.params.height, height) obs.gs_blend_state_push() obs.gs_blend_function(obs.GS_BLEND_ONE, obs.GS_BLEND_INVSRCALPHA) obs.obs_source_process_filter_end(src.source, src.fx, width, height) obs.gs_blend_state_pop() end __LIST_SCENE_ITEMS__={} -- other utils functionalities utils.quick_update = function(src) local time_mode= src.setting.get_str("source_filter_time_mode") if time_mode ~= "mode4" and not src.isActive then local is_recording = obs.obs_frontend_recording_active() local is_streaming = obs.obs_frontend_streaming_active() if is_recording then src.front_end_callback(obs.OBS_FRONTEND_EVENT_RECORDING_STARTING) end if is_streaming then src.front_end_callback(obs.OBS_FRONTEND_EVENT_STREAMING_STARTING) end end end utils.get_setting_from_source= function(setting, name, reset) local obj= setting.get_obj(script_path() .. "_" .. name .. "_setting") if not obj or obj.data == nil or reset then if obj then obj.free() end obj= PairStack() local hotkey_start=PairStack();hotkey_start.bul("shift", false);hotkey_start.str("key", "OBS_KEY_NONE") local hotkey_start_arr=ArrayStack();hotkey_start_arr.insert(hotkey_start.data) local hotkey_stop=PairStack();hotkey_stop.bul("shift", false);hotkey_stop.str("key", "OBS_KEY_NONE") local hotkey_stop_arr=ArrayStack();hotkey_stop_arr.insert(hotkey_stop.data) local hotkey_reset=PairStack();hotkey_reset.bul("shift", false);hotkey_reset.str("key","OBS_KEY_NONE") local hotkey_reset_arr=ArrayStack();hotkey_reset_arr.insert(hotkey_reset.data) obj.arr("hotkey_start",hotkey_start_arr.data) obj.arr("hotkey_stop",hotkey_stop_arr.data) obj.arr("hotkey_reset",hotkey_reset_arr.data) hotkey_start.free();hotkey_stop.free();hotkey_reset.free() hotkey_start_arr.free();hotkey_stop_arr.free();hotkey_reset_arr.free() setting.obj(script_path() .. "_" .. name .. "_setting", obj.data) end return obj end utils.obs_properties_add_list= function(p, name, desc, type, format) table.insert(utils.properties, name) local property= obs.obs_properties_add_list(p, name, desc, type, format) return property end utils.obs_properties_add_button= function(p, name, desc, callback) table.insert(utils.properties, name) local property= obs.obs_properties_add_button(p, name, desc, callback) return property end utils.obs_properties_add_text= function(p, name, desc, type) table.insert(utils.properties, name) local property= obs.obs_properties_add_text(p, name, desc, type) return property end utils.obs_properties_add_group= function(p, name, desc, type, op) table.insert(utils.properties, name) local property= obs.obs_properties_add_group(p, name, desc, type, op) return property end utils.obs_properties_add_bool= function(p, name, desc) table.insert(utils.properties, name) local property= obs.obs_properties_add_bool(p, name, desc) return property end utils.hide_all= function(p, src) -- hide all the properties for _, property_name in pairs(utils.properties) do local property= obs.obs_properties_get(p, property_name) local is_visible= obs.obs_property_visible(property) if is_visible then table.insert(utils.history, property_name) end if property then obs.obs_property_set_visible(property, false) end end end utils.go_back= function(p, src) if #utils.history <= 0 then return end for _, property_name in pairs(utils.properties) do -- check property_name is in utils.history then show it else hide it local property= obs.obs_properties_get(p, property_name) if property then local is_visible= false for _, prop in pairs(utils.history) do if prop == property_name then is_visible= true break end end obs.obs_property_set_visible(property, is_visible) end end utils.history= {} end utils.show_hotkeys= function(p, src) local hotkey_start_group= obs.obs_properties_get(p, "hotkey_start_group") local hotkey_stop_group= obs.obs_properties_get(p, "hotkey_stop_group") local hotkey_reset_group= obs.obs_properties_get(p, "hotkey_reset_group") local mode= src.setting.get_str("mode") if mode == "mode4" then obs.obs_property_set_visible(hotkey_start_group, true) obs.obs_property_set_visible(hotkey_stop_group, true) obs.obs_property_set_visible(hotkey_reset_group, true) local hotkey_start_key_list= obs.obs_properties_get(p, "hotkey_start_key_list") local hotkey_stop_key_list= obs.obs_properties_get(p, "hotkey_stop_key_list") local hotkey_reset_key_list= obs.obs_properties_get(p, "hotkey_reset_key_list") obs.obs_property_set_visible(hotkey_start_key_list, true) obs.obs_property_set_visible(hotkey_stop_key_list, true) obs.obs_property_set_visible(hotkey_reset_key_list, true) local hotkey_start_shift_key= obs.obs_properties_get(p, "hotkey_start_shift_key") local hotkey_stop_shift_key= obs.obs_properties_get(p, "hotkey_stop_shift_key") local hotkey_reset_shift_key= obs.obs_properties_get(p, "hotkey_reset_shift_key") obs.obs_property_set_visible(hotkey_start_shift_key, true) obs.obs_property_set_visible(hotkey_stop_shift_key, true) obs.obs_property_set_visible(hotkey_reset_shift_key, true) end end utils.update_hotkey= function(src) local source_name= src.source_name local obj= utils.get_setting_from_source(src.setting, source_name) local hotkey_start_arr= obj.get_arr("hotkey_start") local hotkey_stop_arr= obj.get_arr("hotkey_stop") local hotkey_reset_arr = obj.get_arr("hotkey_reset") obs.obs_hotkey_unregister(src.start) obs.obs_hotkey_unregister(src.stop) obs.obs_hotkey_unregister(src.reset) if hotkey_start_arr.data == nil or hotkey_stop_arr.data == nil or hotkey_reset_arr.data == nil then -- handle error obj.free() return end -- make sure hotkeys are valid before registering them local hotkey_start= hotkey_start_arr.next() local hotkey_stop= hotkey_stop_arr.next() local hotkey_reset= hotkey_reset_arr.next() if hotkey_start.data == nil or hotkey_stop.data == nil or hotkey_reset.data == nil then -- handle error hotkey_start_arr.free();hotkey_stop_arr.free();hotkey_reset_arr.free() obj.free() return end local start_key= hotkey_start.get_str("key") local stop_key= hotkey_stop.get_str("key") local reset_key= hotkey_reset.get_str("key") local start_shift= hotkey_start.get_bul("shift") local stop_shift= hotkey_stop.get_bul("shift") local reset_shift= hotkey_reset.get_bul("shift") if (start_key ~= nil and start_key ~= "OBS_KEY_NONE") or start_shift then local start_id = obs.obs_hotkey_register_frontend(script_path(),"Start (upstreamX) for " .. tostring(source_name), src.start); obs.obs_hotkey_load(start_id, hotkey_start_arr.data) end if (stop_key ~= nil and stop_key ~= "OBS_KEY_NONE") or stop_shift then local stop_id = obs.obs_hotkey_register_frontend(script_path(),"Stop (upstreamX) for " .. tostring(source_name) , src.stop); obs.obs_hotkey_load(stop_id, hotkey_stop_arr.data) end if (reset_key ~= nil and reset_key ~= "OBS_KEY_NONE") or reset_shift then local reset_id = obs.obs_hotkey_register_frontend(script_path(),"Reset (upstreamX) for " .. tostring(source_name), src.reset); obs.obs_hotkey_load(reset_id, hotkey_reset_arr.data) end hotkey_start.free();hotkey_stop.free();hotkey_reset.free() hotkey_start_arr.free();hotkey_stop_arr.free();hotkey_reset_arr.free() obj.free() end utils.update_source_time = function(src) local source = get_filter_source(src) if not source then return end local source_id= obs.obs_source_get_unversioned_id(source) if source_id == "text_gdiplus" then local text= obs.obs_source_get_settings(source) local text_settings= PairStack(text, nil, true) local text_source= text_settings.get_str("text") local hours= math.floor(src.now/3600) local minutes= math.floor((src.now%3600)/60) local seconds= math.floor(src.now%60) local time= string.format("%02d:%02d:%02d", hours, minutes, seconds) text_settings.str("text", time) obs.obs_source_update(source, text) text_settings.free() end end utils.init_filter_source_size= function(src) local src_target= obs.obs_filter_get_target(src.source) if src_target then src.width= obs.obs_source_get_base_width(src_target) src.height= obs.obs_source_get_base_height(src_target) else src.height=0;src.width=0 end end utils.get_sceneitem= function(item_name) local sourceObject = obs.obs_get_source_by_name(item_name) local current_source_scene = obs.obs_frontend_get_current_scene() local current_scene = obs.obs_scene_from_source(current_source_scene) if current_scene ~= nil and sourceObject ~= nil then local scene_item = obs.obs_scene_sceneitem_from_source(current_scene, 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(current_scene, 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 if current_source_scene ~= nil then obs.obs_source_release(current_source_scene) end if sourceObject ~= nil then obs.obs_source_release(sourceObject) end local item_obj = { index = (#__LIST_SCENE_ITEMS__) + 1; item = scene_item; source = obs.obs_sceneitem_get_source(scene_item) } item_obj["release"] = function() if item_obj.item ~= nil then obs.obs_sceneitem_release(item_obj.item) item_obj.item = 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 if current_source_scene ~= nil then obs.obs_source_release(current_source_scene) end if sourceObject ~= nil then obs.obs_source_release(sourceObject) end return nil end utils.get_all_source_names_from_scene = function(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 -- schedule an event 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(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 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;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 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) end); size = error_wrapper_handler(function() if self.data == nil then return 0 end return obs.obs_data_array_count(self.data); 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 (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); del= error_wrapper_handler(function(name) obs.obs_data_erase(self.data, name) return true 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 --[[ MAIN SOURCE INIT STARTUP FUNCTIONS]] function script_load(settings) obs.obs_register_source(filter) end function WelcomeHome() return [[

UpstreamX - 2.0

Select a source and add a filter called 'UpstreamX'.

Useful Links

Video Tutorial: click here

Dev

Author: @iixisii

updates follow XDeviixisiiX

]] end function script_properties() local p = obs.obs_properties_create() utils.obs_properties_add_text(p, "label", WelcomeHome(), obs.OBS_TEXT_INFO) return p; end shader = [[ uniform float4x4 ViewProj; uniform texture2d image; uniform int width; uniform int height; sampler_state textureSampler { Filter = Linear; AddressU = Border; AddressV = Border; BorderColor = 00000000; }; struct VertData { float4 pos : POSITION; float2 uv : TEXCOORD0; }; float4 ps_get(VertData v_in) : TARGET { return image.Sample(textureSampler, v_in.uv.xy); } VertData VSDefault(VertData v_in) { VertData vert_out; vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); vert_out.uv = v_in.uv; return vert_out; } technique Draw { pass { vertex_shader = VSDefault(v_in); pixel_shader = ps_get(v_in); } } ]]