-- OBS Lua Script: Stream Uptime to File -- This script writes the current stream uptime to a user-selected file while streaming. obs = obslua source_name = "" output_path = "" format_string = "{h}:{m}:{s}" interval_sec = 1 -- seconds interval = 1000 -- ms (default, will be set in script_update) start_time = nil hotkey_id = obs.OBS_INVALID_HOTKEY_ID function script_description() return "Writes the current stream uptime to a file while streaming. Configure the output file path in script settings." end function script_properties() local props = obs.obs_properties_create() obs.obs_properties_add_path(props, "output_path", "Output File Path", obs.OBS_PATH_FILE_SAVE, "Text File (*.txt)", nil) obs.obs_properties_add_text(props, "format_string", "Format String", obs.OBS_TEXT_DEFAULT) obs.obs_property_set_long_description(obs.obs_properties_get(props, "format_string"), "Use {h} for hours, {m} for minutes, {s} for seconds. Example: {h}:{m}:{s}, {h}h {m}m, {m} minutes, etc.") local interval_prop = obs.obs_properties_add_int(props, "interval_sec", "Update Interval (seconds)", 1, 60, 1) obs.obs_property_set_long_description(interval_prop, "How often to update the file, in seconds (default: 1)") return props end function script_update(settings) output_path = obs.obs_data_get_string(settings, "output_path") local fmt = obs.obs_data_get_string(settings, "format_string") if fmt ~= nil and fmt ~= "" then format_string = fmt else format_string = "{h}:{m}:{s}" end interval_sec = obs.obs_data_get_int(settings, "interval_sec") if interval_sec < 1 then interval_sec = 1 end interval = interval_sec * 1000 end function on_event(event) if event == obs.OBS_FRONTEND_EVENT_STREAMING_STARTED then start_time = os.time() obs.timer_add(update_uptime, interval) elseif event == obs.OBS_FRONTEND_EVENT_STREAMING_STOPPED then obs.timer_remove(update_uptime) erase_uptime_file() start_time = nil end -- If timer is running, update interval obs.timer_remove(update_uptime) if start_time then obs.timer_add(update_uptime, interval) end end function update_uptime() if start_time and output_path ~= "" then local elapsed = os.time() - start_time write_uptime(elapsed) end end function write_uptime(seconds) local file = io.open(output_path, "w") if file then file:write(format_time_custom(seconds)) file:close() end end function erase_uptime_file() if output_path ~= "" then local file = io.open(output_path, "w") if file then file:write("") file:close() end end end function format_time_custom(seconds) local d = math.floor(seconds / 86400) local h = math.floor((seconds % 86400) / 3600) local m = math.floor((seconds % 3600) / 60) local s = seconds % 60 local fmt = format_string or "{h}:{m}:{s}" -- Zero-padded local dd = string.format("%02d", d) local hh = string.format("%02d", h) local mm = string.format("%02d", m) local ss = string.format("%02d", s) -- Non-padded local D = tostring(d) local H = tostring(h) local M = tostring(m) local S = tostring(s) -- Smart removal for non-padded units if d == 0 then fmt = fmt:gsub("0d[ ,]*", "") fmt = fmt:gsub("0d", "") fmt = fmt:gsub("0D[ ,]*", "") fmt = fmt:gsub("0D", "") fmt = fmt:gsub("{D}d[ ,]*", "") fmt = fmt:gsub("{D}d", "") end if h == 0 then fmt = fmt:gsub("0h[ ,]*", "") fmt = fmt:gsub("0h", "") fmt = fmt:gsub("0H[ ,]*", "") fmt = fmt:gsub("0H", "") fmt = fmt:gsub("{H}h[ ,]*", "") fmt = fmt:gsub("{H}h", "") end if m == 0 then fmt = fmt:gsub("0m[ ,]*", "") fmt = fmt:gsub("0m", "") fmt = fmt:gsub("0M[ ,]*", "") fmt = fmt:gsub("0M", "") fmt = fmt:gsub("{M}m[ ,]*", "") fmt = fmt:gsub("{M}m", "") end -- Replace placeholders fmt = fmt:gsub("{d}", dd) fmt = fmt:gsub("{h}", hh) fmt = fmt:gsub("{m}", mm) fmt = fmt:gsub("{s}", ss) fmt = fmt:gsub("{D}", d ~= 0 and D or "") fmt = fmt:gsub("{H}", h ~= 0 and H or "") fmt = fmt:gsub("{M}", m ~= 0 and M or "") fmt = fmt:gsub("{S}", S) -- Remove leading/trailing whitespace fmt = fmt:gsub("^%s+", ""):gsub("%s+$", "") return fmt end function script_load(settings) obs.obs_frontend_add_event_callback(on_event) end function script_unload() obs.timer_remove(update_uptime) end