Aggiunge overlay server-side burn-in e stabilizza diretta live.

Tabellone, badge e brand sono ricodificati nel flusso _air via OverlayRelay;
sync punteggio HTTP, volume condiviso rails/sidekiq, qualità 720p migliorata
e badge CONNECTING in ripresa da pausa.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-03 23:55:17 +02:00
parent fd225fbadd
commit ab9cb02083
58 changed files with 2423 additions and 242 deletions

View File

@@ -1,5 +1,7 @@
module Public
class LiveController < SiteBaseController
include MediamtxPlayback
layout "marketing_live"
ACTIVE_STATUSES = %w[live connecting reconnecting].freeze
@@ -40,31 +42,41 @@ module Public
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_online?(@session)
@on_air = !@stream_closed && mediamtx_stream_playable?(@session)
@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? || mediamtx_online?(session)),
on_air: !closed && mediamtx_online?(session),
live: !closed && (
session.live? || session.paused? || session.connecting? || session.reconnecting? ||
mediamtx_publisher_online?(session)
),
on_air: playable,
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
private
def mediamtx_online?(session)
@online_paths_cache ||= Mediamtx::Client.new.online_path_names
@online_paths_cache.include?(session.mediamtx_path_name)
end
end
end

View File

@@ -1,6 +1,7 @@
module Public
class RegiaController < SiteBaseController
include Public::LiveHelper
include MediamtxPlayback
layout "regia"
@@ -17,7 +18,7 @@ module Public
)
@rules = Scoring::Rules.from_match(@match)
@stream_closed = @session.terminal?
@on_air = !@stream_closed && mediamtx_online?(@session)
@on_air = !@stream_closed && mediamtx_stream_playable?(@session)
@share_url = request.original_url
@share_title = "Regia — #{@team.name} vs #{@match.opponent_name}"
end
@@ -61,12 +62,14 @@ module Public
def status_payload
score = @session.score_state
closed = @session.terminal?
playable = !closed && mediamtx_stream_playable?(@session)
{
status: @session.status,
paused: @session.paused?,
stream_closed: closed,
live: !closed && (@session.live? || mediamtx_online?(@session)),
on_air: !closed && mediamtx_online?(@session),
live: !closed && (@session.live? || @session.paused? || mediamtx_publisher_online?(@session)),
on_air: playable,
publisher_online: !closed && mediamtx_publisher_online?(@session),
ended_at: @session.ended_at,
home_name: @session.match.team.name,
away_name: @session.match.opponent_name,
@@ -74,10 +77,5 @@ module Public
score: score&.as_cable_payload
}
end
def mediamtx_online?(session)
@online_paths_cache ||= Mediamtx::Client.new.online_path_names
@online_paths_cache.include?(session.mediamtx_path_name)
end
end
end