// api_v1/scanner/scanPhotosUser.js const path = require('path'); const fsp = require('fs/promises'); const scanPhoto = require('./scanPhoto'); const { log } = require('./logger'); const { WEB_ROOT, SUPPORTED_EXTS } = require('../config'); // --------------------------------------------------------- // ETA CALCULATOR // --------------------------------------------------------- function computeETA(startTime, current, total) { if (current === 0) return "calcolo..."; const elapsed = (Date.now() - startTime) / 1000; // sec const rate = current / elapsed; const remaining = (total - current) / rate; const m = Math.floor(remaining / 60); const s = Math.floor(remaining % 60); return `${m}m ${s}s`; } // --------------------------------------------------------- // PRIMA PASSATA: conta TUTTI i file reali (ricorsiva) // --------------------------------------------------------- async function countFilesUser(rootDir) { let count = 0; async function walk(dir) { let entries = []; try { entries = await fsp.readdir(dir, { withFileTypes: true }); } catch { return; } for (const e of entries) { const abs = path.join(dir, e.name); if (e.isDirectory()) { await walk(abs); } else { const ext = path.extname(e.name).toLowerCase(); if (SUPPORTED_EXTS.has(ext)) { count++; } } } } await walk(rootDir); return count; } // --------------------------------------------------------- // SECONDA PASSATA: scansiona SOLO le cartelle vere // --------------------------------------------------------- async function scanPhotosUser(userName, db) { log(`🔵 Inizio scan TUTTE le cartelle per user=${userName}`); const photosRoot = path.resolve(__dirname, '..', '..', WEB_ROOT, 'photos'); const userDir = path.join(photosRoot, userName, 'original'); const statusPath = path.join(photosRoot, "scan_status.json"); let entries = []; try { entries = await fsp.readdir(userDir, { withFileTypes: true }); } catch { log(`❌ Nessuna directory per utente ${userName}`); return []; } // 🔥 Filtra SOLO cartelle vere dentro "original" const folders = entries.filter(e => e.isDirectory()).map(e => e.name); // --------------------------------------------------------- // 🔥 RIMOZIONE CARTELLE CANCELLATE DAL FILESYSTEM // --------------------------------------------------------- const createDeleteFolderFunctions = require('./deleteFolder'); const { deleteFolderForUser } = createDeleteFolderFunctions(db); const dbFolders = await db('photos') .where({ user: userName }) .distinct('cartella') .pluck('cartella'); for (const dbFolder of dbFolders) { if (!folders.includes(dbFolder)) { log(`🗑️ Cartella rimossa dal filesystem: ${dbFolder}`); await deleteFolderForUser(userName, dbFolder); } } // 🔥 PRIMA PASSATA: conta i file reali const TOTAL_FILES = await countFilesUser(userDir); const CURRENT = { value: 0 }; const start = Date.now(); log(`📊 File totali da scansionare: ${TOTAL_FILES}`); // Scrivi stato iniziale await fsp.writeFile( statusPath, JSON.stringify({ current: 0, total: TOTAL_FILES, percent: 0, eta: "calcolo..." }, null, 2) ); const allNewFiles = []; // 🔥 SECONDA PASSATA: UNA SOLA SCANSIONE PER CARTELLA for (const cartella of folders) { log(`📁 Scan cartella utente: ${cartella}`); const newFiles = await scanPhoto( cartella, userName, db, CURRENT, TOTAL_FILES, start, async () => { // 🔥 Callback di aggiornamento progresso await fsp.writeFile( statusPath, JSON.stringify({ current: CURRENT.value, total: TOTAL_FILES, percent: Math.round((CURRENT.value / TOTAL_FILES) * 100), eta: computeETA(start, CURRENT.value, TOTAL_FILES) }, null, 2) ); } ); if (newFiles?.length) { allNewFiles.push(...newFiles); } } // Stato finale await fsp.writeFile( statusPath, JSON.stringify({ current: TOTAL_FILES, total: TOTAL_FILES, percent: 100, eta: "0m 0s" }, null, 2) ); log(`🟣 Scan COMPLETATO per user=${userName}. Nuovi file totali: ${allNewFiles.length}`); return allNewFiles; } module.exports = scanPhotosUser;