feat(scan): support iX1600 duplex scanning on Linux

This commit is contained in:
2026-09-17 18:22:43 +02:00
parent cfef07b96d
commit dd37622bb6
24 changed files with 1411 additions and 215 deletions

View File

@@ -1,66 +1,45 @@
import path from "node:path"
import { fileURLToPath } from "node:url"
import { AgentConfig, ScanResult } from "../types.js"
import { commandExists, runCommand } from "../commands.js"
import { runCommand } from "../commands.js"
const currentFile = fileURLToPath(import.meta.url)
const agentRoot = path.resolve(path.dirname(currentFile), "../..")
const postprocessScript = path.join(agentRoot, "scripts/opencv_postprocess.py")
const agentRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..")
const documentScript = path.join(agentRoot, "scripts/scan_document.py")
const extensionMimeTypes: Record<string, string> = {
".pdf": "application/pdf",
".png": "image/png",
".tif": "image/tiff",
".tiff": "image/tiff",
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
export const checkScanRuntime = async (config: AgentConfig, postprocess: boolean) => {
const imports = postprocess && config.postprocessStrict ? "import PIL, cv2, numpy" : "import PIL"
try {
const result = await runCommand(config.postprocessPython, ["-c", imports], { timeoutMs: 10_000 })
if (result.code === 0) return
} catch {
// Report missing runtimes before the scanner consumes any paper.
}
throw new Error("Python/Pillow (bei strikter Nachbearbeitung zusätzlich OpenCV) fehlt. Bitte npm run setup:opencv ausführen oder python3-pil installieren.")
}
const ensureOutputExtension = (filename: string, format: AgentConfig["scanFormat"]) => {
const ext = path.extname(filename)
if (ext) return filename
return `${filename}.${format}`
}
export const hasOpenCvPostprocessRuntime = async (config: AgentConfig) => {
if (!await commandExists(config.postprocessPython)) return false
const result = await runCommand(config.postprocessPython, [
"-c",
"import cv2, PIL, numpy",
], { timeoutMs: 10_000 })
return result.code === 0
}
export const postprocessScan = async (
export const createScanDocument = async (
config: AgentConfig,
inputPath: string,
outputFilename: string,
outputFormat: AgentConfig["scanFormat"],
pages: string[],
outputPath: string,
resolution: number,
postprocess: boolean,
profile: AgentConfig["postprocessProfile"]
): Promise<ScanResult> => {
const filename = ensureOutputExtension(outputFilename, outputFormat)
const outputPath = path.join(config.workDir, filename)
const args = [documentScript, "--output", outputPath, "--resolution", String(resolution), "--profile", profile]
if (postprocess) args.push("--postprocess")
if (config.postprocessStrict) args.push("--strict")
args.push("--inputs", ...pages)
const result = await runCommand(config.postprocessPython, [
postprocessScript,
"--input",
inputPath,
"--output",
outputPath,
"--profile",
profile,
], { timeoutMs: 5 * 60 * 1000 })
const result = await runCommand(config.postprocessPython, args, {
timeoutMs: config.scanTimeoutSeconds * 1000,
})
if (result.code !== 0) throw new Error(result.stderr || "Scan-Datei konnte nicht erstellt werden")
if (result.code !== 0) {
throw new Error(result.stderr || `OpenCV-Nachbearbeitung wurde mit Code ${result.code} beendet`)
}
const extension = path.extname(outputPath).toLowerCase()
const details = JSON.parse(result.stdout) as { pageCount: number; warnings: string[] }
return {
path: outputPath,
filename,
mimeType: extensionMimeTypes[extension] || "application/octet-stream",
filename: path.basename(outputPath),
mimeType: outputPath.endsWith(".pdf") ? "application/pdf" : outputPath.endsWith(".png") ? "image/png" : "image/tiff",
...details,
}
}