server_photo_s35js/scanPhoto_how_works.md
2026-07-18 13:51:20 +02:00

554 lines
12 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.

📌 1. Avvio dello scan
- Parte con un log: "Inizio scan globale per user=X".
- Calcola il percorso della cartella photos/<user>/original.
- Conta velocemente quanti file supportati esistono (con countFilesFast).
- Se non ci sono file → termina.
📊 2. Gestione dello stato di avanzamento
Durante lo scan:
- Tiene traccia di:
- CURRENT = quanti file ha già processato
- TOTAL_FILES = quanti file totali ci sono
- Aggiorna un file JSON pubblico:
public/photos/scan_status.json
Questo file contiene:
- current
- total
- percent
- elapsed
- eta
Serve per mostrare una barra di avanzamento in tempo reale.
📁 3. Scansione delle cartelle dellutente
Per ogni sottocartella dentro original/:
- Chiama scanSingleFolder(user, cartella, absCartella).
Dentro questa funzione succede il grosso del lavoro.
🔍 4. Per ogni file trovato:
scanCartella() restituisce oggetti con:
- id (deterministico)
- name
- relPath
- absPath
- ext
- stat (size, mtime, ecc.)
- path normalizzato
user=Fabio cartella=2017Irlanda19-29ago relPath=VID_20260317_115145.mp4 id=51a7e1861de8da5147bbbc35d70a0128fcd2a0451c7121a8bf2412cb2903d469
Per ogni file:
🟦 4.1 Recupera eventuale record esistente dal DB
`js
prev = await db("photos").where({ id }).first();
`
🟩 4.2 Se il path è cambiato → considerato nuovo
`js
if (prev && prev.path !== f.path) prev = null;
`
🟢 4.3 Se è un nuovo file
- Log dettagliato
- Chiama processFile() per generare metadati, thumbnails, EXIF, ecc.
- Inserisce nel DB
- Registra un evento in photo_changes
- Aggiunge a newFiles
🔵 4.4 Fast-skip (file invariato)
Due livelli:
1. FAST-SIZE-SKIP
Se la size è identica → aggiorna solo path/mtime/hash.
2. FAST-HASH-SKIP
Se lhash veloce (sha256(size-mtime)) è identico → aggiorna solo path/mtime.
🟠 4.5 File modificato
Se non passa i fast-skip:
- Rielabora il file con processFile()
- Aggiorna il DB con .insert().onConflict().merge()
- Registra evento updated
- Aggiunge a newFiles
---
🗑️ 5. Gestione degli orfani
Alla fine della cartella:
- Tutti gli ID presenti nel DB ma non trovati sul disco vengono:
- rimossi dal DB
- rimossi i thumbnails
- loggati come cancellati
---
🌐 6. Invio al server remoto (opzionale)
Se SENDPHOTOS=true e BASEURL è configurato:
- Ogni nuovo file viene inviato via postWithAuth() al server remoto.
Se non configurato:
- Logga [NO SEND].
---
🟣 7. Fine scan
Logga il tempo totale e restituisce larray newFiles.
---
🧩 In sintesi
scanPhoto.js è un motore di sincronizzazione locale → database → server remoto.
Fa tutto questo:
| Funzione | Descrizione |
|---------|-------------|
| Scansione ricorsiva | Trova tutti i file supportati nelle cartelle dellutente |
| Confronto con DB | Determina se un file è nuovo, modificato o invariato |
| Fast-skip | Evita elaborazioni inutili usando size e hash veloce |
| Elaborazione file | Chiama processFile() per EXIF, thumbnails, metadati |
| Aggiornamento DB | Inserisce/aggiorna record in photos |
| Rilevamento orfani | Cancella dal DB file non più presenti sul disco |
| Log avanzamento | Scrive un file JSON con percentuale, ETA, ecc. |
| Invio remoto | Invia i nuovi file a un server remoto (se abilitato) |
_____
functio countFilesFast(rootDir)
conta i file nelle cartelle photos/<user>/original
function formatTime(ms)
trasforma ms in h:m:s
---
async function scanPhoto(dir, userName, db)
const photosRoot
è il path assoluto <app>/public/photos
const userDir
è il path assoluto <app>/public/photos/<user>/original
function updateStatusFile()
fa l'aggiornamento dello stato di scansione dei file
function scanSingleFolder(user, cartella, absCartella)
user: Fabio
cartella: 2017Irlanda19-29ago
absCartella: /home/nvme/dockerdata/prove/s15j/public/photos/Fabio/original/2017Irlanda19-29ago
_____
🧩 Cosa fa esattamente questa riga
```js
const { deleteThumbsById, deleteFromDB } = createCleanupFunctions(db);
```
---
Quella riga fa una cosa molto semplice… ma potentissima.
E soprattutto è il cuore della pulizia degli orfani.
📌 0. definita da
```
const createCleanupFunctions = require('./orphanCleanup');
```
🔍 1. Chiama createCleanupFunctions(db)
createCleanupFunctions è una funzione che riceve il database (db) e restituisce un oggetto contenente due funzioni:
- deleteThumbsById(id)
- deleteFromDB(id, user)
In pratica, è un factory: crea funzioni già configurate con il DB.
---
📌 2. Cosa fanno le due funzioni restituite
---
🗑️ deleteThumbsById(id)
Cancella i thumbnails associati a una foto:
- thub1
- thub2
- eventuali altre miniature generate
Serve quando un file non esiste più sul disco → quindi i suoi thumbnails diventano spazzatura.
---
🗄️ deleteFromDB(id, user)
Cancella dal database:
- la riga nella tabella photos
- eventuali metadati collegati
- eventuali record in photo_changes (dipende da come lhai implementato)
È la parte che rimuove lentry dal DB quando il file è sparito dal filesystem.
---
🔴 3. Perché serve?
Durante lo scan, alla fine di ogni cartella, hai:
`js
for (const orphanId of idsSet) {
log( 🔴 Cancellato ${orphanId});
await deleteThumbsById(orphanId);
await deleteFromDB(orphanId, user);
}
`
idsSet contiene gli ID presenti nel DB ma non trovati sul disco.
Quindi:
- deleteThumbsById() → pulisce i file fisici
- deleteFromDB() → pulisce il database
È la logica che mantiene il sistema coerente.
---
🧠 In sintesi
Quella riga:
`js
const { deleteThumbsById, deleteFromDB } = createCleanupFunctions(db);
`Eccoti una spiegazione cristallina e completa di cosa fa:
`js
async function scanSingleFolder(user, cartella, absCartella)
`
È una funzione fondamentale: gestisce tutta la sincronizzazione tra filesystem e database per una singola cartella dellutente.
---
📁 Obiettivo della funzione
Scansionare una cartella (es. DCIM, Vacanze2023, WhatsApp Images) e:
1. Trovare i file presenti sul disco
2. Confrontarli con il database
3. Capire cosa è nuovo, cosa è modificato, cosa è invariato
4. Aggiornare il DB
5. Elaborare i nuovi file (EXIF, thumbnails, metadati)
6. Rimuovere dal DB i file orfani (presenti nel DB ma non più sul disco)
È il cuore della logica di sincronizzazione.
---
🧠 Funzionamento passo per passo
1⃣ Log di inizio
`js
log(📁 Inizio cartella: ${cartella});
`
---
2⃣ Recupera dal DB tutti gli ID già conosciuti
`js
const rows = await db('photos')
.where({ user, cartella })
.select('id');
const idsSet = new Set(rows.map(r => r.id));
`
idsSet serve per capire quali file non verranno trovati sul disco → saranno orfani.
---
3⃣ Itera su tutti i file trovati sul disco
`js
for await (const f of scanCartella(...)) {
`
scanCartella() restituisce per ogni file:
- id deterministico
- name
- path normalizzato
- relPath, absPath
- ext
- stat (size, mtime, ecc.)
---
4⃣ Aggiorna lo stato di avanzamento
Incrementa CURRENT e aggiorna il file JSON con percentuale, ETA, ecc.
---
5⃣ Recupera eventuale record esistente dal DB
`js
let prev = await db("photos")
.select(...)
.where({ id })
.first();
`
---
6⃣ Calcola il fast-hash
`js
const fastHash = sha256(${st.size}-${st.mtimeMs});
`
Serve per capire velocemente se il file è cambiato.
---
🔍 7⃣ Logica decisionale per ogni file
🟢 Caso A — File nuovo
Se prev è null:
- Log
- Chiama processFile() per estrarre metadati, EXIF, thumbnails
- Inserisce nel DB
- Registra evento added
- Aggiunge a newFiles
- Rimuove lID da idsSet
---
🔵 Caso B — File invariato (fast-skip)
1. FAST-SIZE-SKIP
Se la size è identica → file invariato.
2. FAST-HASH-SKIP
Se lhash veloce è identico → invariato.
In entrambi i casi:
- Aggiorna solo path/mtime/hash nel DB
- Rimuove lID da idsSet
---
🟠 Caso C — File modificato
Se non passa i fast-skip:
- Log
- Chiama processFile()
- Aggiorna il DB con .insert().onConflict().merge()
- Registra evento updated
- Aggiunge a newFiles
- Rimuove lID da idsSet
---
🔴 8⃣ Gestione degli orfani
Alla fine della cartella:
`js
for (const orphanId of idsSet) {
await deleteThumbsById(orphanId);
await deleteFromDB(orphanId, user);
}
`
Questi sono file che:
- esistono nel DB
- non sono stati trovati sul disco
Quindi vengono eliminati.
---
🧩 In sintesi
scanSingleFolder():
| Step | Cosa fa |
|------|---------|
| 1 | Carica dal DB gli ID della cartella |
| 2 | Scansiona i file reali sul disco |
| 3 | Per ogni file decide: nuovo, modificato, invariato |
| 4 | Elabora i nuovi/modificati con processFile() |
| 5 | Aggiorna o inserisce nel DB |
| 6 | Registra gli eventi in photo_changes |
| 7 | Rimuove gli orfani (DB → disco) |
| 8 | Restituisce i nuovi file |
È la funzione che mantiene coerenza totale tra filesystem e database.
---
Se vuoi, posso anche:
- disegnarti un diagramma di flusso
- riscrivere la funzione in modo più modulare
- aggiungere logging più leggibile
- ottimizzare la parte di fast-skip
- aggiungere batching per il DB
Dimmi tu come vuoi procedere.
fa questo:
- chiama una funzione factory
- che crea due funzioni già configurate con il DB
- che servono per cancellare foto orfane (thumbnails + DB)
È un modo elegante per incapsulare la logica di cleanup.
___
Eccoti la spiegazione chiara, completa e tecnica di cosa fa scanCartella.js e cosa restituisce.
---
📌 Cosè scanCartella?
È una funzione asincrona generator (async function*) che:
- scansiona ricorsivamente una cartella sul filesystem
- filtra solo i file con estensioni supportate
- calcola un ID deterministico per ogni file
- raccoglie informazioni minime (stat, path, nome, estensione…)
- NON elabora il file (niente EXIF, niente thumbnails)
- NON scrive sul database
👉 Il suo unico scopo è enumerare i file reali presenti sul disco e restituire un oggetto per ciascuno.
È volutamente “leggera”: tutta la logica pesante (processFile, DB, confronto) è in scanPhoto.
---
🧠 Cosa fa, passo per passo
1⃣ Legge la cartella ricorsivamente
La funzione interna walk():
- usa fsp.readdir() con { withFileTypes: true }
- entra nelle sottocartelle
- ignora file non supportati
---
2⃣ Filtra per estensioni supportate
`js
if (!SUPPORTED_EXTS.has(ext)) continue;
`
---
3⃣ Costruisce il percorso relativo
`js
const fileRelPath = relPath ? ${relPath}/${e.name} : e.name;
`
---
4⃣ Genera un ID deterministico
`js
const id = sha256(${userName}/${cartella}/${fileRelPath});
`
Questo ID è:
- stabile
- identico tra scansioni
- basato su user/cartella/percorso relativo
È fondamentale per confrontare file reali ↔ DB.
---
5⃣ Legge lo stat del file
`js
st = await fsp.stat(absPath);
`
Serve per:
- size
- mtimeMs
- fast-hash
- confronto rapido in scanPhoto
---
6⃣ Logga il file trovato
`js
log(📂 scanCartella → user=${userName} cartella=${cartella} relPath=${fileRelPath} id=${id});
`
---
7⃣ Restituisce un oggetto per ogni file
Usa yield per restituire uno stream di risultati, non un array.
---
📤 Cosa restituisce esattamente?
Per ogni file valido, restituisce un oggetto:
`js
{
id, // ID deterministico
user: userName, // nome utente
cartella, // nome cartella (es. DCIM)
name: e.name, // nome file
relPath: fileRelPath, // percorso relativo dentro la cartella
absPath, // percorso assoluto sul disco
ext, // estensione (es. .jpg)
stat: st, // fs.stat() → size, mtimeMs, ecc.
path: /photos/${userName}/original/${cartella}/${fileRelPath}
}
`
---
🧩 In sintesi
✔️ Cosa fa
- Scansiona ricorsivamente una cartella
- Filtra solo i file supportati
- Genera ID deterministici
- Legge stat del file
- Costruisce percorsi relativi/assoluti
- Restituisce un oggetto per ogni file
❌ Cosa NON fa
- Non elabora il file (niente EXIF, niente thumbnails)
- Non scrive nel DB
- Non decide se il file è nuovo/modificato
- Non cancella orfani
---
🎯 A cosa serve?
È il motore di enumerazione dei file.
scanPhoto() usa i dati prodotti da scanCartella() per:
- confrontare con il DB
- decidere se un file è nuovo/modificato/invariato
- elaborarlo con processFile()
- aggiornare il DB
- eliminare orfani
---
Se vuoi, posso anche:
- disegnarti un diagramma di flusso
- riscrivere scanCartella in modo più modulare
- aggiungere caching o parallelizzazione
- aggiungere controlli per file corrotti
Dimmi tu come vuoi procedere.