Stabilizza regia, live web e sync punteggi app nativa.

Corregge scadenza token regia oltre le 8h, tabellone HTML sulla pagina live,
pulsanti pausa/ripresa in regia, link admin e broadcast ActionCable diretto.
App Android: overlay branding, sync score da regia via WebSocket con reconnect e poll.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-08 09:30:43 +02:00
parent 08e800120a
commit ae36d17adb
55 changed files with 2819 additions and 285 deletions

View File

@@ -91,7 +91,7 @@
<% if @active_sessions.any? %>
<table class="admin-table">
<thead>
<tr><th>Partita</th><th>Stato</th><th>Inizio</th><th></th></tr>
<tr><th>Partita</th><th>Stato</th><th>Inizio</th><th>Link</th><th></th></tr>
</thead>
<tbody id="active-sessions-body">
<% @active_sessions.each do |s| %>
@@ -99,6 +99,12 @@
<td><%= s.match.team.name %> vs <%= s.match.opponent_name %></td>
<td><span class="badge badge--<%= s.status == 'live' ? 'live' : (s.status == 'paused' ? 'paused' : 'connecting') %>"><%= s.status %></span></td>
<td class="muted"><%= s.started_at&.strftime("%d/%m %H:%M") || "—" %></td>
<td class="admin-link-compact">
<% admin_session_watch_links(s).each do |link| %>
<%= link_to link[:label], link[:url], target: "_blank", rel: "noopener", class: "admin-link-chip" %>
<% end %>
<%= link_to "Regia", admin_session_path(s), class: "admin-link-chip admin-link-chip--regia" %>
</td>
<td class="admin-actions">
<%= link_to "Dettaglio", admin_session_path(s) %>
<%= button_to "Termina", stop_admin_session_path(s), method: :post, class: "admin-btn admin-btn--danger admin-btn--sm", form: { data: { turbo_confirm: "Terminare questa sessione?" } } %>

View File

@@ -0,0 +1,41 @@
<% watch_links = admin_session_watch_links(session) %>
<section class="admin-links-panel">
<h3>Link diretta</h3>
<% if watch_links.any? %>
<ul class="admin-link-list">
<% watch_links.each do |link| %>
<li>
<span class="admin-link-label"><%= link[:label] %></span>
<%= link_to link[:url], link[:url], target: "_blank", rel: "noopener", class: "admin-link-url" %>
</li>
<% end %>
</ul>
<% else %>
<p class="muted">Nessun link video disponibile per questa sessione.</p>
<% end %>
<h3>Link regia</h3>
<% if session.terminal? %>
<p class="muted">Sessione terminata — link regia non disponibile.</p>
<% else %>
<% if flash[:regia_url].present? %>
<p class="admin-link-generated">
<span class="admin-link-label">Regia</span>
<%= link_to flash[:regia_url], flash[:regia_url], target: "_blank", rel: "noopener", class: "admin-link-url" %>
</p>
<p><code class="admin-link-code"><%= flash[:regia_url] %></code></p>
<% if (expires = admin_regia_expires_label(flash[:regia_expires_at])) %>
<p class="muted">Valido fino a <%= expires %></p>
<% end %>
<% elsif session.regia_token_active? %>
<p class="muted">Esiste già un link regia attivo (lURL non è recuperabile). Generane uno nuovo se serve condividerlo di nuovo.</p>
<% else %>
<p class="muted">Nessun link regia attivo. Genera un link da condividere con chi gestisce il punteggio.</p>
<% end %>
<%= button_to "Genera link regia",
regia_link_admin_session_path(session),
method: :post,
class: "admin-btn admin-btn--secondary admin-btn--sm",
form: { class: "admin-inline-form" } %>
<% end %>
</section>

View File

@@ -1,12 +1,18 @@
<h2>Stream Sessions</h2>
<table class="admin-table">
<thead><tr><th>Match</th><th>Status</th><th>Disconnects</th><th></th></tr></thead>
<thead><tr><th>Match</th><th>Status</th><th>Disconnects</th><th>Link</th><th></th></tr></thead>
<tbody>
<% @sessions.each do |s| %>
<tr>
<td><%= s.match.team.name %> vs <%= s.match.opponent_name %></td>
<td><span class="badge <%= s.status == 'live' ? 'badge--live' : 'badge--paused' %>"><%= s.status %></span></td>
<td><%= s.disconnection_count %></td>
<td class="admin-link-compact">
<% admin_session_watch_links(s).each do |link| %>
<%= link_to link[:label], link[:url], target: "_blank", rel: "noopener", class: "admin-link-chip" %>
<% end %>
<%= link_to "Regia", admin_session_path(s), class: "admin-link-chip admin-link-chip--regia" %>
</td>
<td><%= link_to "Dettaglio", admin_session_path(s) %></td>
</tr>
<% end %>

View File

@@ -10,8 +10,11 @@
</p>
<% end %>
<p>Match: <%= @session.match.team.name %> vs <%= @session.match.opponent_name %></p>
<%= render "admin/sessions/links", session: @session %>
<% if @session.youtube_broadcast_id %>
<p>YouTube: <a href="https://studio.youtube.com/video/<%= @session.youtube_broadcast_id %>/livestreaming" target="_blank">Broadcast</a></p>
<p>YouTube Studio: <a href="https://studio.youtube.com/video/<%= @session.youtube_broadcast_id %>/livestreaming" target="_blank" rel="noopener">Broadcast</a></p>
<% end %>
<p>RTMP ingest: <code><%= @session.rtmp_ingest_url %></code></p>