photo_server_json_con_aves22/scanPhoto_how_works.md
2026-04-18 20:14:42 +02:00

12 KiB
Raw Permalink Blame History

📌 1. Avvio dello scan

  • Parte con un log: "Inizio scan globale per user=X".
  • Calcola il percorso della cartella photos//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//original

function formatTime(ms) trasforma ms in hⓂ️s


async function scanPhoto(dir, userName, db)

const photosRoot è il path assoluto /public/photos

const userDir è il path assoluto /public/photos//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

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.