Public Access
Add patch script to wire printer.js to the native QL module
Replaces the brother_ql subprocess call in printBadge with printPng, and swaps available() for a real reachability check. Uses brace matching rather than literal body matching so it survives formatting differences, refuses to run if the anchors are missing, is idempotent, and writes a .bak first. The badge layout is untouched.
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* One-shot patch: point printer.js at the native QL protocol module instead of
|
||||
* shelling out to the Python brother_ql CLI.
|
||||
*
|
||||
* node scripts/wire-native-printing.mjs # patch
|
||||
* node scripts/wire-native-printing.mjs --check # report only, change nothing
|
||||
*
|
||||
* Safe to run twice — it detects an already-patched file and stops.
|
||||
* Writes printer.js.bak next to the original before touching anything.
|
||||
*
|
||||
* Only the imports and the two functions at the bottom change. The badge
|
||||
* layout (drawBadge / renderBadgePng) is left exactly as it is.
|
||||
*/
|
||||
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const here = path.dirname(fileURLToPath(import.meta.url));
|
||||
const target = path.resolve(here, '..', 'src', 'printer.js');
|
||||
const checkOnly = process.argv.includes('--check');
|
||||
|
||||
function fail(message) {
|
||||
console.error(`\n FAILED: ${message}\n`);
|
||||
console.error(' Nothing was changed. Patch printer.js by hand instead.\n');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a top-level function and return its full text by matching braces.
|
||||
* Formatting-independent, unlike matching the whole body literally.
|
||||
*/
|
||||
function extractFunction(source, signature) {
|
||||
const start = source.indexOf(signature);
|
||||
if (start === -1) return null;
|
||||
|
||||
let depth = 0;
|
||||
let i = source.indexOf('{', start);
|
||||
if (i === -1) return null;
|
||||
|
||||
for (; i < source.length; i++) {
|
||||
if (source[i] === '{') depth++;
|
||||
else if (source[i] === '}') {
|
||||
depth--;
|
||||
if (depth === 0) {
|
||||
return { start, end: i + 1, text: source.slice(start, i + 1) };
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!fs.existsSync(target)) fail(`${target} does not exist. Run this from the repo root.`);
|
||||
|
||||
let source = fs.readFileSync(target, 'utf8');
|
||||
const original = source;
|
||||
|
||||
if (source.includes("from './printing/ql-print.js'")) {
|
||||
console.log('\n Already patched — printer.js imports the native module. Nothing to do.\n');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const changes = [];
|
||||
|
||||
/* -- 1. Import the native module ---------------------------------- */
|
||||
|
||||
const importAnchor = "import { photoAbsolutePath } from './photos.js';";
|
||||
if (!source.includes(importAnchor)) fail('could not find the photos.js import');
|
||||
|
||||
source = source.replace(
|
||||
importAnchor,
|
||||
`${importAnchor}\nimport { printPng, printerHealth } from './printing/ql-print.js';`
|
||||
);
|
||||
changes.push('added the ql-print import');
|
||||
|
||||
/* -- 2. Replace printBadge ---------------------------------------- */
|
||||
|
||||
const printBadge = extractFunction(source, 'export async function printBadge(visit, site)');
|
||||
if (!printBadge) fail('could not locate printBadge');
|
||||
if (!printBadge.text.includes('runBrotherQl')) {
|
||||
fail('printBadge does not call runBrotherQl — it may already have been changed');
|
||||
}
|
||||
|
||||
const newPrintBadge = `export async function printBadge(visit, site) {
|
||||
if (!isConfigured(site)) throw new Error('Server printing is not turned on for this site.');
|
||||
|
||||
const png = await renderBadgePng(visit, site);
|
||||
const port = Number(site.printer_port) || 9100;
|
||||
const target = \`tcp://\${site.printer_host}:\${port}\`;
|
||||
|
||||
// The roll decides whether the job is two-colour, not the accent setting.
|
||||
// A DK-22251 roll refuses a monochrome job even when the badge has no red
|
||||
// on it, so labelFor() is passed straight through and ql-print maps it.
|
||||
const result = await printPng(png, {
|
||||
host: site.printer_host,
|
||||
port,
|
||||
label: labelFor(site),
|
||||
});
|
||||
|
||||
note(site.id, result.ok, result.message);
|
||||
if (!result.ok) throw new Error(result.message);
|
||||
|
||||
// confirmed is false when the printer accepted the bytes but never said the
|
||||
// label came out. Over the network that is the normal case, not a fault.
|
||||
return { ok: true, confirmed: Boolean(result.confirmed), target };
|
||||
}`;
|
||||
|
||||
source =
|
||||
source.slice(0, printBadge.start) + newPrintBadge + source.slice(printBadge.end);
|
||||
changes.push('printBadge now uses printPng');
|
||||
|
||||
/* -- 3. Replace available ----------------------------------------- */
|
||||
|
||||
const available = extractFunction(source, 'export function available()');
|
||||
if (available) {
|
||||
const newAvailable = `export function available() {
|
||||
// Kept for callers that only ask "can this server print at all?". The
|
||||
// external brother_ql binary is no longer involved, so the answer is always
|
||||
// yes; use health(site) to ask about a specific printer.
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reachability and, where the printer will say, roll state.
|
||||
*
|
||||
* ready is tri-state: true, false, or null meaning "reachable but it won't
|
||||
* tell us". Null is the normal answer over the network — show it as unknown
|
||||
* in the admin console rather than green or red.
|
||||
*/
|
||||
export function health(site) {
|
||||
return printerHealth({
|
||||
host: site?.printer_host,
|
||||
port: Number(site?.printer_port) || 9100,
|
||||
label: labelFor(site),
|
||||
});
|
||||
}`;
|
||||
|
||||
source =
|
||||
source.slice(0, available.start) + newAvailable + source.slice(available.end);
|
||||
changes.push('available() no longer probes for a binary; added health(site)');
|
||||
} else {
|
||||
console.warn(' note: available() not found, skipping that step');
|
||||
}
|
||||
|
||||
/* -- 4. Flag the now-dead subprocess code ------------------------- */
|
||||
|
||||
for (const name of ['function explainPrintError', 'function runBrotherQl']) {
|
||||
if (source.includes(name)) {
|
||||
changes.push(`${name.split(' ')[1]}() is now unused and can be deleted`);
|
||||
}
|
||||
}
|
||||
|
||||
/* -- Report / write ----------------------------------------------- */
|
||||
|
||||
console.log('\n Changes to src/printer.js:');
|
||||
for (const c of changes) console.log(` - ${c}`);
|
||||
|
||||
if (checkOnly) {
|
||||
console.log('\n --check given, nothing written.\n');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (source === original) fail('nothing actually changed');
|
||||
|
||||
fs.writeFileSync(`${target}.bak`, original);
|
||||
fs.writeFileSync(target, source);
|
||||
|
||||
console.log(`\n Wrote ${target}`);
|
||||
console.log(` Backup at ${target}.bak`);
|
||||
console.log('\n Next: docker compose build && docker compose up -d\n');
|
||||
Reference in New Issue
Block a user