scan2paperless/src/main.ts

134 lines
3.4 KiB
TypeScript

import { parse } from "std/flags";
import { iterateReader } from "std/streams/iterate_reader";
const getRandomString = () => `${+(new Date())}-${Math.round(Math.random() * 100000)}`;
(async () => {
const tempDir = await Deno.makeTempDir();
const args = parse(Deno.args);
if (args.help || args.h) {
console.log(`
Usage: scan2paperless [OPTION] [NAME]
Scan one or multiple pages into a PDF document ready for consumption by paperless
NAME will be the name of the document without extension.
Available options:
-d | --device=DEVICE Scanner device to use. Check \`scanimage -L\` for available devices.
This defaults to \$SCANNER_DEFAULT_DEVICE
-o | --output-dir=DIR Directory to write final PDF to. This defaults to \$SCANNER_OUTPUT_DIR
-h | --help show this help message
Example:
scan2paperless \
-d hp3900:libusb:005:002 \
-n 5 \
-o /var/lib/paperless/consume \
bank-statement-01-2023
`);
Deno.exit(0);
}
const device = args.d || args.device || Deno.env.get('SCANNER_DEFAULT_DEVICE');
const outputDir = args.d || args.device || Deno.env.get('SCANNER_OUTPUT_DIR');
const outputName = args._[0] || getRandomString();
/*
const pagesToScanStr = args.n || args.pages;
const pagesToScan = (() => {
if (!pagesToScanStr) {
return Infinity;
}
const pagesToScanInt = parseInt(pagesToScanStr, 10);
if (isNaN(pagesToScanInt)) {
console.error(`Number of pages to scan does not appear to be a number: ${pagesToScanStr}`);
Deno.exit(1);
}
return pagesToScanInt;
})();
let pagesScanned = 0;
*/
const files = [];
let cancelled = false;
do {
const fileName = `${tempDir}/${getRandomString()}.jpg`;
const scanimageProcess = Deno.run({
cmd: [
"scanimage",
"-d", device,
"--format=jpeg",
"--resolution", "300",
"--progress",
"-o", fileName,
],
stderr: 'piped',
});
const decoder = new TextDecoder();
const encoder = new TextEncoder();
let inScan = false;
for await (const chunk of iterateReader(scanimageProcess.stderr)) {
const line = decoder.decode(chunk);
if (!inScan && line.startsWith('Progress: ')) {
inScan = true;
Deno.kill(scanimageProcess.pid, 'SIGSTOP');
const answer = prompt("Scan next page? [Y/n]");
if (answer && !['y', 'yes'].includes(answer.toLowerCase())) {
Deno.kill(scanimageProcess.pid, 'SIGKILL');
cancelled = true;
break;
} else {
Deno.kill(scanimageProcess.pid, 'SIGCONT');
cancelled = false;
}
}
Deno.stdout.write(encoder.encode(`\r${line}`));
}
const status = await scanimageProcess.status();
scanimageProcess.stderr.close();
if (!cancelled && status.code !== 0) {
console.error('Scan seems to have failed with status ', status.code);
}
if (!cancelled) {
files.push(fileName);
}
} while(!cancelled);
const pdfFile = `${outputDir}/${outputName}.pdf`;
const img2pdfProcess = Deno.run({
cmd: [
"img2pdf",
"--output", pdfFile,
...files,
],
});
const pdfStatus = await img2pdfProcess.status();
if (pdfStatus.code !== 0) {
console.error('Error writing PDF');
Deno.exit(2);
}
console.log(`PDF written to ${pdfFile}`);
})();