554 lines
12 KiB
Markdown
554 lines
12 KiB
Markdown
|
||
📌 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 dell’utente
|
||
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 l’hash 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 l’array 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 dell’utente |
|
||
| 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 l’hai implementato)
|
||
|
||
È la parte che rimuove l’entry 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 dell’utente.
|
||
|
||
---
|
||
|
||
📁 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 l’ID da idsSet
|
||
|
||
---
|
||
|
||
🔵 Caso B — File invariato (fast-skip)
|
||
|
||
1. FAST-SIZE-SKIP
|
||
Se la size è identica → file invariato.
|
||
|
||
2. FAST-HASH-SKIP
|
||
Se l’hash veloce è identico → invariato.
|
||
|
||
In entrambi i casi:
|
||
|
||
- Aggiorna solo path/mtime/hash nel DB
|
||
- Rimuove l’ID 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 l’ID 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.
|