Aggiunge modulo Replay, Garage dev e YouTube a livello società.
Pipeline registrazione/upload con storage S3, archivio web e app, proxy replay per il browser, OAuth YouTube sulla pagina club (Premium Full) e footer legale. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
90
backend/app/views/public/replay/index.html.erb
Normal file
90
backend/app/views/public/replay/index.html.erb
Normal file
@@ -0,0 +1,90 @@
|
||||
<% content_for :title, "Replay — Match Live TV" %>
|
||||
<% content_for :meta_description, "Archivio replay delle dirette sportive su Match Live TV. Cerca per società, squadra o avversario." %>
|
||||
|
||||
<div class="wrap replay-index">
|
||||
<%= link_to "← Dirette live", public_live_index_path, class: "back-link" %>
|
||||
|
||||
<h1>Live passate</h1>
|
||||
<p class="results-hint">Replay pubblici delle società sportive — riguarda le partite già trasmesse.</p>
|
||||
|
||||
<%= form_with url: public_replay_index_path, method: :get, local: true, class: "search-form" do %>
|
||||
<% if params[:club_id].present? %>
|
||||
<%= hidden_field_tag :club_id, params[:club_id] %>
|
||||
<% end %>
|
||||
<input
|
||||
type="search"
|
||||
name="q"
|
||||
value="<%= @query %>"
|
||||
placeholder="Es. Tigers Volley, avversario, società, luogo…"
|
||||
aria-label="Cerca replay"
|
||||
autocomplete="off"
|
||||
/>
|
||||
<button type="submit" class="btn btn-primary">Cerca</button>
|
||||
<% if @query.present? || params[:club_id].present? %>
|
||||
<%= link_to "Azzera", public_replay_index_path, class: "btn btn-secondary" %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<% if @clubs.many? %>
|
||||
<%= form_with url: public_replay_index_path, method: :get, local: true, class: "replay-filter-form" do %>
|
||||
<% if @query.present? %>
|
||||
<%= hidden_field_tag :q, @query %>
|
||||
<% end %>
|
||||
<label for="club_id" class="muted">Società</label>
|
||||
<select name="club_id" id="club_id" class="input" onchange="this.form.submit()">
|
||||
<option value="">Tutte le società</option>
|
||||
<% @clubs.each do |club| %>
|
||||
<option value="<%= club.id %>"<%= " selected" if params[:club_id].to_s == club.id.to_s %>><%= club.name %></option>
|
||||
<% end %>
|
||||
</select>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<% if @query.present? %>
|
||||
<p class="results-hint">
|
||||
Risultati per «<%= h @query %>»<% if @filter_club %> · <%= @filter_club.name %><% end %>:
|
||||
<%= @recordings.size %> replay
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
<% if @recordings.any? %>
|
||||
<div class="replay-grid">
|
||||
<% @recordings.each do |rec| %>
|
||||
<% match = rec.stream_session.match %>
|
||||
<%= link_to public_replay_path(rec.stream_session_id), class: "replay-card" do %>
|
||||
<div class="replay-card__media">
|
||||
<% if rec.thumbnail_url %>
|
||||
<img src="<%= rec.thumbnail_url %>" alt="" class="replay-card__thumb" loading="lazy" width="320" height="180">
|
||||
<% else %>
|
||||
<div class="replay-card__thumb replay-card__thumb--placeholder" aria-hidden="true">
|
||||
<span class="replay-card__placeholder-icon">▶</span>
|
||||
</div>
|
||||
<% end %>
|
||||
<span class="replay-card__duration"><%= rec.duration_label %></span>
|
||||
<span class="replay-card__play" aria-hidden="true">▶</span>
|
||||
</div>
|
||||
<div class="replay-card__body">
|
||||
<strong class="replay-card__title"><%= rec.title_or_default %></strong>
|
||||
<p class="replay-card__meta">
|
||||
<%= match.team.club.name %> · <%= l_local(rec.recorded_at_or_fallback) %>
|
||||
</p>
|
||||
<p class="replay-card__meta replay-card__meta--sub">
|
||||
<%= rec.views_label %>
|
||||
</p>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% else %>
|
||||
<div class="empty-state">
|
||||
<% if @query.present? || params[:club_id].present? %>
|
||||
<p><strong>Nessun replay trovato</strong> con i filtri selezionati.</p>
|
||||
<p><%= link_to "Mostra tutti i replay pubblici", public_replay_index_path, class: "btn btn-secondary" %></p>
|
||||
<% else %>
|
||||
<p><strong>Nessun replay pubblico al momento.</strong></p>
|
||||
<p>Quando una società rende pubblica una registrazione, comparirà qui.</p>
|
||||
<%= link_to "Vai alle dirette live", public_live_index_path, class: "btn btn-secondary", style: "margin-top:12px;display:inline-block" %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
@@ -1,39 +1,58 @@
|
||||
<% content_for :title, "Replay — #{@match.team.name} vs #{@match.opponent_name}" %>
|
||||
<% content_for :meta_description, "Replay partita #{@match.team.name} vs #{@match.opponent_name} su Match Live TV." %>
|
||||
<% content_for :robots, "noindex, nofollow" %>
|
||||
<% content_for :title, "Replay — #{@recording.title_or_default}" %>
|
||||
<% content_for :meta_description, "Replay #{@recording.title_or_default} su Match Live TV." %>
|
||||
<% content_for :robots, @recording.publicly_listed? ? "index, follow" : "noindex, nofollow" %>
|
||||
|
||||
<div class="wrap">
|
||||
<%= link_to "← Tutte le dirette", public_live_index_path, class: "back-link" %>
|
||||
<div class="wrap replay-show">
|
||||
<%= link_to "← Live passate", public_replay_index_path, class: "back-link" %>
|
||||
|
||||
<h1>Replay</h1>
|
||||
<p style="color:#aaa"><%= @match.team.name %> vs <%= @match.opponent_name %></p>
|
||||
<header class="replay-show__header">
|
||||
<h1><%= @recording.title_or_default %></h1>
|
||||
<p class="replay-show__meta">
|
||||
<%= @match.team.club.name %> · <%= l_local(@recording.recorded_at_or_fallback) %>
|
||||
· <%= @recording.views_label %>
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<% if @recording %>
|
||||
<div class="card" style="margin:16px 0">
|
||||
<% if @session.hls_playback_url.present? %>
|
||||
<video id="replay-player" class="replay-video" controls playsinline></video>
|
||||
<script src="https://cdn.jsdelivr.net/npm/hls.js@1.5.7"></script>
|
||||
<script>
|
||||
(function() {
|
||||
var src = "<%= j @session.hls_playback_url %>";
|
||||
var video = document.getElementById("replay-player");
|
||||
if (video.canPlayType("application/vnd.apple.mpegurl")) {
|
||||
video.src = src;
|
||||
} else if (typeof Hls !== "undefined" && Hls.isSupported()) {
|
||||
var hls = new Hls();
|
||||
hls.loadSource(src);
|
||||
hls.attachMedia(video);
|
||||
} else {
|
||||
video.outerHTML = "<p>Il replay non è disponibile su questo browser.</p>";
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<% else %>
|
||||
<p>Registrazione indicizzata. Il playback dipende dalla retention del piano Premium.</p>
|
||||
<% end %>
|
||||
<% if @recording.expires_at %>
|
||||
<p class="stream-ended-meta" style="margin-top:12px">Disponibile fino al <%= l_local(@recording.expires_at) %></p>
|
||||
<% end %>
|
||||
<% if @recording.status == "processing" %>
|
||||
<div class="card replay-show__status">
|
||||
<p>Registrazione in elaborazione. Riceverai un’email quando sarà pronta.</p>
|
||||
</div>
|
||||
<% elsif @recording.ready? %>
|
||||
<div class="replay-show__player card">
|
||||
<video id="replay-player" class="replay-video" controls playsinline preload="metadata"
|
||||
<% if @recording.thumbnail_url %> poster="<%= @recording.thumbnail_url %>"<% end %>>
|
||||
<source src="<%= public_replay_stream_path(@session) %>" type="video/mp4">
|
||||
</video>
|
||||
</div>
|
||||
|
||||
<div class="replay-show__details card">
|
||||
<p class="replay-show__details-line">
|
||||
<span>Durata <%= @recording.duration_label %></span>
|
||||
<% if @recording.expires_at %>
|
||||
<span>· Disponibile fino al <%= l_local(@recording.expires_at) %></span>
|
||||
<% end %>
|
||||
<span>· <%= @recording.publicly_listed? ? "Pubblico" : "Privato (link)" %></span>
|
||||
<% if @recording.byte_size.to_i.positive? %>
|
||||
<span>· <%= @recording.byte_size_label %></span>
|
||||
<% end %>
|
||||
</p>
|
||||
<% ent = @recording.team.entitlements %>
|
||||
<div class="replay-show__actions">
|
||||
<% if ent.phone_download_enabled? && (logged_in? || @recording.unlisted?) %>
|
||||
<%= link_to "Scarica MP4", public_replay_download_path(@session), class: "btn btn-primary" %>
|
||||
<% end %>
|
||||
<% if @recording.youtube_watch_url %>
|
||||
<%= link_to "Apri su YouTube", @recording.youtube_watch_url, class: "btn btn-secondary", target: "_blank", rel: "noopener" %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<% elsif @recording.status == "failed" %>
|
||||
<div class="card replay-show__status">
|
||||
<p>Replay non disponibile: <%= @recording.error_message.presence || "errore di elaborazione" %>.</p>
|
||||
</div>
|
||||
<% else %>
|
||||
<div class="card replay-show__status">
|
||||
<p>Replay non più disponibile.</p>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user