Così overflow Hetzner e VOD YouTube restano in archivio dopo lo spegnimento del nodo, e la colonna ingest non si svuota. Co-authored-by: Cursor <cursoragent@cursor.com>
5.4 KiB
Modulo Replay — Match Live TV
Panoramica
Al termine di ogni diretta Premium, MediaMTX registra i segmenti. Sidekiq unisce i segmenti, genera thumbnail e carica su Garage (S3-compatible).
- Live MatchLiveTV (HLS): copia permanente (policy
retained) con retention del piano. - Live YouTube: copia temporanea di sicurezza (policy
temporary, default 48h), poi player YouTube in archivio. Niente re-upload automatico del MP4 su YouTube.
Piani e retention
| Piano | Registrazione | Retention archivio MLTV | Download MP4 | YouTube |
|---|---|---|---|---|
| Free | No | — | No | No |
| Premium Light | Sì | 30 giorni (expires_at) |
Sì (solo se file MLTV) | No |
| Premium Full | Sì | 90 giorni (expires_at) se HLS; nessuna scadenza archivio se YouTube (expires_at nil) |
Sì solo con file Garage | Live → VOD nativo; verify collega youtube_video_id |
Storage policy (Recordings::StoragePolicy)
if youtube_destination? → temporary
elsif can_create_recordings? → retained
else → none
| Policy | Prefisso storage | expires_at (archivio) |
temp_expires_at |
|---|---|---|---|
temporary |
temporary_replays/... |
nil |
now + YOUTUBE_TEMP_REPLAY_RETENTION_HOURS (24–72, default 48) |
retained |
teams/... |
piano recording_days |
nil |
none |
— | — | — |
Archivio logico vs storage fisico
Dopo cleanup della copia temp, la partita resta in archivio MatchLiveTV. Cambia solo la sorgente:
replay_source |
Player | File Garage permanente |
|---|---|---|
youtube |
embed / link YouTube | no (MP4 temp già eliminato) |
matchlivetv |
player MP4 proprietario | sì |
none |
non playable | — |
available_in_archive? = ready e (YouTube URL/youtube_video_id oppure storage_key).
Clear/purge temporary: solo oggetti S3 video; mai soft-delete; mai azzerare youtube_video_id / metadata / score. Thumbnail preferibilmente conservata.
Flusso YouTube
Stop → StoragePolicy(temporary) → Finalize (expires_at nil, temp_expires_at)
→ Upload temporary_replays/... → ready
→ VerifyYoutubeReplayJob (backoff)
→ ok: youtube_video_id + youtube_verified_at → ClearTemporaryMediaJob
→ fail fino a max attempts: aspetta safety net
→ PurgeTemporaryMediaJob (cron): a temp_expires_at elimina file + log se non verificato
Funzionalità implementate
1. Registrazione automatica
Pipeline: Sessions::Stop → FinalizeSession → UploadJob → storage → PostProcessJob.
MediaMTX registra solo mentre il telefono pubblica (RTMP connesso).
2. Retention e purge
- Retained:
Recordings::PurgeExpiredJob/rails recordings:purge_expired(soft-delete + S3 + YouTube se collegato) - Temporary:
rails recordings:purge_temporary(solo storage)
3. Archivio Replay
- Web:
/clubs/:id/replays(gestione società) - Pubblico:
/replaycon filtro società/squadra - Stessa UX listing per YouTube e MLTV (differisce solo il player)
4. Riproduzione
- YouTube: embed su
/replay/:id - MLTV: MP4 via
/replay/:id/stream→ redirect/media/...→ Garage - Contatore visualizzazioni su ogni play (
view_count)
5. Visibilità
Pubblico / privato (unlisted) come prima. Sync privacy YouTube se youtube_video_id presente.
6. Eliminazione anticipata
Recordings::Delete manuale — resta distruttiva sul record (distinta dal cleanup temporary automatico).
7–8. KPI, email, thumbnail, download, views — invariati dove applicabili.
Config ENV
YOUTUBE_TEMP_REPLAY_RETENTION_HOURS=48
YOUTUBE_REPLAY_VERIFY_MAX_ATTEMPTS=12
YOUTUBE_REPLAY_VERIFY_BASE_INTERVAL_SECS=300
YOUTUBE_REPLAY_VERIFY_GRACE_SECS=120
REPLAY_STORAGE_ENDPOINT=http://garage:3900
REPLAY_STORAGE_BUCKET=matchlivetv-replays
...
Valori letti da MatchLiveTv (config/initializers/match_live_tv.rb).
Storage Garage
Senza endpoint S3: storage locale /recordings/replays/.
Dev / produzione Garage
Vedi script infra/scripts/setup_garage_*.sh come in precedenza.
Cron (produzione)
cd /opt/matchlivetv/infra
bash scripts/install_production_cron.sh
| Orario | Task |
|---|---|
| 03:00 | recordings:purge_expired — replay retained scaduti |
| 03:30 | recordings:purge_temporary — copie temp YouTube scadute |
| 08:00 | recordings:expiry_warnings — email avviso scadenza (7 gg) |
Audit duplicati storici
rails recordings:audit_youtube_duplicates # dry-run di default
Nessun cleanup automatico dei vecchi MP4 YouTube già in Garage.
API
GET /teams/:id/recordings— includereplay_source,youtube_watch_url,playback_url(null se youtube-only),download_enabledsolo constorage_keyGET /recordings/:id/download— richiede file GaragePOST /recordings/:id/publish_youtube— solo MatchLiveTV-only con file (non per live già YouTube)
Colonne DB (migration)
storage_policy(defaultretained)temp_expires_at,youtube_verified_at,local_media_purged_at
Rischi
- VOD YouTube in ritardo oltre retention temp → safety net elimina file; archivio resta se id collegato; altrimenti anomaly log.
- Thumb: se manca storage, poster YouTube da metadata / default.
- Record storici restano
storage_policy=retained.