143 lines
3.5 KiB
JavaScript
143 lines
3.5 KiB
JavaScript
'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;
|