249 lines
7.2 KiB
Markdown
249 lines
7.2 KiB
Markdown
scanPhotosUser(userName, db)
|
|
│
|
|
├── individua:
|
|
│ photos/<user>/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
|
|
1. 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.
|
|
2. 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()
|
|
│ │
|
|
└──────────────────────────────────────┘
|