diff --git a/openresty/lua/thumbnail.lua b/openresty/lua/thumbnail.lua index b2f02ab..6d3eae8 100644 --- a/openresty/lua/thumbnail.lua +++ b/openresty/lua/thumbnail.lua @@ -2,10 +2,17 @@ local _M = {} local THUMB_DIR = "/tmp/thumbs" local GENERATION_INTERVAL = 600 -- 10 minutes in seconds +local NEW_STREAM_DELAY = 60 -- 1 minute delay for new streams +local NEW_STREAM_CHECK_INTERVAL = 10 -- Check for new streams every 10 seconds local FFMPEG_TIMEOUT = 15 -- seconds (needs more time for animated capture) local ANIMATION_DURATION = 3 -- seconds of video to capture local ANIMATION_FPS = 8 -- frames per second in output +-- Track known live streams (in-memory, resets on nginx restart) +local known_streams = {} +-- Track streams with pending thumbnail generation +local pending_streams = {} + -- SECURITY FIX: Shell escape function to prevent command injection local function shell_escape(str) -- Escape single quotes by ending the quoted string, adding escaped quote, and starting new quoted string @@ -141,6 +148,79 @@ local function generate_thumbnail(stream_key) return false end +-- Generate thumbnail for a specific new stream (delayed callback) +local function generate_new_stream_thumbnail(premature, realm_name, stream_key) + if premature then + return + end + + -- Clear pending flag + pending_streams[realm_name] = nil + + ngx.log(ngx.INFO, "Generating initial thumbnail for new stream: ", realm_name) + + -- Ensure thumb directory exists + os.execute("mkdir -p " .. THUMB_DIR) + + -- Generate the thumbnail + if generate_thumbnail(stream_key) then + ngx.log(ngx.INFO, "Initial thumbnail generated for new stream: ", realm_name) + else + ngx.log(ngx.WARN, "Failed to generate initial thumbnail for new stream: ", realm_name) + end +end + +-- Check for new streams and schedule thumbnail generation +local function check_for_new_streams(premature) + if premature then + return + end + + local realms = get_live_realms() + local current_streams = {} + + for _, realm in ipairs(realms) do + if realm.name then + current_streams[realm.name] = true + + -- Check if this is a new stream we haven't seen + if not known_streams[realm.name] and not pending_streams[realm.name] then + ngx.log(ngx.INFO, "New stream detected: ", realm.name, " - scheduling thumbnail in ", NEW_STREAM_DELAY, " seconds") + + local stream_key = get_stream_key_for_realm(realm.name) + if stream_key then + -- Mark as pending to avoid duplicate scheduling + pending_streams[realm.name] = true + + -- Schedule thumbnail generation after delay + local ok, err = ngx.timer.at(NEW_STREAM_DELAY, generate_new_stream_thumbnail, realm.name, stream_key) + if not ok then + ngx.log(ngx.ERR, "Failed to schedule new stream thumbnail: ", err) + pending_streams[realm.name] = nil + end + end + end + + -- Mark stream as known + known_streams[realm.name] = true + end + end + + -- Clean up streams that are no longer live + for name, _ in pairs(known_streams) do + if not current_streams[name] then + known_streams[name] = nil + pending_streams[name] = nil + end + end + + -- Schedule next check + local ok, err = ngx.timer.at(NEW_STREAM_CHECK_INTERVAL, check_for_new_streams) + if not ok then + ngx.log(ngx.ERR, "Failed to schedule next new stream check: ", err) + end +end + -- Background job: Generate thumbnails for all live streams local function generate_all_thumbnails(premature) if premature then @@ -186,11 +266,17 @@ function _M.init_worker() -- Ensure thumb directory exists os.execute("mkdir -p " .. THUMB_DIR) - -- Start first generation after 10 seconds (let services start up) + -- Start first full generation after 10 seconds (let services start up) local ok, err = ngx.timer.at(10, generate_all_thumbnails) if not ok then ngx.log(ngx.ERR, "Failed to start thumbnail generator: ", err) end + + -- Start new stream detection after 15 seconds + ok, err = ngx.timer.at(15, check_for_new_streams) + if not ok then + ngx.log(ngx.ERR, "Failed to start new stream detector: ", err) + end end -- Serve existing thumbnail (no generation on request)