1.0.14 Package Updates and Assets fix
This commit is contained in:
@@ -5,7 +5,6 @@ import { FileTypeResult } from "file-type/core";
|
||||
import fs from "fs-extra";
|
||||
import gm from "gm";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
import { logger } from "../server.js";
|
||||
import { generateUniqueHeicFilename } from "./generateUniqueFilename.js";
|
||||
import { FolderPaths } from "./serverInit.js";
|
||||
@@ -81,8 +80,12 @@ async function handleOriginalFile(fileInfo: { path: string; destination: string;
|
||||
await fs.unlink(fileInfo.path);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error("Error handling original file:", error);
|
||||
throw error;
|
||||
// Don't throw error for file handling issues - log as warning and continue
|
||||
logger.warning("Error handling original HEIC file (continuing with conversion):", {
|
||||
file: fileInfo.originalFilename,
|
||||
path: fileInfo.path,
|
||||
error: (error as Error).message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,15 +94,38 @@ async function handleOriginalFile(fileInfo: { path: string; destination: string;
|
||||
*/
|
||||
async function convertToJpeg(inputPath: string, outputPath: string): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const readStream = fs.createReadStream(inputPath);
|
||||
const writeStream = fs.createWriteStream(outputPath);
|
||||
try {
|
||||
const readStream = fs.createReadStream(inputPath);
|
||||
const writeStream = fs.createWriteStream(outputPath);
|
||||
|
||||
gm(readStream)
|
||||
.setFormat("jpg")
|
||||
.stream()
|
||||
.pipe(writeStream)
|
||||
.on("finish", () => resolve(outputPath))
|
||||
.on("error", reject);
|
||||
// Add error handling for stream creation
|
||||
readStream.on("error", (err) => {
|
||||
logger.warning(`Error reading HEIC file ${inputPath}:`, err);
|
||||
reject(new Error(`Failed to read HEIC file: ${err.message}`));
|
||||
});
|
||||
|
||||
writeStream.on("error", (err) => {
|
||||
logger.warning(`Error writing converted file ${outputPath}:`, err);
|
||||
reject(new Error(`Failed to write converted file: ${err.message}`));
|
||||
});
|
||||
|
||||
gm(readStream)
|
||||
.setFormat("jpg")
|
||||
.stream()
|
||||
.on("error", (err) => {
|
||||
logger.warning(`GraphicsMagick conversion error for ${inputPath}:`, err);
|
||||
reject(new Error(`GraphicsMagick conversion failed: ${err.message}`));
|
||||
})
|
||||
.pipe(writeStream)
|
||||
.on("finish", () => resolve(outputPath))
|
||||
.on("error", (err) => {
|
||||
logger.warning(`Stream pipe error for ${inputPath}:`, err);
|
||||
reject(new Error(`Stream processing failed: ${err.message}`));
|
||||
});
|
||||
} catch (error) {
|
||||
logger.warning(`Unexpected error in convertToJpeg for ${inputPath}:`, error);
|
||||
reject(new Error(`Conversion setup failed: ${(error as Error).message}`));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -127,33 +153,73 @@ export async function convertHeicFiles(files: Express.Multer.File[]) {
|
||||
}));
|
||||
|
||||
// Add jobs and wait for completion of each before proceeding
|
||||
const successfulConversions: Array<{ originalFilename: string; convertedFileName: string }> = [];
|
||||
const failedConversions: Array<{ originalFilename: string; error: string }> = [];
|
||||
|
||||
for (const jobData of jobs) {
|
||||
try {
|
||||
const job = await heicQueue.add(jobData.name, jobData.data);
|
||||
await job.waitUntilFinished(heicQueueEvents);
|
||||
logger.debug(`Job ${job.id} finished successfully.`);
|
||||
const result = await job.waitUntilFinished(heicQueueEvents);
|
||||
|
||||
if (result && result.success) {
|
||||
logger.debug(`Job ${job.id} finished successfully.`);
|
||||
successfulConversions.push({
|
||||
originalFilename: jobData.data.fileInfo.originalFilename,
|
||||
convertedFileName: jobData.data.convertedFileName
|
||||
});
|
||||
} else {
|
||||
logger.warning(`Job ${job.id} completed but conversion failed for ${jobData.data.fileInfo.originalFilename}`);
|
||||
failedConversions.push({
|
||||
originalFilename: jobData.data.fileInfo.originalFilename,
|
||||
error: result?.error || "Unknown conversion error"
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Job for ${jobData.data.fileInfo.originalFilename} failed:`, error);
|
||||
// Depending on your error handling strategy you might rethrow or continue
|
||||
logger.warning(`Job for ${jobData.data.fileInfo.originalFilename} failed:`, error);
|
||||
failedConversions.push({
|
||||
originalFilename: jobData.data.fileInfo.originalFilename,
|
||||
error: (error as Error).message
|
||||
});
|
||||
// Continue with next job instead of stopping
|
||||
}
|
||||
}
|
||||
|
||||
// Update original files list with new names, mimetype, and path
|
||||
// Log summary
|
||||
if (failedConversions.length > 0) {
|
||||
logger.warning(
|
||||
`HEIC conversion summary: ${successfulConversions.length} successful, ${failedConversions.length} failed:`,
|
||||
{
|
||||
failed: failedConversions.map((f) => f.originalFilename)
|
||||
}
|
||||
);
|
||||
} else {
|
||||
logger.debug(`HEIC conversion summary: All ${successfulConversions.length} files converted successfully`);
|
||||
}
|
||||
|
||||
// Update original files list with new names, mimetype, and path - only for successful conversions
|
||||
const filenameToIndex = new Map(files.map((f, i) => [f.filename, i]));
|
||||
for (const { data } of jobs) {
|
||||
const idx = filenameToIndex.get(data.fileInfo.originalFilename);
|
||||
for (const conversion of successfulConversions) {
|
||||
const idx = filenameToIndex.get(conversion.originalFilename);
|
||||
if (idx !== undefined) {
|
||||
const oldPath = files[idx].path;
|
||||
files[idx].filename = data.convertedFileName;
|
||||
files[idx].filename = conversion.convertedFileName;
|
||||
files[idx].mimetype = "image/jpeg";
|
||||
files[idx].path = path.join(data.fileInfo.destination, data.convertedFileName);
|
||||
logger.debug(`Updated file entry: ${data.fileInfo.originalFilename} -> ${data.convertedFileName}`, {
|
||||
files[idx].path = path.join(files[idx].destination, conversion.convertedFileName);
|
||||
logger.debug(`Updated file entry: ${conversion.originalFilename} -> ${conversion.convertedFileName}`, {
|
||||
oldPath,
|
||||
newPath: files[idx].path,
|
||||
newMimetype: files[idx].mimetype
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Remove failed conversions from the files array to prevent further processing
|
||||
for (let i = files.length - 1; i >= 0; i--) {
|
||||
if (failedConversions.some((f) => f.originalFilename === files[i].filename)) {
|
||||
logger.debug(`Removing failed HEIC file from processing: ${files[i].filename}`);
|
||||
files.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Worker processing HEIC conversion jobs
|
||||
@@ -166,11 +232,12 @@ const heicWorker = new Worker(
|
||||
}>
|
||||
) => {
|
||||
const { fileInfo, convertedFileName } = job.data;
|
||||
const outputPath = path.join(fileInfo.destination, convertedFileName);
|
||||
|
||||
try {
|
||||
logger.debug(`Converting ${fileInfo.originalFilename} from HEIC to JPEG.`);
|
||||
await job.updateProgress(10);
|
||||
|
||||
const outputPath = path.join(fileInfo.destination, convertedFileName);
|
||||
await convertToJpeg(fileInfo.path, outputPath);
|
||||
|
||||
await job.updateProgress(50);
|
||||
@@ -179,10 +246,60 @@ const heicWorker = new Worker(
|
||||
logger.debug(`Successfully converted ${fileInfo.originalFilename} to JPEG.`);
|
||||
await job.updateProgress(100);
|
||||
|
||||
return true;
|
||||
return { success: true, convertedFileName };
|
||||
} catch (error) {
|
||||
logger.error(`Error converting ${fileInfo.originalFilename}:`, error);
|
||||
throw error;
|
||||
const errorMessage = (error as Error).message;
|
||||
// Handle GraphicsMagick and other conversion errors gracefully
|
||||
if (
|
||||
errorMessage.includes("Magick") ||
|
||||
errorMessage.includes("HEIC") ||
|
||||
errorMessage.includes("corrupt") ||
|
||||
errorMessage.includes("invalid")
|
||||
) {
|
||||
logger.warning(
|
||||
`[heicWorker] HEIC conversion error for ${fileInfo.originalFilename}, moving to damaged folder:`,
|
||||
errorMessage
|
||||
);
|
||||
|
||||
// Clean up both the original HEIC file and any partially created JPEG
|
||||
try {
|
||||
// Move original HEIC file to damaged subfolder instead of deleting
|
||||
const damagedDir = path.join(fileInfo.destination, FolderPaths.DamagedSubDir);
|
||||
await fs.ensureDir(damagedDir);
|
||||
const damagedFilePath = path.join(damagedDir, fileInfo.originalFilename);
|
||||
|
||||
if (await fs.pathExists(fileInfo.path)) {
|
||||
await fs.move(fileInfo.path, damagedFilePath, { overwrite: true });
|
||||
logger.debug(`Moved damaged HEIC file to: ${damagedFilePath}`);
|
||||
}
|
||||
} catch (cleanupError) {
|
||||
logger.warning(`Failed to move damaged file for ${fileInfo.originalFilename}:`, cleanupError);
|
||||
}
|
||||
|
||||
return { success: false, error: errorMessage, originalFilename: fileInfo.originalFilename };
|
||||
}
|
||||
|
||||
logger.error(`[heicWorker] Unexpected error converting ${fileInfo.originalFilename}:`, error);
|
||||
|
||||
// For other errors, also clean up any partial files
|
||||
try {
|
||||
// Move original HEIC file to damaged subfolder instead of deleting
|
||||
const damagedDir = path.join(fileInfo.destination, FolderPaths.DamagedSubDir);
|
||||
await fs.ensureDir(damagedDir);
|
||||
const damagedFilePath = path.join(damagedDir, fileInfo.originalFilename);
|
||||
|
||||
if (await fs.pathExists(fileInfo.path)) {
|
||||
await fs.move(fileInfo.path, damagedFilePath, { overwrite: true });
|
||||
logger.debug(`Moved damaged HEIC file after unexpected error to: ${damagedFilePath}`);
|
||||
}
|
||||
} catch (cleanupError) {
|
||||
logger.warning(
|
||||
`Failed to move damaged file after unexpected error for ${fileInfo.originalFilename}:`,
|
||||
cleanupError
|
||||
);
|
||||
}
|
||||
|
||||
return { success: false, error: errorMessage, originalFilename: fileInfo.originalFilename };
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user