# frozen_string_literal: true module Mediamtx # Rimuove path MediaMTX live/match_{uuid} lasciati attivi dopo fine diretta. class CleanupOrphanPaths Result = Struct.new(:removed, :skipped, keyword_init: true) SESSION_PATH = %r{\Alive/match_([0-9a-f-]{36})(?:_air)?\z}i ACTIVE_SESSION_STATUSES = %w[live paused connecting reconnecting].freeze PROCESSING_GRACE = -> { ENV.fetch("RECORDINGS_LOCAL_PROCESSING_GRACE_HOURS", "6").to_i.hours } def initialize(client: Client.new, logger: Rails.logger) @client = client @logger = logger @removed = 0 @skipped = 0 @deleted_session_ids = Set.new end def call online = @client.online_path_names @client.list_paths.each do |item| name = item["name"].to_s session_id = session_id_from_path(name) unless session_id @skipped += 1 next end if online.include?(name) || item["online"] @skipped += 1 next end next unless removable?(session_id) remove_session_paths(session_id, name) end Result.new(removed: @removed, skipped: @skipped) end def removable?(session_id) session = StreamSession.find_by(id: session_id) return true if session.nil? return false if ACTIVE_SESSION_STATUSES.include?(session.status) recording = Recording.find_by(stream_session: session) return true if recording.nil? return false if recording.status == "processing" && processing_within_grace?(session) true end private def session_id_from_path(path_name) match = path_name.match(SESSION_PATH) match&.[](1) end def processing_within_grace?(session) ended_at = session.ended_at || session.updated_at ended_at.nil? || ended_at >= PROCESSING_GRACE.call.ago end def remove_session_paths(session_id, path_name) return if @deleted_session_ids.include?(session_id) session = StreamSession.find_by(id: session_id) if session @client.delete_path(session) else delete_orphan_path_names(session_id) end @deleted_session_ids.add(session_id) @removed += 1 @logger.info("[Mediamtx::CleanupOrphanPaths] removed paths for session #{session_id} (from #{path_name})") rescue Client::Error, Faraday::Error => e @skipped += 1 @logger.warn("[Mediamtx::CleanupOrphanPaths] skip #{path_name}: #{e.message}") end def delete_orphan_path_names(session_id) @client.delete_path_name("live/match_#{session_id}") @client.delete_path_name("live/match_#{session_id}_air") end end end