module Public class LiveController < SiteBaseController include MediamtxPlayback layout "marketing_live" ACTIVE_STATUSES = %w[live connecting reconnecting].freeze def index @query = params[:q].to_s.strip @club = Club.find_by(id: params[:club_id]) if params[:club_id].present? team_ids = @club&.teams&.pluck(:id) sessions = StreamSession .broadcasting .publicly_listed .where(platform: "matchlivetv") .includes(:score_state, match: { team: :club }) sessions = sessions.joins(:match).where(matches: { team_id: team_ids }) if team_ids.present? @sessions = sessions .search_by_team_or_opponent(@query) .order(Arel.sql("started_at DESC NULLS LAST"), created_at: :desc) # Solo dirette già in onda: le partite «programmate» restano visibili anche se c’è sessione idle. broadcasting_match_ids = StreamSession.broadcasting.select(:match_id) upcoming = Match.scheduled_for_live.includes(team: :club) upcoming = upcoming.where(team_id: team_ids) if team_ids.present? @upcoming_matches = upcoming .search_teams_or_opponents(@query) .where.not(id: broadcasting_match_ids) .order(scheduled_at: :asc) .limit(50) @online_paths = Mediamtx::Client.new.online_path_names return unless logged_in? && @club @schedulable_teams = current_user.schedulable_teams_for(@club) @can_schedule_match = @schedulable_teams.any? end def show @session = StreamSession.includes(match: { team: :club }).find(params[:id]) @session = Mediamtx::PublisherSync.new(@session).call unless @session.terminal? @match = @session.match @stream_closed = @session.terminal? @on_air = !@stream_closed && ( mediamtx_stream_playable?(@session) || @session.status.in?(%w[connecting live reconnecting paused]) ) @publisher_online = !@stream_closed && mediamtx_publisher_online?(@session) end def status # Mai cache: il tabellone deve restare allineato con l'app (poll ogni ~1,5s). response.headers["Cache-Control"] = "no-store, no-cache, must-revalidate" response.headers["Pragma"] = "no-cache" session = StreamSession.includes(match: { team: :club }).find(params[:id]) session = Mediamtx::PublisherSync.new(session).call unless session.terminal? closed = session.terminal? publisher_online = !closed && mediamtx_publisher_online?(session) playable = !closed && mediamtx_stream_playable?(session) render json: { status: session.status, paused: session.paused?, stream_closed: closed, live: !closed && ( session.live? || session.paused? || session.connecting? || session.reconnecting? || mediamtx_publisher_online?(session) ), on_air: playable || (!closed && session.status.in?(%w[connecting live reconnecting paused])), publisher_online: publisher_online, awaiting_signal: !closed && !mediamtx_path_has_output?(session), showing_cover: !closed && mediamtx_path_has_output?(session) && (session.paused? || !mediamtx_publisher_online?(session)), ended_at: session.ended_at, home_name: session.match.team.name, away_name: session.match.opponent_name, score: session.score_state&.as_cable_payload } end end end