2022-02-10 01:14:52 +08:00
|
|
|
const Logger = require('../lib/utils/logger');
|
|
|
|
const config = require('../config');
|
|
|
|
const fs = require('fs');
|
|
|
|
const redis = require('redis');
|
2022-08-10 21:03:26 +08:00
|
|
|
const {Worker, workerData} = require('worker_threads');
|
2022-04-29 19:50:42 +08:00
|
|
|
const path = require('path');
|
2022-07-28 02:54:16 +08:00
|
|
|
const cp = require('child_process');
|
2022-02-10 01:14:52 +08:00
|
|
|
|
|
|
|
const jobId = workerData;
|
|
|
|
|
|
|
|
const logger = new Logger('presAnn Collector');
|
2022-07-27 22:49:25 +08:00
|
|
|
logger.info('Collecting job ' + jobId);
|
2022-02-10 01:14:52 +08:00
|
|
|
|
2022-08-17 02:27:40 +08:00
|
|
|
const kickOffProcessWorker = (jobId, statusUpdate) => {
|
2022-02-13 04:03:07 +08:00
|
|
|
return new Promise((resolve, reject) => {
|
2022-08-17 02:27:40 +08:00
|
|
|
const worker = new Worker('./workers/process.js', {workerData: [jobId, statusUpdate]});
|
2022-07-27 22:49:25 +08:00
|
|
|
worker.on('message', resolve);
|
|
|
|
worker.on('error', reject);
|
|
|
|
worker.on('exit', (code) => {
|
|
|
|
if (code !== 0) {
|
|
|
|
reject(new Error(`Process Worker stopped with exit code ${code}`));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
};
|
2022-02-10 01:14:52 +08:00
|
|
|
|
2022-06-28 19:37:29 +08:00
|
|
|
const dropbox = path.join(config.shared.presAnnDropboxDir, jobId);
|
2022-02-10 01:14:52 +08:00
|
|
|
|
2022-02-13 04:03:07 +08:00
|
|
|
// Takes the Job from the dropbox
|
2022-07-27 22:49:25 +08:00
|
|
|
const job = fs.readFileSync(path.join(dropbox, 'job'));
|
|
|
|
const exportJob = JSON.parse(job);
|
2022-02-10 19:48:54 +08:00
|
|
|
|
2022-02-10 01:14:52 +08:00
|
|
|
// Collect the annotations from Redis
|
2022-02-10 19:48:54 +08:00
|
|
|
(async () => {
|
2022-07-27 22:49:25 +08:00
|
|
|
const client = redis.createClient({
|
|
|
|
host: config.redis.host,
|
|
|
|
port: config.redis.port,
|
|
|
|
password: config.redis.password,
|
|
|
|
});
|
2022-02-10 19:48:54 +08:00
|
|
|
|
2022-07-27 22:49:25 +08:00
|
|
|
client.on('error', (err) => logger.info('Redis Client Error', err));
|
2022-06-02 05:22:08 +08:00
|
|
|
|
2022-07-27 22:49:25 +08:00
|
|
|
await client.connect();
|
2022-06-28 19:37:29 +08:00
|
|
|
|
2022-07-27 22:49:25 +08:00
|
|
|
const presAnn = await client.hGetAll(exportJob.jobId);
|
|
|
|
|
|
|
|
// Remove annotations from Redis
|
|
|
|
await client.del(jobId);
|
|
|
|
|
|
|
|
const annotations = JSON.stringify(presAnn);
|
2022-02-10 01:14:52 +08:00
|
|
|
|
2022-07-27 22:49:25 +08:00
|
|
|
const whiteboard = JSON.parse(annotations);
|
|
|
|
const pages = JSON.parse(whiteboard.pages);
|
|
|
|
|
|
|
|
fs.writeFile(path.join(dropbox, 'whiteboard'), annotations, function(err) {
|
|
|
|
if (err) {
|
|
|
|
return logger.error(err);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Collect the presentation page files (PDF / PNG / JPEG)
|
|
|
|
// from the presentation directory
|
|
|
|
const presFile = path.join(exportJob.presLocation, exportJob.presId);
|
|
|
|
const pdfFile = `${presFile}.pdf`;
|
|
|
|
|
2022-08-17 02:27:40 +08:00
|
|
|
// Message to display conversion progress toast
|
|
|
|
const statusUpdate = {
|
|
|
|
envelope: {
|
|
|
|
name: config.log.msgName,
|
|
|
|
routing: {
|
|
|
|
sender: exportJob.module,
|
|
|
|
},
|
|
|
|
timestamp: (new Date()).getTime(),
|
|
|
|
},
|
|
|
|
core: {
|
|
|
|
header: {
|
|
|
|
name: config.log.msgName,
|
|
|
|
meetingId: exportJob.parentMeetingId,
|
|
|
|
userId: '',
|
|
|
|
},
|
|
|
|
body: {
|
|
|
|
presId: exportJob.presId,
|
|
|
|
pageNumber: 1,
|
|
|
|
totalPages: pages.length,
|
|
|
|
status: 'collecting',
|
|
|
|
error: false,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2022-07-27 22:49:25 +08:00
|
|
|
if (fs.existsSync(pdfFile)) {
|
|
|
|
for (const p of pages) {
|
|
|
|
const pageNumber = p.page;
|
|
|
|
const outputFile = path.join(dropbox, `slide${pageNumber}`);
|
|
|
|
|
|
|
|
// CairoSVG doesn't handle transparent SVG and PNG embeds properly,
|
|
|
|
// e.g., in rasterized text. So textboxes may get a black background
|
|
|
|
// when downloading/exporting repeatedly. To avoid that, we take slides
|
|
|
|
// from the uploaded file, but later probe the dimensions from the SVG
|
|
|
|
// so it matches what was shown in the browser.
|
|
|
|
|
|
|
|
const extract_png_from_pdf = [
|
|
|
|
'-png',
|
|
|
|
'-f', pageNumber,
|
|
|
|
'-l', pageNumber,
|
|
|
|
'-scale-to', config.collector.pngWidthRasterizedSlides,
|
|
|
|
'-singlefile',
|
|
|
|
'-cropbox',
|
|
|
|
pdfFile, outputFile,
|
2022-07-28 02:54:16 +08:00
|
|
|
];
|
2022-07-27 22:49:25 +08:00
|
|
|
|
2022-08-17 02:27:40 +08:00
|
|
|
try {
|
|
|
|
cp.spawnSync(config.shared.pdftocairo, extract_png_from_pdf, {shell: false});
|
|
|
|
} catch (error) {
|
|
|
|
logger.error(`Extracting slide ${pageNumber} failed for job ${jobId}: ${error.message}`);
|
|
|
|
statusUpdate.core.body.error = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
statusUpdate.core.body.pageNumber = pageNumber;
|
|
|
|
await client.publish(config.redis.channels.publish, JSON.stringify(statusUpdate));
|
|
|
|
statusUpdate.core.body.error = false;
|
2022-07-27 22:49:25 +08:00
|
|
|
}
|
|
|
|
// If PNG file already available
|
|
|
|
} else if (fs.existsSync(`${presFile}.png`)) {
|
|
|
|
fs.copyFileSync(`${presFile}.png`, path.join(dropbox, 'slide1.png'));
|
2022-08-17 02:27:40 +08:00
|
|
|
await client.publish(config.redis.channels.publish, JSON.stringify(statusUpdate));
|
2022-07-27 22:49:25 +08:00
|
|
|
// If JPEG file available
|
|
|
|
} else if (fs.existsSync(`${presFile}.jpeg`)) {
|
|
|
|
fs.copyFileSync(`${presFile}.jpeg`, path.join(dropbox, 'slide1.jpeg'));
|
2022-08-17 02:27:40 +08:00
|
|
|
await client.publish(config.redis.channels.publish, JSON.stringify(statusUpdate));
|
2022-07-27 22:49:25 +08:00
|
|
|
} else {
|
2022-08-17 02:27:40 +08:00
|
|
|
statusUpdate.core.body.error = true;
|
|
|
|
await client.publish(config.redis.channels.publish, JSON.stringify(statusUpdate));
|
|
|
|
client.disconnect();
|
|
|
|
return logger.error(`Presentation file missing for job ${exportJob.jobId}`);
|
2022-07-27 22:49:25 +08:00
|
|
|
}
|
|
|
|
|
2022-08-17 02:27:40 +08:00
|
|
|
client.disconnect();
|
|
|
|
kickOffProcessWorker(exportJob.jobId, statusUpdate);
|
2022-07-27 22:49:25 +08:00
|
|
|
})();
|