ASCIInator/server/routes/convert.js

67 lines
2.3 KiB
JavaScript
Raw Normal View History

import { randomUUID } from 'node:crypto'
import { writeFile, unlink } from 'node:fs/promises'
import path from 'node:path'
import { preprocess } from '../lib/imagemagick.js'
import {
runChafa,
runJp2a,
runAsciiImageConverter,
runImgToTxt,
} from '../lib/converters.js'
const RUNNERS = {
chafa: runChafa,
jp2a: runJp2a,
'ascii-image-converter': runAsciiImageConverter,
img2txt: runImgToTxt,
}
export async function convertRoute(fastify) {
fastify.post('/convert', async (request, reply) => {
let tempInput = null
let tempProcessed = null
try {
const parts = request.parts()
let tool = null
let flags = {}
for await (const part of parts) {
if (part.type === 'file' && part.fieldname === 'image') {
const SAFE_EXTS = new Set(['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp', '.tiff', '.tif', '.avif'])
const rawExt = path.extname(part.filename || '').toLowerCase()
const ext = SAFE_EXTS.has(rawExt) ? rawExt : '.jpg'
tempInput = `/tmp/asciinator-${randomUUID()}${ext}`
const buf = await part.toBuffer()
await writeFile(tempInput, buf)
} else if (part.type === 'field') {
if (part.fieldname === 'tool') tool = part.value
if (part.fieldname === 'flags') {
try { flags = JSON.parse(part.value) } catch { flags = {} }
}
}
}
if (!tempInput) return reply.code(400).type('text/plain').send('No image provided')
if (!tool) return reply.code(400).type('text/plain').send('No tool specified')
const runner = RUNNERS[tool]
if (!runner) return reply.code(400).type('text/plain').send(`Unknown tool: ${tool}`)
const format = tool === 'jp2a' ? 'jpg' : 'png'
tempProcessed = await preprocess(tempInput, format)
const result = await runner(tempProcessed, flags)
return reply.type('text/plain').send(result.stdout)
} catch (err) {
if (err.exitCode !== undefined) {
return reply.code(422).type('text/plain').send(err.stderr || err.message)
}
return reply.code(400).type('text/plain').send(err.message)
} finally {
if (tempInput) await unlink(tempInput).catch(() => {})
if (tempProcessed) await unlink(tempProcessed).catch(() => {})
}
})
}