-- obs-recording-timer.lua
-- Recording Timer for OBS Studio
--
-- 作用:把一个文本源变成「录制时长计时器」。
-- 开始录制时自动计时,暂停录制时冻结,停止后保留最终时长。
--
-- 安装:OBS → 工具 → 脚本 → 加号 → 选这个文件
-- 然后在下拉框里选一个「文本(GDI+)」源,即可生效。
--
-- 作者:<你的署名>
-- 许可:MIT
obs = obslua
-- ---------------------------------------------------------------------------
-- 设置项
-- ---------------------------------------------------------------------------
source_name = ""
prefix = ""
idle_text = ""
reset_on_start = true
hotkey_id = nil -- 该常量枚举不到,不依赖;script_load 里必然先赋值
-- ---------------------------------------------------------------------------
-- 内部状态
-- ---------------------------------------------------------------------------
local state = "idle" -- idle | recording | paused
local elapsed_base = 0 -- 已完成分段的累计秒数
local seg_start = 0 -- 当前分段的起点墙钟(os.time)
local last_text = ""
-- ---------------------------------------------------------------------------
-- 工具函数
-- ---------------------------------------------------------------------------
local function format_hms(total)
total = math.floor(total)
if total < 0 then total = 0 end
local s = total % 60
local m = math.floor(total / 60) % 60
local h = math.floor(total / 3600)
return string.format("%02d:%02d:%02d", h, m, s)
end
local function total_seconds()
if state == "recording" and seg_start > 0 then
return elapsed_base + (os.time() - seg_start)
end
return elapsed_base
end
local function set_text(text)
-- 只在文字真的变了的时候才去更新源(和官方 countdown.lua 同样的做法,
-- 避免每秒都惊动一次 OBS 的无关重绘)
if text == last_text then
return
end
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, "text", text)
obs.obs_source_update(source, settings)
obs.obs_data_release(settings)
obs.obs_source_release(source)
end
last_text = text
end
-- ---------------------------------------------------------------------------
-- 计时循环
-- ---------------------------------------------------------------------------
local function tick()
local active = obs.obs_frontend_recording_active()
local paused = false
if active then
paused = obs.obs_frontend_recording_paused()
end
local new_state
if not active then
new_state = "idle"
elseif paused then
new_state = "paused"
else
new_state = "recording"
end
-- 状态迁移
if new_state ~= state then
if state == "idle" then
-- 新一轮录制开始
if reset_on_start then
elapsed_base = 0
end
seg_start = os.time()
elseif state == "recording" and new_state == "paused" then
-- 暂停:把这一段封存起来
if seg_start > 0 then
elapsed_base = elapsed_base + (os.time() - seg_start)
end
seg_start = 0
elseif state == "paused" and new_state == "recording" then
-- 继续
seg_start = os.time()
elseif new_state == "idle" then
-- 停止:封存最后一段,并保留最终值在画面上
if state == "recording" and seg_start > 0 then
elapsed_base = elapsed_base + (os.time() - seg_start)
end
seg_start = 0
end
state = new_state
end
-- 渲染
if state == "idle" then
if idle_text ~= "" then
set_text(idle_text)
end
else
set_text(prefix .. format_hms(total_seconds()))
end
end
-- ---------------------------------------------------------------------------
-- 手动重置
-- ---------------------------------------------------------------------------
local function reset(pressed)
if not pressed then
return
end
elapsed_base = 0
if state == "recording" then
seg_start = os.time()
end
-- 强制重画
local keep = last_text
last_text = "\0"
set_text(keep)
end
-- ---------------------------------------------------------------------------
-- 脚本接口
-- ---------------------------------------------------------------------------
function script_description()
return "把文本源变成录制时长计时器。\n\n" ..
"开始录制自动计时,暂停时冻结,停止后保留最终时长。\n" ..
"适合放在画面角上,让观众/自己看到这段已经录了多久。"
end
function script_properties()
local props = obs.obs_properties_create()
local p = obs.obs_properties_add_list(props, "source", "文本源 (Text Source)",
obs.OBS_COMBO_TYPE_EDITABLE, obs.OBS_COMBO_FORMAT_STRING)
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(p, name, name)
end
end
end
obs.source_list_release(sources)
obs.obs_properties_add_text(props, "prefix", "前缀(可选)", obs.OBS_TEXT_DEFAULT)
obs.obs_properties_add_text(props, "idle_text", "未录制时显示(留空=不动)", obs.OBS_TEXT_DEFAULT)
obs.obs_properties_add_bool(props, "reset_on_start", "每次开录清零")
obs.obs_properties_add_button(props, "reset_button", "手动清零", reset_button_clicked)
return props
end
function script_defaults(settings)
obs.obs_data_set_default_string(settings, "prefix", "")
obs.obs_data_set_default_string(settings, "idle_text", "")
obs.obs_data_set_default_bool(settings, "reset_on_start", true)
end
function script_update(settings)
source_name = obs.obs_data_get_string(settings, "source")
prefix = obs.obs_data_get_string(settings, "prefix")
idle_text = obs.obs_data_get_string(settings, "idle_text")
reset_on_start = obs.obs_data_get_bool(settings, "reset_on_start")
last_text = "\0" -- 强制重画一次
end
function reset_button_clicked(props, p)
reset(true)
return false
end
function script_save(settings)
local hotkey_save_array = obs.obs_hotkey_save(hotkey_id)
obs.obs_data_set_array(settings, "reset_hotkey", hotkey_save_array)
obs.obs_data_array_release(hotkey_save_array)
end
function script_load(settings)
hotkey_id = obs.obs_hotkey_register_frontend("recording_timer_reset", "录制计时器:清零", reset)
local hotkey_save_array = obs.obs_data_get_array(settings, "reset_hotkey")
obs.obs_hotkey_load(hotkey_id, hotkey_save_array)
obs.obs_data_array_release(hotkey_save_array)
-- 打印一行到 OBS 日志:脚本到底加载成功没有,用这个确认,别靠猜
print("[obs-recording-timer] loaded ok")
-- 每 500ms 轮询一次:不用事件回调,避免依赖不同版本的 frontend event 接口
obs.timer_add(tick, 500)
end
function script_unload()
obs.timer_remove(tick)
end