Tabellone, badge e brand sono ricodificati nel flusso _air via OverlayRelay; sync punteggio HTTP, volume condiviso rails/sidekiq, qualità 720p migliorata e badge CONNECTING in ripresa da pausa. Co-authored-by: Cursor <cursoragent@cursor.com>
136 lines
4.4 KiB
Markdown
136 lines
4.4 KiB
Markdown
# 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: schermata Replay
|
||
|
||
### 4. Riproduzione
|
||
- Player MP4: `/replay/:id` + stream `/replay/:id/stream`
|
||
- Contatore visualizzazioni su ogni play (`view_count`)
|
||
|
||
### 5. Visibilità
|
||
Pubblico / Privato (unlisted), modificabile dall’admin società.
|
||
|
||
### 6. Eliminazione anticipata
|
||
`Recordings::Delete` — DB + video + thumbnail su storage.
|
||
|
||
### 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: `/opt/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
|
||
```
|
||
|
||
## 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" } } } } }
|
||
```
|