-- OBS RANDOM PLAYOUT obs = obslua tv_scene_name = "" sch_scene_name = "" zen_scene_name = "" zen_source = "" tv_source = "" music_source = "" text1_source = "" text2_source = "" text3_source = "" sch_time = 0 prevent_repeats = false auto_start = false max_shows = 0 max_zen = 0 max_songs = 0 scan_scene = 0 tv_list = "/SHOW/media.txt" music_list = "/SHOW/music.txt" zen_list = "/SHOW/zen.txt" obsurl = "random-playout.1877" icon ="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAFFGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIgogICAgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgeG1wOkNyZWF0ZURhdGU9IjIwMjQtMDEtMjVUMTY6NDE6NDItMDUwMCIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjQtMDEtMjVUMTY6NDU6NTktMDU6MDAiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMjQtMDEtMjVUMTY6NDU6NTktMDU6MDAiCiAgIHBob3Rvc2hvcDpEYXRlQ3JlYXRlZD0iMjAyNC0wMS0yNVQxNjo0MTo0Mi0wNTAwIgogICBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIgogICBwaG90b3Nob3A6SUNDUHJvZmlsZT0ic1JHQiBJRUM2MTk2Ni0yLjEiCiAgIGV4aWY6UGl4ZWxYRGltZW5zaW9uPSI1MCIKICAgZXhpZjpQaXhlbFlEaW1l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" desc = [[

OBSPlayout (v %s)

Setup instructions at OBS Forum Thread.

]] path_now = "" path_then = "" path_later = "" title_now = "" title_then = "" title_later = "" zen_path = "" song_path = "" sversion = "1.0" ---------------------------------------------------------- -- main functions function onFrontendEvent(event) if event == obs.OBS_FRONTEND_EVENT_SCENE_CHANGED then local sceneSrc = obs.obs_frontend_get_current_scene() local scene = obs.obs_source_get_name(sceneSrc) if scene == sch_scene_name then scan_scene = "" set_media(tv_source,path_now) set_media(music_source,song_path) set_text(title_now,text1_source) set_text(title_then,text2_source) set_text(title_later,text3_source) obs.timer_add(schedule_hold_tf,sch_time * 1000) obs.timer_add(text1show_tf,1000) obs.timer_add(text2show_tf,2000) obs.timer_add(text3show_tf,3000) print(path_now) get_next_shows() else obs.timer_remove(schedule_hold_tf) obs.timer_remove(text1show_tf) obs.timer_remove(text2show_tf) obs.timer_remove(text3show_tf) end end obs.obs_source_release(sceneSrc) end function get_next_shows() zen_path = getshow(math.random(1,max_zen), zen_list)[1] song_path = getshow(math.random(1,max_songs), music_list)[1] local ar = {} local count = 0 repeat ar = getshow(math.random(1,max_shows), tv_list) count = count + 1 until (ar[2] ~= title_later and ar[2] ~= title_then ) or prevent_repeats == false or count == 15 path_now = path_then title_now = title_then path_then = path_later title_then = title_later path_later = ar[1] title_later = ar[2] end function getshow(number,filename) if not file_exists(filename) then return end local count = 0 local ar = {} for line in io.lines(filename) do count = count + 1 if count == number then ar = mysplit(line, ";") break end end ar[1] = trim(ar[1]) ar[2] = trim(ar[2]) return ar end function reload_list() max_shows = file_line_count(tv_list) max_zen = file_line_count(zen_list) max_songs = file_line_count(music_list) end ---------------------------------------------------------- -- timer events function schedule_hold_tf() obs.timer_remove(schedule_hold_tf) safe_change_scene(zen_scene_name) set_media(zen_source,zen_path) obs.timer_add(start_Zen_End_Check,1000) end function text1show_tf() obs.timer_remove(text1show_tf) show_currentSceneItem(text1_source) end function text2show_tf() obs.timer_remove(text2show_tf) show_currentSceneItem(text2_source) end function text3show_tf() obs.timer_remove(text3show_tf) show_currentSceneItem(text3_source) end function start_Zen_End_Check() obs.timer_remove(start_Zen_End_Check) hide_schedule_items() scan_scene = "ZEN" end function start_TV_End_Check() obs.timer_remove(start_TV_End_Check) scan_scene = "TV" end function start_Stream() obs.timer_remove(start_Stream) obs.obs_frontend_streaming_start() end ------------------------------------------------------------- -- scene initial state functions function hide_schedule_items() local source = obs.obs_get_source_by_name(sch_scene_name) local scene = obs.obs_scene_from_source(source) local item = obs.obs_scene_find_source(scene, text1_source) obs.obs_sceneitem_set_visible(item, false) local item = obs.obs_scene_find_source(scene, text2_source) obs.obs_sceneitem_set_visible(item, false) local item = obs.obs_scene_find_source(scene, text3_source) obs.obs_sceneitem_set_visible(item, false) obs.obs_source_release(source) end --------------------------------------------------------------- -- helper funtions function safe_change_scene(name) -- work around for OBS bug causing deadlock when swaping scenes when you have a scene change callback. local scene_source = obs.obs_get_source_by_name(name) if scene_source ~= nil then obs.obs_frontend_remove_event_callback(onFrontendEvent) obs.obs_frontend_set_current_scene(scene_source) obs.obs_frontend_add_event_callback(onFrontendEvent) obs.obs_source_release(scene_source) end end function mysplit (inputstr, sep) if sep == nil then sep = "%s" end local t={} for str in string.gmatch(inputstr, "([^"..sep.."]+)") do table.insert(t, str) end return t end function file_line_count(file) local ctr = 0 for _ in io.lines(file) do ctr = ctr + 1 end return ctr end function file_exists(file) local f = io.open(file, "rb") if f then f:close() end return f ~= nil end function show_currentSceneItem(item_name) local source = obs.obs_frontend_get_current_scene() local scene = obs.obs_scene_from_source(source) local item = obs.obs_scene_find_source(scene, item_name) obs.obs_sceneitem_set_visible(item, true) obs.obs_source_release(source) end function trim(s) -- trim junk off path strings if s ~= nil then return (string.gsub(s, "^%s*(.-)%s*$", "%1")) else return "" end end function set_text(text,textSourceName) local source = obs.obs_get_source_by_name(textSourceName) if source ~= nil then local settings = obs.obs_data_create() obs.obs_data_set_string(settings, "text", text) obs.obs_source_update(source, settings) obs.obs_data_release(settings) obs.obs_source_release(source) end end function set_media(source_name,media_file) local source = obs.obs_get_source_by_name(source_name) if source ~= nil then local settings = obs.obs_data_create() obs.obs_data_set_string(settings, "local_file", media_file) obs.obs_source_update(source, settings) obs.obs_data_release(settings) end obs.obs_source_release(source) end ---------------------------------------------------------- -- OBS Core Functions function script_properties() local props = obs.obs_properties_create() obs.obs_properties_add_text(props, "tv_list", "TV List ", obs.OBS_TEXT_DEFAULT) obs.obs_properties_add_text(props, "music_list", "Music List ", obs.OBS_TEXT_DEFAULT) obs.obs_properties_add_text(props, "zen_list", "Zen List ", obs.OBS_TEXT_DEFAULT) obs.obs_properties_add_text(props, "tv_scene", "TV Scene ", obs.OBS_TEXT_DEFAULT) obs.obs_properties_add_text(props, "sch_scene", "Schedule Scene ", obs.OBS_TEXT_DEFAULT) obs.obs_properties_add_text(props, "zen_scene", "Zen Scene ", obs.OBS_TEXT_DEFAULT) local p = obs.obs_properties_add_list(props, "tv_source", "TV Source ", obs.OBS_COMBO_TYPE_EDITABLE, obs.OBS_COMBO_FORMAT_STRING) local q = obs.obs_properties_add_list(props, "zen_source", "Zen Source ", obs.OBS_COMBO_TYPE_EDITABLE, obs.OBS_COMBO_FORMAT_STRING) local r = obs.obs_properties_add_list(props, "music_source", "Music Source ", obs.OBS_COMBO_TYPE_EDITABLE, obs.OBS_COMBO_FORMAT_STRING) local s = obs.obs_properties_add_list(props, "text1_source", "Up Next 1 ", obs.OBS_COMBO_TYPE_EDITABLE, obs.OBS_COMBO_FORMAT_STRING) local t = obs.obs_properties_add_list(props, "text2_source", "Up Next 2 ", obs.OBS_COMBO_TYPE_EDITABLE, obs.OBS_COMBO_FORMAT_STRING) local u = obs.obs_properties_add_list(props, "text3_source", "Up Next 3 ", obs.OBS_COMBO_TYPE_EDITABLE, obs.OBS_COMBO_FORMAT_STRING) obs.obs_properties_add_int( props, "sch_t", "Schedule time (s) ", 0, 60, 1) obs.obs_properties_add_bool(props, "prevent_repeats", "Prevent Repeats") obs.obs_properties_add_bool(props, "auto_start", "Auto Start Stream") obs.obs_properties_add_button(props, "button1", "Re-count lists",reload_list) local sources = obs.obs_enum_sources() if sources ~= nil then for _, source in ipairs(sources) do local name = obs.obs_source_get_name(source) obs.obs_property_list_add_string(p, name, name) obs.obs_property_list_add_string(q, name, name) obs.obs_property_list_add_string(r, name, name) obs.obs_property_list_add_string(s, name, name) obs.obs_property_list_add_string(t, name, name) obs.obs_property_list_add_string(u, name, name) end end obs.source_list_release(sources) return props end function script_description() return string.format(desc, tostring(sversion)) end function script_update(settings) tv_list = obs.obs_data_get_string(settings, "tv_list") music_list = obs.obs_data_get_string(settings, "music_list") zen_list = obs.obs_data_get_string(settings, "zen_list") tv_scene_name = obs.obs_data_get_string(settings, "tv_scene") sch_scene_name = obs.obs_data_get_string(settings, "sch_scene") zen_scene_name = obs.obs_data_get_string(settings, "zen_scene") tv_source = obs.obs_data_get_string(settings, "tv_source") zen_source = obs.obs_data_get_string(settings, "zen_source") music_source = obs.obs_data_get_string(settings, "music_source") text1_source = obs.obs_data_get_string(settings, "text1_source") text2_source = obs.obs_data_get_string(settings, "text2_source") text3_source = obs.obs_data_get_string(settings, "text3_source") sch_time = obs.obs_data_get_int(settings, "sch_t") prevent_repeats = obs.obs_data_get_bool(settings,"prevent_repeats") auto_start = obs.obs_data_get_bool(settings,"prevent_repeats") end function script_load(settings) script_update(settings) obs.obs_frontend_add_event_callback(onFrontendEvent) onFrontendEvent(obs.OBS_FRONTEND_EVENT_SCENE_CHANGED) reload_list() math.randomseed(os.time()) math.random() math.random() math.random() get_next_shows() get_next_shows() get_next_shows() if auto_start then obs.timer_add(start_Stream,1000) safe_change_scene(sch_scene_name) end end function script_tick(seconds) if scan_scene == "TV" then local source = obs.obs_get_source_by_name( tv_source ) if source ~= nil then if obs.obs_source_active( source ) then local state = obs.obs_source_media_get_state(source) if state == obs.OBS_MEDIA_STATE_STOPPED or state == obs.OBS_MEDIA_STATE_ENDED then scan_scene = "" safe_change_scene(sch_scene_name) end end end obs.obs_source_release(source) end if scan_scene == "ZEN" then local source = obs.obs_get_source_by_name( zen_source ) if source ~= nil then if obs.obs_source_active( source ) then local state = obs.obs_source_media_get_state(source) if state == obs.OBS_MEDIA_STATE_STOPPED or state == obs.OBS_MEDIA_STATE_ENDED then scan_scene = "" obs.timer_add(start_TV_End_Check,1000) safe_change_scene(tv_scene_name) end end end obs.obs_source_release(source) end end