Files
videoteca/docs/02-architettura.md
T

5.7 KiB
Raw Blame History

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):

  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.

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.