Files
videoteca/docs/02-architettura.md

92 lines
5.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Architettura
## Vista d'insieme
```
Browser ──HTTP──> PocketBase (Raspberry Pi, rete di casa, :8090; ADR 0013)
│ ├─ / → pb_public/ (frontend statico, ADR 0007)
│ ├─ /api/... → REST: categories, videos, progress
│ ├─ /api/files/ → video (Range supportato)
│ └─ pb_hooks/ → logica di ingest (ADR 0004)
└─ <video> nativo ffmpeg (processo esterno) ← ricodifica
```
Stack: PocketBase ([ADR 0001](adr/0001-backend-pocketbase.md)), JS vanilla senza build ([ADR 0007](adr/0007-frontend-vanilla.md)), ffmpeg ([ADR 0004](adr/0004-pipeline-di-ingest.md)).
## Struttura del repository
```
videoteca/
├─ CLAUDE.md README.md CHANGELOG.md
├─ docs/ (questa documentazione)
├─ pb_public/ frontend (index.html, app.js, style.css)
├─ pb_hooks/ hook JS di PocketBase
├─ pb_migrations/ migrazioni schema (committate)
├─ scripts/ ingest/utility (se ADR 0004 lo richiede)
├─ pocketbase binario (non committato)
└─ pb_data/ DB + file (NON committato, .gitignore)
```
## Modello dati
**categories**
| Campo | Tipo | Note |
|---|---|---|
| name | text, obbligatorio | |
| parent | relation → categories, opzionale | `null` = categoria radice ([ADR 0005](adr/0005-categorie-ad-albero.md)) |
| position | number | ordinamento manuale |
Vincoli: nome univoco tra fratelli; una categoria o ha figli o ha filmati, mai entrambi ([ADR 0010](adr/0010-filmati-solo-nelle-foglie.md)); no cicli (parent ≠ sé stesso né un discendente); eliminazione di una categoria con figli/filmati **bloccata** (non a cascata).
**videos**
| Campo | Tipo | Note |
|---|---|---|
| title | text | |
| category | relation → categories, obbligatorio | |
| file | file (mp4), `maxSize` alzato | default PocketBase ~5 MB, va configurato |
| thumb | file (immagine), opzionale | |
| status | select: processing / ready / error | |
| error_msg | text | diagnostica in caso di errore |
| duration_s, size_bytes, original_size_bytes, codec | number/text | statistiche, utili per misurare il risparmio |
**progress** (un record per utente e filmato, [ADR 0008](adr/0008-progresso-per-utente.md); salvataggio: [ADR 0006](adr/0006-posizione-di-riproduzione.md))
| Campo | Tipo | Note |
|---|---|---|
| user | relation → utenti | eliminazione a cascata con l'utente; rules: ognuno vede solo i propri record |
| video | relation → videos, univoco insieme a `user` | eliminazione a cascata col filmato |
| position_s | number | |
| completed | bool | |
## Flussi
**Ingest** (F-02, F-03): upload → record `status=processing` → ricodifica ffmpeg → file sostituito, statistiche scritte, `status=ready`. In caso di errore: `status=error` + `error_msg`, file originale conservato. Meccanica esatta: [ADR 0004](adr/0004-pipeline-di-ingest.md).
**Navigazione** (F-04): lista figli di `parent=X` (o radici) + filmati con `category=X` e `status=ready`.
**Riproduzione e ripresa** (F-05, F-06):
1. `<video src=/api/files/videos/ID/FILE preload="metadata">`.
2. `loadedmetadata`: se esiste un `progress` dell'utente loggato non completato, `currentTime = position_s` (se mancano meno di ~5 s alla fine, riparti da 0).
3. Salvataggio: all'evento `pause`, ogni ~10 s in riproduzione, su `visibilitychange`/`pagehide` (fetch con `keepalive`). Scritture limitate per non martellare il DB.
4. `ended` → `completed=true`, `position_s=0`.
5. Controlli: controlli nativi + pulsanti −10 s/+10 s + tastiera (spazio, ←/→).
Il seek funziona perché PocketBase serve i file con `http.ServeContent` (Range supportato, verificato nel codice sorgente il 2026-10-11) e perché i file mp4 sono prodotti con `-movflags +faststart` (indice `moov` all'inizio).
## Casi limite da coprire (checklist per i moduli)
- Upload: file non video, mp4 corrotto, estensione falsa, file enorme, nome con caratteri speciali, upload duplicato, disco pieno.
- Transcodifica: ffmpeg assente, processo killato, riavvio di PocketBase durante l'elaborazione (record bloccati in `processing` → riconciliazione all'avvio), file già in codec target, nessuna traccia audio, rotazione/metadata, risultato più grande dell'originale (tenere l'originale).
- Account: password dimenticata (la reimposta l'admin dal pannello; la comunica di persona o con un messaggio, fuori dall'app), utente eliminato mentre ha una sessione aperta, cambio password con sessioni già attive, utente che modifica il profilo di un altro (rifiutato dalle rules), password debole.
- Categorie: filmato in una categoria con figli, sottocategoria sotto una categoria con filmati, spostamento di filmati o rami che viola la regola foglie, cicli, eliminazione con contenuti, nomi duplicati, spostamento di un ramo.
- Player: filmato eliminato mentre è aperto, codec non supportato dal browser (messaggio chiaro), due schede sullo stesso filmato (vince l'ultima scrittura), posizione oltre la durata.
- Dati: backup/ripristino di `pb_data/`, migrazioni su dati esistenti.
## Esecuzione
Da compilare in M0/M1 (comandi di avvio, variabili, percorso ffmpeg). Unico posto in cui vivono: qui. Creazione del superuser e pannello admin: [ADR 0012](adr/0012-pannello-admin-proprio.md).
## Sicurezza (rete di casa)
Il server gira sul Raspberry Pi ed è in ascolto sulla rete locale (non più solo su 127.0.0.1), non esposto a Internet: niente port forwarding sul router. Le API rules sono restrittive già dall'MVP: lettura solo da utenti loggati, scrittura solo al superuser ([ADR 0013](adr/0013-accesso-su-rete-di-casa.md)). Rischio: su HTTP semplice le password viaggiano in chiaro sulla rete Wi-Fi di casa (R-08, Q-16). Esposizione a Internet: fase M6 in [03-roadmap.md](03-roadmap.md).