# Modulo Replay — Match Live TV ## Panoramica Al termine di ogni diretta Premium, MediaMTX registra i segmenti. Sidekiq unisce i segmenti, genera thumbnail, carica su **Garage (S3-compatible)** e attiva i servizi di notifica, statistiche e (opzionale) republicazione YouTube. ## Piani e retention | Piano | Registrazione | Retention | Download MP4 | YouTube VOD | |-------|---------------|-----------|--------------|-------------| | Free | No | — | No | No | | Premium Light | Sì | 30 giorni | Sì | No | | Premium Full | Sì | 90 giorni | Sì | Sì (auto se diretta YouTube) | ## Funzionalità implementate ### 1. Registrazione automatica Pipeline: `Sessions::Stop` → `FinalizeSession` → `UploadJob` → storage. MediaMTX registra **solo mentre il telefono pubblica** (RTMP connesso). In pausa o con sola slate `alwaysAvailable` la registrazione è disattivata, così il replay non contiene minuti di schermo «Trasmissione in pausa». ### 2. Retention e purge `Recordings::PurgeExpiredJob` / `rails recordings:purge_expired` ### 3. Archivio Replay - Web: `/clubs/:id/replays` (gestione società) - Pubblico: `/replay` con filtro società/squadra - App mobile: **nessuna** UI replay (gestione solo da sito web) ### 4. Riproduzione - Player MP4: `/replay/:id` + stream `/replay/:id/stream` - Delivery: Rails verifica accesso → redirect 302 a `/media/...` (edge → Garage). Puma non proxya il file. - Contatore visualizzazioni su ogni play (`view_count`) ### 5. Visibilità - **Pubblico** (`public`): compare in `/replay`, indicizzabile, YouTube `public` - **Privato** (`unlisted`): non in catalogo pubblico, accessibile solo con link diretto, `noindex`, YouTube `unlisted` Modificabile dall’archivio società (`/clubs/:id/replays`). Il cambio privacy sincronizza automaticamente il VOD YouTube se presente (`Recordings::SyncYoutubePrivacyJob`). ### 6. Eliminazione anticipata `Recordings::Delete` — elimina video YouTube collegato, oggetti S3/Garage (MP4 + thumbnail) e soft-delete DB. Stesso comportamento alla scadenza retention (`PurgeExpiredJob` alle 03:00). ### 7. Dashboard KPI Replay disponibili, in scadenza (7 gg), spazio occupato, **visualizzazioni totali**. ### 8. Miglioramenti Premium | Feature | Descrizione | |---------|-------------| | **Email replay pronto** | A owner e membri società quando status → `ready` | | **Email scadenza** | 7 giorni prima di `expires_at` (`rails recordings:expiry_warnings`) | | **Thumbnail** | Frame ffmpeg, URL `/replay/:id/thumbnail` | | **Download MP4** | Premium, link presigned 15 min o `/replay/:id/download` | | **Statistiche views** | `view_count` su ogni replay | | **metadata JSONB** | `source_platform`, `auto_publish_youtube`, `ai: {}` per estensioni | | **YouTube VOD** | Premium Full: upload automatico se diretta era su YouTube; manuale da archivio | ## Storage ```env REPLAY_STORAGE_ENDPOINT=http://garage:3900 REPLAY_STORAGE_BUCKET=matchlivetv-replays REPLAY_STORAGE_ACCESS_KEY_ID=... REPLAY_STORAGE_SECRET_ACCESS_KEY=... REPLAY_DOWNLOAD_URL_TTL_SECONDS=900 ``` Senza endpoint S3: storage locale `/recordings/replays/`. ### Dev locale con Garage ```bash cd infra docker compose up -d # include garage bash scripts/setup_garage_replays.sh # layout, bucket, chiave → .env docker compose up -d rails sidekiq # Verifica pipeline (segmento finto → Garage) docker compose exec -T rails bundle exec rails replay:e2e_garage ``` ### Produzione (`/opt/matchlivetv`) ```bash cd infra bash scripts/setup_garage_production.sh # garage.prod.toml, bucket, chiavi → .env # Rails/Sidekiq usano REPLAY_STORAGE_ENDPOINT=http://garage:3900 (rete Docker) ``` **Credenziali in `.env`:** non compilarle a mano in `.env.example`. Lo script scrive in `infra/.env`: - `REPLAY_STORAGE_ACCESS_KEY_ID` = Key ID Garage (es. `GK...`) - `REPLAY_STORAGE_SECRET_ACCESS_KEY` = Secret mostrato una sola volta alla creazione chiave ## Cron (produzione) Installazione sul server: ```bash cd /opt/matchlivetv/infra bash scripts/install_production_cron.sh ``` Job attivi (utente `eminux`): | Orario | Task | |--------|------| | 03:00 | `recordings:purge_expired` — elimina replay scaduti (DB + Garage) | | 08:00 | `recordings:expiry_warnings` — email avviso scadenza (7 gg) | Log: `${MATCHLIVETV_VIDEOS_ROOT}/log/cron-replay.log` (es. `/media/videos/matchlivetv/log/cron-replay.log`) ### Capacità Garage All’installazione, `setup_garage_production.sh` assegna ~**(disco libero − 20 GB)** al nodo. Per ridimensionare dopo: ```bash bash scripts/garage_set_capacity.sh 85G # oppure senza argomento: calcolo automatico ``` ## Piattaforme live e replay | Origine diretta | Copia Garage/S3 | Player sito `/replay/:id` | YouTube VOD | |-----------------|-----------------|----------------------------|-------------| | `matchlivetv` | Sì (Premium) | MP4 via redirect `/media/` → Garage | Manuale (Premium Full) | | `youtube` | Sì (Premium) | MP4 via redirect `/media/` → Garage | Auto se Premium Full + diretta YouTube | Se il file S3 non è disponibile ma esiste `youtube_video_id`, la pagina replay mostra embed YouTube. ## Retention e abbonamento - `expires_at` viene impostato alla fine diretta: **30 giorni** (Premium Light), **90 giorni** (Premium Full) - `expires_at` **non cambia** al cambio piano successivo - **Nuove registrazioni** richiedono abbonamento attivo (`can_create_recordings?`) - **Archivio esistente** resta accessibile fino a `expires_at` anche se l’abbonamento scade (`can_access_recordings?`) ## API mobile - `GET /teams/:id/recordings` — lista con thumbnail, views, download_enabled - `GET /recordings/:id/download` — URL download temporaneo - `POST /recordings/:id/publish_youtube` — coda republicazione YouTube ## Estensioni future (metadata.ai) Campo `metadata["ai"]` predisposto per: - highlight automatici - trascrizioni - articoli generati - clip / Shorts Esempio aggiornamento via API: ```json { "recording": { "metadata": { "ai": { "transcript_status": "queued" } } } } } ```