// scanner/scanCartella.js const path = require('path'); const fsp = require('fs/promises'); const processFile = require('./processFile'); const { sha256 } = require('./utils'); const { SUPPORTED_EXTS } = require('../config'); /** * Scansiona ricorsivamente una cartella e ritorna SOLO i cambiamenti * (nuovi/modificati) rispetto a previousIndex (mappa { id: meta }). * * @param {string} userName - Nome utente (es. "Fabio" o "Common") * @param {string} cartella - Nome della cartella logica sotto "original" (es. "2017Irlanda19-29ago") * @param {string} absCartella - Percorso assoluto della cartella da scansionare (p.es. .../photos//original/) * @param {Object} previousIndex- Mappa dell'indice precedente: { : meta } * @returns {Promise>} changes - Lista dei soli meta cambiati o nuovi */ async function scanCartella(userName, cartella, absCartella, previousIndex) { const changes = []; async function walk(currentAbs, relPath = '') { let entries = []; try { entries = await fsp.readdir(currentAbs, { withFileTypes: true }); } catch (err) { // Cartella non leggibile: logga e prosegui (non bloccare la scansione globale) console.error(`[SCAN] Impossibile leggere: ${currentAbs} - ${err.message}`); return; } for (const e of entries) { const absPath = path.join(currentAbs, e.name); if (e.isDirectory()) { await walk(absPath, path.join(relPath, e.name)); continue; } // Filtra estensioni non supportate const ext = path.extname(e.name).toLowerCase(); if (!SUPPORTED_EXTS.has(ext)) continue; // Percorso relativo POSIX (usato per mantenere slash '/') const fileRelPath = relPath ? `${relPath}/${e.name}` : e.name; // ID deterministico: user/cartella/relPath const id = sha256(`${userName}/${cartella}/${fileRelPath}`); // Confronto con indice precedente per saltare "unchanged" let st; try { st = await fsp.stat(absPath); } catch (err) { // File sparito o non accessibile: salta console.warn(`[SCAN] stat fallita: ${absPath} - ${err.message}`); continue; } const prev = previousIndex[id]; const unchanged = prev && prev.size_bytes === st.size && prev.mtimeMs === st.mtimeMs; if (unchanged) continue; // nulla da aggiornare // Estrae metadati (EXIF, mime, dimensioni, thumbs, ecc.) const meta = await processFile( userName, cartella, fileRelPath, absPath, ext, st ); // ID sempre presente meta.id = id; // 🔧 Canonicalizza SEMPRE il path pubblicato nell’index: // /photos//original// { // Normalizza separatori e rimuove eventuali leading-slash multipli const relPosix = String(fileRelPath).replace(/\\/g, '/').replace(/^\/+/, ''); // Costruisci il path canonico e comprimi eventuali '//' in '/' const canonical = `/photos/${userName}/original/${cartella}/${relPosix}`.replace(/\/+/g, '/'); meta.path = canonical; // NB: thub1/thub2 rimangono quelli prodotti da processFile (ok per immagini e video) } changes.push(meta); } } await walk(absCartella); return changes; } module.exports = scanCartella;