7.2 KiB
scanPhotosUser(userName, db) │ ├── individua: │ photos//original/ │ ├── legge le entry dentro original/ │ ├── prende SOLO le directory │ ├── countFilesUser(userDir) │ └── ricorsivamente guarda tutti i file │ └── conta solo quelli con estensione in SUPPORTED_EXTS │ └── per ogni cartella: scanPhoto(cartella, userName, db, ...) │ └── QUI viene probabilmente deciso cosa fare col singolo file
scanPhotosUser │ │ per ogni cartella ▼ scanPhoto() │ ▼ scanPhotoCartella() │ └── QUI probabilmente avviene la decisione sul file
Quindi scanPhoto(): costruisce il percorso: const absCartella = path.join(userDir, cartella); passa la cartella a scanPhotoCartella(). scanPhotoCartella() riempie newFiles. solo dopo, scanPhoto() manda al server i file presenti in newFiles:
scanCartella.js non decide se la foto è già stata scansionata: decide solo quali file sono candidati alla scansione.
cartella ↓ walk() ricorsivo ↓ è directory? ├─ sì → entra dentro └─ no ↓ estensione supportata? ├─ no → SKIP └─ sì ↓ crea ID deterministico ↓ stat() del file ↓ yield metadata
Il valore di SUPPORTED_EXTS è quindi importante. Lo trovi in: api_v1/config.js La cosa ancora più interessante: l'ID Per ogni file accettato crea:
const id = sha256(${userName}/${cartella}/${fileRelPath});
Quindi l'ID non dipende dal contenuto della foto. Dipende da: user + cartella + percorso relativo
scanPhotoUser ↓ scanPhoto ↓ scanPhotoCartella ↓ scanCartella ↓ per ogni file con estensione supportata ↓ scanPhotoSingle(...) ↓ QUI viene probabilmente deciso: ├── già presente → non fare certe operazioni ├── nuovo → processa └── modificato → eventualmente aggiorna
Quindi non è scanPhotoCartella a decidere se processare o meno la foto.
È scanPhotoSingle.
4. Ma c'è un altro meccanismo importante: gli orfani
Dopo scanPhotoSingle trovi:
idsSet.delete(f.id);
Questo significa:
"Ho trovato fisicamente questo file durante la scansione, quindi il suo ID non è un orfano."
Alla fine:
for (const orphanId of idsSet) {
log(🔴 Orfano: ${orphanId});
await deleteThumbsById(orphanId);
await deleteFromDB(orphanId, user);
}
DB all'inizio │ ├── A ├── B └── C ↓ scansione filesystem ↓ trovo A → idsSet.delete(A) trovo C → idsSet.delete(C) ↓ rimane B ↓ B non esiste più sul filesystem ↓ B = ORFANO ↓ cancella thumbnail + DB
Una foto viene presa in considerazione se:
si trova dentro una cartella sotto original;
è un file, non una directory;
la sua estensione è presente in SUPPORTED_EXTS.
Poi viene generato un ID:
sha256(${userName}/${cartella}/${fileRelPath})
e il file viene passato a:
scanPhotoSingle(...)
La decisione è tutta qui
Quando trova un file, fa:
let prev = await db('photos')
.select('id', 'size_bytes', 'mtimeMs', 'fast_hash', 'path')
.where({ id })
.first();
Quindi cerca nel DB una foto con lo stesso ID.
L'ID, come abbiamo visto prima, è:
sha256(${userName}/${cartella}/${fileRelPath})
Quindi normalmente identifica quel preciso percorso del file.
Poi calcola:
const fastHash = sha256(${st.size}-${st.mtimeMs});
Quindi il contenuto non viene hashato: il "fast hash" dipende solo da:
dimensione file + data modifica
Le 3 situazioni
- Foto nuova Se: if (!prev) cioè non esiste nel DB un record con quell'ID, fa: const meta = await processFile(...) Questa è la parte che legge/elabora veramente la foto. Poi la inserisce nel DB: await db('photos').insert(...) e soprattutto: newFiles.push(meta); Quindi una foto nuova viene processata e considerata newFile.
- Foto già presente e invariata Questa è la situazione che probabilmente ti interessava: if (prev.fast_hash === fastHash) { In questo caso NON chiama processFile(). Fa solamente: await db('photos') .where({ id }) .update({ path: f.path, size_bytes: st.size, mtimeMs: st.mtimeMs, fast_hash: fastHash, updated_at: new Date().toISOString(), });
return null;
Quindi:
Se l'ID esiste e size + mtimeMs sono uguali, la foto viene saltata.
Non viene riletta, non vengono rigenerate thumbnail, non viene ricalcolato GPS/location, ecc.
Questo è il vero "skip".
3. Foto modificata
Se esiste prev, ma:
prev.fast_hash !== fastHash
allora:
log(${prefix} 🟠 Modificato: ${fileName});
e viene nuovamente chiamato:
const meta = await processFile(...)
Quindi la foto viene riprocessata completamente.
C'è però un caso speciale: path cambiato
Prima del controllo fast_hash c'è:
if (prev && prev.path !== f.path) {
log(${prefix} 🟢 Nuovo/Modificato (path changed): ${fileName});
prev = null;
}
Questo significa:
se trovo lo stesso id ma il path nel DB è diverso da quello attuale, tratto il file come nuovo.
E quindi finisce qui:
if (!prev) {
...
await processFile(...)
...
await db('photos').insert(...)
}
Ma qui vedo una possibile anomalia
Se prev esiste già nel DB e fai:
prev = null;
poi fai:
await db('photos').insert({
id: meta.id,
...
});
Se id è una chiave unica, potresti avere un errore di duplicate key invece di un vero aggiornamento.
Però per capire se questo caso è realmente possibile bisogna vedere come viene costruito f.path e come viene gestito il DB.
TROVO FILE
│
▼
estensione supportata?
│ │
NO SI
│ │
SKIP ▼
calcolo ID
│
▼
cerco ID nel DB
│
┌────────────┴────────────┐
│ │
non esiste esiste
│ │
▼ ▼
processFile() path diverso?
│ │ │
│ SI NO
│ │ │
│ ▼ ▼
│ prev=null confronto
│ fastHash
│ │ │
│ UGUALE DIVERSO
│ │ │
│ ▼ ▼
│ SKIP processFile()
│ │
└──────────────────────────────────────┘