5.7 KiB
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), JS vanilla senza build (ADR 0007), ffmpeg (ADR 0004).
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) |
| position | number | ordinamento manuale |
Vincoli: nome univoco tra fratelli; una categoria o ha figli o ha filmati, mai entrambi (ADR 0010); 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; salvataggio: ADR 0006)
| 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.
Navigazione (F-04): lista figli di parent=X (o radici) + filmati con category=X e status=ready.
Riproduzione e ripresa (F-05, F-06):
<video src=/api/files/videos/ID/FILE preload="metadata">.loadedmetadata: se esiste unprogressdell'utente loggato non completato,currentTime = position_s(se mancano meno di ~5 s alla fine, riparti da 0).- Salvataggio: all'evento
pause, ogni ~10 s in riproduzione, suvisibilitychange/pagehide(fetch conkeepalive). Scritture limitate per non martellare il DB. ended→completed=true,position_s=0.- 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.
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). 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.