obs = obslua -- User-defined settings with default values settings = { text_source_name = "", prefix = "Next Stream: ", separator = " • ", even_week_time = "08:00 AM", odd_week_time = "09:00 PM", suffix = "o'clock", days = { {enabled = true, auto_time = true, custom_time = ""}, -- Monday, default enabled {enabled = false, auto_time = true, custom_time = ""}, -- Tuesday {enabled = false, auto_time = true, custom_time = ""}, -- Wednesday {enabled = false, auto_time = true, custom_time = ""}, -- Thursday {enabled = false, auto_time = true, custom_time = ""}, -- Friday {enabled = false, auto_time = true, custom_time = ""}, -- Saturday {enabled = false, auto_time = true, custom_time = ""} -- Sunday }, day_format = "full" -- Options: "full" or "short" } -- Helper function to get the current week number function get_week_number(day_offset) day_offset = day_offset or 0 local current_time = os.time() + (day_offset * 24 * 60 * 60) local year_start = os.time{year=os.date("%Y", current_time), month=1, day=1} local day_of_year = os.date("%j", current_time) local week_num = math.ceil(day_of_year / 7) return week_num end -- Helper function to check if a given week number is even function is_even_week(week_num) return week_num % 2 == 0 end -- Helper function to format time as HH:MM AM/PM function format_time(time_str) local hours, minutes, period = time_str:match("^(%d+):?(%d*)%s*([APMapm]*)$") hours = tonumber(hours) or 0 minutes = tonumber(minutes) or 0 period = period and period:upper() or "AM" -- Default to AM if period is nil return string.format("%02d:%02d %s", hours, minutes, period) end -- Function to determine the next stream day and time function get_next_stream() local days_of_week = settings.day_format == "full" and {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"} or {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"} local current_time = os.time() local current_day = (os.date("*t").wday + 5) % 7 + 1 -- Adjusting to make Monday=1, ..., Sunday=7 for i = 0, 6 do local index = (current_day + i - 1) % 7 + 1 local day_setting = settings.days[index] if day_setting.enabled then local day_offset = i local week_num = get_week_number(day_offset) local is_even = is_even_week(week_num) local time if day_setting.auto_time then time = is_even and settings.even_week_time or settings.odd_week_time else time = day_setting.custom_time end local hour, minute, period = time:match("(%d+):(%d+)%s*([APMapm]+)") hour = tonumber(hour) minute = tonumber(minute) period = period and period:upper() or "AM" -- Ensure period is not nil if period == "PM" and hour < 12 then hour = hour + 12 elseif period == "AM" and hour == 12 then hour = 0 end local stream_time = os.time{year=os.date("%Y", current_time), month=os.date("%m", current_time), day=os.date("%d", current_time) + day_offset, hour=hour, min=minute} if stream_time > current_time then local day_name if i == 0 then day_name = "Today" elseif i == 1 then day_name = "Tomorrow" else day_name = days_of_week[index] end return day_name .. settings.separator .. format_time(time) end end end return nil end -- Function to update the text source function update_text_source() local next_stream = get_next_stream() local text = next_stream and (settings.prefix .. next_stream .. " " .. settings.suffix) or "No stream scheduled" local source = obs.obs_get_source_by_name(settings.text_source_name) if source then local source_settings = obs.obs_source_get_settings(source) obs.obs_data_set_string(source_settings, "text", text) obs.obs_source_update(source, source_settings) obs.obs_data_release(source_settings) obs.obs_source_release(source) end end -- Function to populate the dropdown list with text sources function add_text_sources(property) obs.obs_property_list_add_string(property, "No text source selected", "") local sources = obs.obs_enum_sources() if sources ~= nil then for _, source in ipairs(sources) do local source_id = obs.obs_source_get_unversioned_id(source) if source_id == "text_gdiplus" or source_id == "text_ft2_source" then local name = obs.obs_source_get_name(source) obs.obs_property_list_add_string(property, name, name) end end end obs.source_list_release(sources) end -- Script properties function script_properties() local props = obs.obs_properties_create() -- Display current time, date, and week number local current_time = os.date("🗓️ %A %H:%M") local week_num = get_week_number() local is_even = is_even_week(week_num) and "even" or "odd" local header = string.format("%s, (Week %d)", current_time, week_num) obs.obs_properties_add_text(props, "header", header, obs.OBS_TEXT_INFO) local text_source_list = obs.obs_properties_add_list(props, "text_source_name", "⚙️ Text Source", obs.OBS_COMBO_TYPE_LIST, obs.OBS_COMBO_FORMAT_STRING) add_text_sources(text_source_list) -- Group for prefix, separator, and suffix local format_group = obs.obs_properties_create() local p = obs.obs_properties_add_text(format_group, "prefix", "Prefix", obs.OBS_TEXT_DEFAULT) obs.obs_property_set_long_description(p, "Text displayed before the next stream time.") obs.obs_data_set_default_string(obs.obs_data_create(), "prefix", settings.prefix) local s = obs.obs_properties_add_text(format_group, "separator", "Separator", obs.OBS_TEXT_DEFAULT) obs.obs_property_set_long_description(s, "Character displayed between the day and time of the next stream.") obs.obs_data_set_default_string(obs.obs_data_create(), "separator", settings.separator) local x = obs.obs_properties_add_text(format_group, "suffix", "Suffix", obs.OBS_TEXT_DEFAULT) obs.obs_property_set_long_description(x, "Text displayed after the next stream time.") obs.obs_data_set_default_string(obs.obs_data_create(), "suffix", settings.suffix) obs.obs_properties_add_group(props, "format_settings", "⚙️ Format Settings", obs.OBS_GROUP_NORMAL, format_group) -- Group for stream times local time_group = obs.obs_properties_create() local e = obs.obs_properties_add_text(time_group, "even_week_time", "Stream Start (Even Weeks)", obs.OBS_TEXT_DEFAULT) obs.obs_property_set_long_description(e, "Automatic stream start for even weeks.") obs.obs_data_set_default_string(obs.obs_data_create(), "even_week_time", settings.even_week_time) local o = obs.obs_properties_add_text(time_group, "odd_week_time", "Stream Start (Odd Weeks)", obs.OBS_TEXT_DEFAULT) obs.obs_property_set_long_description(o, "Automatic stream start for odd weeks.") obs.obs_data_set_default_string(obs.obs_data_create(), "odd_week_time", settings.odd_week_time) obs.obs_properties_add_group(props, "time_settings", "⚙️ Stream Start Settings", obs.OBS_GROUP_NORMAL, time_group) -- Day format settings local day_format = obs.obs_properties_add_list(props, "day_format", "🗓️ Day Format", obs.OBS_COMBO_TYPE_LIST, obs.OBS_COMBO_FORMAT_STRING) obs.obs_property_list_add_string(day_format, "Full (Monday)", "full") obs.obs_property_list_add_string(day_format, "Short (Mon)", "short") obs.obs_property_set_long_description(day_format, "Choose between full or short day format.") obs.obs_data_set_default_string(obs.obs_data_create(), "day_format", settings.day_format) -- Group for each day settings local days = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"} for i, day in ipairs(days) do local day_group = obs.obs_properties_create() local e = obs.obs_properties_add_bool(day_group, day:lower() .. "_enabled", "Streaming " .. day) obs.obs_property_set_long_description(e, "Enable to stream on this day.") if i == 1 then obs.obs_data_set_default_bool(obs.obs_data_create(), day:lower() .. "_enabled", true) end local time_choice = obs.obs_properties_add_list(day_group, day:lower() .. "_time_choice", "Stream Start", obs.OBS_COMBO_TYPE_LIST, obs.OBS_COMBO_FORMAT_STRING) obs.obs_property_list_add_string(time_choice, "Automatic (by week)", "auto") obs.obs_property_list_add_string(time_choice, "Manual", "custom") obs.obs_property_set_long_description(time_choice, "Choose whether the stream start time is automatic (based on the week) or manual.") obs.obs_data_set_default_string(obs.obs_data_create(), day:lower() .. "_time_choice", "auto") local c = obs.obs_properties_add_text(day_group, day:lower() .. "_custom_time", "Manual Stream Start", obs.OBS_TEXT_DEFAULT) obs.obs_property_set_long_description(c, "Manually set stream start time for this day.") obs.obs_data_set_default_string(obs.obs_data_create(), day:lower() .. "_custom_time", "") obs.obs_properties_add_group(props, day:lower(), day, obs.OBS_GROUP_NORMAL, day_group) end return props end -- Update settings when changed function script_update(new_settings) settings.text_source_name = obs.obs_data_get_string(new_settings, "text_source_name") settings.prefix = obs.obs_data_get_string(new_settings, "prefix") settings.separator = obs.obs_data_get_string(new_settings, "separator") settings.even_week_time = format_time(obs.obs_data_get_string(new_settings, "even_week_time")) settings.odd_week_time = format_time(obs.obs_data_get_string(new_settings, "odd_week_time")) settings.suffix = obs.obs_data_get_string(new_settings, "suffix") settings.day_format = obs.obs_data_get_string(new_settings, "day_format") local days = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"} for i, day in ipairs(days) do local day_lower = day:lower() settings.days[i].enabled = obs.obs_data_get_bool(new_settings, day_lower .. "_enabled") settings.days[i].auto_time = obs.obs_data_get_string(new_settings, day_lower .. "_time_choice") == "auto" settings.days[i].custom_time = format_time(obs.obs_data_get_string(new_settings, day_lower .. "_custom_time")) end update_text_source() end -- Initialize the script function script_description() return "'K_STYER's Dynamic Next Stream Version 1.4' updates a text source with your next stream day and start time. Automatic or manual settings for each day are possible. First, create a text source in OBS and select it from the dropdown list under 'Text Source'." end function script_load(new_settings) script_update(new_settings) obs.timer_add(update_text_source, 10000) -- Update every 10 seconds end function script_unload() obs.timer_remove(update_text_source) end