'use strict'; // Scansione completa: individua i file e li accoda. // L'elaborazione effettiva è responsabilità di photoIngestWorker. const path = require('path'); const fsp = require('fs/promises'); const crypto = require('crypto'); const { WEB_ROOT, SUPPORTED_EXTS } = require('../config'); const { log } = require('./logger'); const scanCartella = require('./scanCartella'); const { ensurePhotoIngestQueue, enqueuePhotoIngestJob, } = require('./photoIngestQueue'); function sha256(value) { return crypto.createHash('sha256').update(value).digest('hex'); } async function thumbnailsExist(userName, cartella, relPath) { const parsed = path.parse(relPath); const thumbDir = path.resolve( __dirname, '..', '..', WEB_ROOT, 'photos', userName, 'thumbs', cartella, parsed.dir ); const base = path.join(thumbDir, parsed.name); try { await Promise.all([ fsp.access(`${base}_min.jpg`), fsp.access(`${base}_avg.jpg`), ]); return true; } catch { return false; } } async function scanPhotosUser(userName, db) { log(`🔵 Inizio scansione e accodamento per user=${userName}`); const photosRoot = path.resolve(__dirname, '..', '..', WEB_ROOT, 'photos'); const userDir = path.join(photosRoot, userName, 'original'); let entries; try { entries = await fsp.readdir(userDir, { withFileTypes: true }); } catch { log(`❌ Nessuna directory per utente ${userName}`); return []; } const folders = entries .filter(entry => entry.isDirectory()) .map(entry => entry.name); // Mantiene la pulizia delle cartelle eliminate dal filesystem. const { deleteFolderForUser } = require('./deleteFolder')(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); } } await ensurePhotoIngestQueue(db); let queued = 0; let skipped = 0; for (const cartella of folders) { const absCartella = path.join(userDir, cartella); log(`📁 Controllo cartella: ${cartella}`); for await (const file of scanCartella(userName, cartella, absCartella, db)) { const previous = await db('photos') .select('id', 'size_bytes', 'mtimeMs', 'fast_hash', 'path') .where({ id: file.id }) .first(); const expectedHash = sha256(`${file.stat.size}-${file.stat.mtimeMs}`); const expectedPath = file.path; let needsProcessing = !previous || previous.path !== expectedPath || previous.fast_hash !== expectedHash; if (!needsProcessing) { needsProcessing = !(await thumbnailsExist( userName, cartella, file.relPath )); } if (needsProcessing) { await enqueuePhotoIngestJob(db, { user: userName, cartella, relPath: file.relPath, mtimeMs: file.stat.mtimeMs, sizeBytes: file.stat.size, }); queued++; } else { skipped++; } } } log( `🟣 Scansione/accodamento terminato per ${userName}: ` + `${queued} accodati, ${skipped} invariati` ); // Restituisce un array per compatibilità con chi già chiama questa funzione. // Non contiene risultati elaborati: quelli arriveranno dal worker. return []; } module.exports = scanPhotosUser;