# 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`) ```text 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 ```text 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: `/replay` con 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 ```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) ```bash 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 ```bash rails recordings:audit_youtube_duplicates # dry-run di default ``` Nessun cleanup automatico dei vecchi MP4 YouTube già in Garage. ## API - `GET /teams/:id/recordings` — include `replay_source`, `youtube_watch_url`, `playback_url` (null se youtube-only), `download_enabled` solo con `storage_key` - `GET /recordings/:id/download` — richiede file Garage - `POST /recordings/:id/publish_youtube` — solo MatchLiveTV-only con file (non per live già YouTube) ## Colonne DB (migration) - `storage_policy` (default `retained`) - `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`.