module Streams # Assicura che la slate MP4 custom sia sul nodo assegnato (home/lab locale, CPX via agent). class SlateDistributor class Error < StandardError; end def self.slate_path_for_session(session) new(session).mediamtx_slate_path end def self.ensure_for!(session) new(session).ensure! end def initialize(session) @session = session @match = session.match @node = session.stream_node @resolver = CoverResolver.new(@match) end def mediamtx_slate_path ensure! unless defined?(@mediamtx_path) @mediamtx_path end def ensure! result = @resolver.resolve unless result @mediamtx_path = default_slate_path return @mediamtx_path end host_path = result.slate_local_path filename = File.basename(host_path) mediamtx_path = "#{CoverSlatePaths.slates_custom_root}/#{filename}" if cloud_node? push_to_agent!(filename, result.record, host_path) else verify_local_slate!(host_path) end @mediamtx_path = mediamtx_path rescue Error => e Rails.logger.warn("[SlateDistributor] session=#{@session.id} #{e.message}") @mediamtx_path = default_slate_path end private def cloud_node? @node&.role == "cloud" && agent_url.present? end def agent_url ENV["STREAM_NODE_RELAY_AGENT_URL"].presence || @node&.relay_agent_url end def agent_secret ENV["STREAM_NODE_AGENT_SECRET"].presence || "mediamtx_webhook_dev_secret" end def push_to_agent!(filename, record, host_path) bytes = read_slate_bytes(record, host_path) raise Error, "slate bytes missing for #{filename}" if bytes.blank? uri = URI.parse("#{agent_url.chomp('/')}/slates/#{CGI.escape(filename)}") http = Net::HTTP.new(uri.host, uri.port) http.open_timeout = 5 http.read_timeout = 120 req = Net::HTTP::Put.new(uri) req["Authorization"] = "Bearer #{agent_secret}" if agent_secret.present? req["Content-Type"] = "video/mp4" req.body = bytes res = http.request(req) return if res.is_a?(Net::HTTPSuccess) raise Error, "agent PUT /slates/#{filename} HTTP #{res.code} #{res.body.to_s.truncate(200)}" end def read_slate_bytes(record, host_path) return File.binread(host_path) if host_path.present? && File.exist?(host_path) return unless record.cover_slate.attached? record.cover_slate.blob.open(tmpdir: Dir.tmpdir) do |file| return File.binread(file.path) end end def verify_local_slate!(host_path) return if host_path.present? && File.exist?(host_path) raise Error, "slate missing on disk: #{host_path}" end def default_slate_path ENV.fetch("MEDIAMTX_SLATE_FILE", "/slates/offline.mp4") end end end