From a94d9381315eec49497ef8a291f81d11d873bf18 Mon Sep 17 00:00:00 2001 From: chris Date: Mon, 8 Dec 2025 15:56:12 -0500 Subject: [PATCH] chore: allow reprocess to use raw source files --- .../backend/scripts/reprocess_uploads.js | 37 ++++++++++++++----- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/photo-gallery-app/backend/scripts/reprocess_uploads.js b/photo-gallery-app/backend/scripts/reprocess_uploads.js index 2b785ad..7a2a64f 100644 --- a/photo-gallery-app/backend/scripts/reprocess_uploads.js +++ b/photo-gallery-app/backend/scripts/reprocess_uploads.js @@ -21,6 +21,7 @@ const path = require('path'); const crypto = require('crypto'); const sharp = require('sharp'); const mongoose = require('mongoose'); +const heicConvert = require('heic-convert'); const Photo = require('../models/photo'); const MONGO_URI = process.env.MONGO_URI || 'mongodb://localhost:27017/photogallery'; @@ -32,6 +33,7 @@ const VARIANTS = { medium: { size: 1200, quality: 80, suffix: '-md' }, thumb: { size: 640, quality: 76, suffix: '-sm' }, }; +const SOURCE_EXTS = ['.webp', '.jpg', '.jpeg', '.png', '.heic', '.heif', '.avif', '.bmp', '.tif', '.tiff']; const diagonalOverlay = Buffer.from(` @@ -64,19 +66,23 @@ function parseBaseName(doc) { function sourceCandidates(doc) { const baseName = parseBaseName(doc); - const preferred = []; + const preferred = new Set(); const fromDocPath = doc.path ? doc.path.replace(/^\/+/, '') : ''; const fromDocFile = doc.filename ? path.join('uploads', doc.filename) : ''; [fromDocPath, fromDocFile] .filter(Boolean) - .forEach(rel => preferred.push(path.join(UPLOAD_DIR, rel.replace(/^uploads[\\/]/, '')))); + .forEach(rel => preferred.add(path.join(UPLOAD_DIR, rel.replace(/^uploads[\\/]/, '')))); - preferred.push( - path.join(UPLOAD_DIR, `${baseName}.webp`), - path.join(UPLOAD_DIR, `${baseName}-md.webp`), - path.join(UPLOAD_DIR, `${baseName}-sm.webp`) - ); - return preferred; + // Look for any common source extension using the base name + for (const ext of SOURCE_EXTS) { + preferred.add(path.join(UPLOAD_DIR, `${baseName}${ext}`)); + } + // Also check variant-style names in case only a variant exists + for (const ext of SOURCE_EXTS) { + preferred.add(path.join(UPLOAD_DIR, `${baseName}-md${ext}`)); + preferred.add(path.join(UPLOAD_DIR, `${baseName}-sm${ext}`)); + } + return Array.from(preferred); } async function findExistingFile(candidates) { @@ -89,6 +95,19 @@ async function findExistingFile(candidates) { return null; } +async function loadSourceBuffer(filePath) { + const ext = path.extname(filePath).toLowerCase(); + let inputBuffer = await fsPromises.readFile(filePath); + if (ext === '.heic' || ext === '.heif' || ext === '.avif' || isHeifBuffer(inputBuffer)) { + inputBuffer = await heicConvert({ + buffer: inputBuffer, + format: 'JPEG', + quality: 1, + }); + } + return inputBuffer; +} + async function stampAndVariants(inputBuffer, baseName) { // Build main stamped image const base = sharp(inputBuffer) @@ -139,7 +158,7 @@ async function processDoc(doc) { }; } - const inputBuffer = await fsPromises.readFile(sourceFile); + const inputBuffer = await loadSourceBuffer(sourceFile); const hash = crypto.createHash('sha256').update(inputBuffer).digest('hex'); const outputs = await stampAndVariants(inputBuffer, parseBaseName(doc));