Update server.js
This commit is contained in:
52
server.js
52
server.js
@@ -45,7 +45,7 @@ async function toJpegWithSharp(inputBuffer) {
|
|||||||
|
|
||||||
async function toJpegWithFfmpeg(inputBuffer) {
|
async function toJpegWithFfmpeg(inputBuffer) {
|
||||||
const id = randomUUID();
|
const id = randomUUID();
|
||||||
const inPath = `/tmp/${id}.bin`; // ffmpeg probes container; extension not required
|
const inPath = `/tmp/${id}.bin`; // ffmpeg probes; extension not required
|
||||||
const outPath = `/tmp/${id}.jpg`;
|
const outPath = `/tmp/${id}.jpg`;
|
||||||
|
|
||||||
await fs.writeFile(inPath, inputBuffer);
|
await fs.writeFile(inPath, inputBuffer);
|
||||||
@@ -59,36 +59,35 @@ async function toJpegWithFfmpeg(inputBuffer) {
|
|||||||
"-frames:v",
|
"-frames:v",
|
||||||
"1",
|
"1",
|
||||||
"-q:v",
|
"-q:v",
|
||||||
"1", // high quality JPEG
|
"1", // high-quality JPEG from ffmpeg
|
||||||
outPath,
|
outPath,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const jpg = await fs.readFile(outPath);
|
const jpg = await fs.readFile(outPath);
|
||||||
|
|
||||||
// Normalize via sharp so output settings are consistent (quality 100, 4:4:4)
|
// Normalize output with sharp to enforce consistent settings (quality 100, 4:4:4)
|
||||||
return toJpegWithSharp(jpg);
|
return toJpegWithSharp(jpg);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function toJpegWithMagick(inputBuffer) {
|
async function toJpegWithImagemagick(inputBuffer) {
|
||||||
const id = randomUUID();
|
const id = randomUUID();
|
||||||
const inPath = `/tmp/${id}.bin`;
|
const inPath = `/tmp/${id}.bin`;
|
||||||
const outPath = `/tmp/${id}.jpg`;
|
const outPath = `/tmp/${id}.jpg`;
|
||||||
|
|
||||||
await fs.writeFile(inPath, inputBuffer);
|
await fs.writeFile(inPath, inputBuffer);
|
||||||
|
|
||||||
// ImageMagick: convert whatever it can to JPEG
|
// ImageMagick 7 uses `magick`; IM6 uses `convert`
|
||||||
// -strip removes metadata; remove if you want to preserve EXIF
|
// We'll try `magick` first, then fallback to `convert`.
|
||||||
await execFilePromise("magick", [
|
const args = [inPath, "-auto-orient", "-quality", "100", outPath];
|
||||||
inPath,
|
|
||||||
"-auto-orient",
|
try {
|
||||||
"-quality",
|
await execFilePromise("magick", args);
|
||||||
"100",
|
} catch (e) {
|
||||||
outPath,
|
// If magick isn't installed, IM6 usually provides `convert`
|
||||||
]);
|
await execFilePromise("convert", args);
|
||||||
|
}
|
||||||
|
|
||||||
const jpg = await fs.readFile(outPath);
|
const jpg = await fs.readFile(outPath);
|
||||||
|
|
||||||
// Normalize via sharp for consistent chromaSubsampling etc.
|
|
||||||
return toJpegWithSharp(jpg);
|
return toJpegWithSharp(jpg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,14 +121,14 @@ app.post("/convert", async (req, res) => {
|
|||||||
const input = req.body;
|
const input = req.body;
|
||||||
if (!input || input.length === 0) return res.status(400).send("Empty body");
|
if (!input || input.length === 0) return res.status(400).send("Empty body");
|
||||||
|
|
||||||
// PDF: always handle via poppler (pdftoppm)
|
// PDF: always handle via poppler
|
||||||
if (isPdfRequest(req)) {
|
if (isPdfRequest(req)) {
|
||||||
const jpeg = await pdfFirstPageToJpeg(input, 300);
|
const jpeg = await pdfFirstPageToJpeg(input, 300);
|
||||||
res.setHeader("Content-Type", "image/jpeg");
|
res.setHeader("Content-Type", "image/jpeg");
|
||||||
return res.status(200).send(jpeg);
|
return res.status(200).send(jpeg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Non-PDF: sharp -> ffmpeg -> magick
|
// Non-PDF: sharp -> ffmpeg -> imagemagick
|
||||||
try {
|
try {
|
||||||
const jpeg = await toJpegWithSharp(input);
|
const jpeg = await toJpegWithSharp(input);
|
||||||
res.setHeader("Content-Type", "image/jpeg");
|
res.setHeader("Content-Type", "image/jpeg");
|
||||||
@@ -140,7 +139,7 @@ app.post("/convert", async (req, res) => {
|
|||||||
res.setHeader("Content-Type", "image/jpeg");
|
res.setHeader("Content-Type", "image/jpeg");
|
||||||
return res.status(200).send(jpeg);
|
return res.status(200).send(jpeg);
|
||||||
} catch (e2) {
|
} catch (e2) {
|
||||||
const jpeg = await toJpegWithMagick(input);
|
const jpeg = await toJpegWithImagemagick(input);
|
||||||
res.setHeader("Content-Type", "image/jpeg");
|
res.setHeader("Content-Type", "image/jpeg");
|
||||||
return res.status(200).send(jpeg);
|
return res.status(200).send(jpeg);
|
||||||
}
|
}
|
||||||
@@ -177,7 +176,13 @@ app.post("/convert/pdf", async (req, res) => {
|
|||||||
await fs.mkdir(outDir, { recursive: true });
|
await fs.mkdir(outDir, { recursive: true });
|
||||||
await fs.writeFile(pdfPath, input);
|
await fs.writeFile(pdfPath, input);
|
||||||
|
|
||||||
await execFilePromise("pdftoppm", ["-jpeg", "-r", String(dpi), pdfPath, outPrefix]);
|
await execFilePromise("pdftoppm", [
|
||||||
|
"-jpeg",
|
||||||
|
"-r",
|
||||||
|
String(dpi),
|
||||||
|
pdfPath,
|
||||||
|
outPrefix,
|
||||||
|
]);
|
||||||
|
|
||||||
const files = (await fs.readdir(outDir))
|
const files = (await fs.readdir(outDir))
|
||||||
.filter((f) => /^page-\d+\.jpg$/i.test(f))
|
.filter((f) => /^page-\d+\.jpg$/i.test(f))
|
||||||
@@ -185,12 +190,17 @@ app.post("/convert/pdf", async (req, res) => {
|
|||||||
|
|
||||||
if (files.length === 0) return res.status(500).send("PDF render produced no pages");
|
if (files.length === 0) return res.status(500).send("PDF render produced no pages");
|
||||||
if (files.length > maxPages) {
|
if (files.length > maxPages) {
|
||||||
return res.status(413).send(`PDF has ${files.length} pages; exceeds maxPages=${maxPages}`);
|
return res
|
||||||
|
.status(413)
|
||||||
|
.send(`PDF has ${files.length} pages; exceeds maxPages=${maxPages}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
res.status(200);
|
res.status(200);
|
||||||
res.setHeader("Content-Type", "application/zip");
|
res.setHeader("Content-Type", "application/zip");
|
||||||
res.setHeader("Content-Disposition", `attachment; filename="pdf-pages-${id}.zip"`);
|
res.setHeader(
|
||||||
|
"Content-Disposition",
|
||||||
|
`attachment; filename="pdf-pages-${id}.zip"`
|
||||||
|
);
|
||||||
|
|
||||||
const archive = archiver("zip", { zlib: { level: 6 } });
|
const archive = archiver("zip", { zlib: { level: 6 } });
|
||||||
archive.on("error", (err) => {
|
archive.on("error", (err) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user