import/export: Promisify Abiword and LibreOffice conversion

This commit is contained in:
Richard Hansen 2021-03-18 01:01:47 -04:00 committed by John McLear
parent b321267e66
commit b2c0837cf5
4 changed files with 67 additions and 91 deletions

View file

@ -98,8 +98,7 @@ exports.doExport = async (req, res, padId, readOnlyId, type) => {
settings.soffice != null ? require('../utils/LibreOffice') settings.soffice != null ? require('../utils/LibreOffice')
: settings.abiword != null ? require('../utils/Abiword') : settings.abiword != null ? require('../utils/Abiword')
: null; : null;
// @TODO no Promise interface for converters (yet) await converter.convertFile(srcFile, destFile, type);
await util.promisify(converter.convertFile)(srcFile, destFile, type);
} }
// send the file // send the file

View file

@ -179,17 +179,12 @@ const doImport = async (req, res, padId) => {
// if no converter only rename // if no converter only rename
await fs.rename(srcFile, destFile); await fs.rename(srcFile, destFile);
} else { } else {
// @TODO - no Promise interface for converters (yet) try {
await new Promise((resolve, reject) => { await converter.convertFile(srcFile, destFile, exportExtension);
converter.convertFile(srcFile, destFile, exportExtension, (err) => { } catch (err) {
// catch convert errors
if (err) {
logger.warn(`Converting Error: ${err.stack || err}`); logger.warn(`Converting Error: ${err.stack || err}`);
return reject(new ImportError('convertFailed')); throw new ImportError('convertFailed');
} }
resolve();
});
});
} }
} }

View file

@ -24,27 +24,23 @@ const async = require('async');
const settings = require('./Settings'); const settings = require('./Settings');
const os = require('os'); const os = require('os');
let doConvertTask;
// on windows we have to spawn a process for each convertion, // on windows we have to spawn a process for each convertion,
// cause the plugin abicommand doesn't exist on this platform // cause the plugin abicommand doesn't exist on this platform
if (os.type().indexOf('Windows') > -1) { if (os.type().indexOf('Windows') > -1) {
doConvertTask = (task, callback) => { exports.convertFile = async (srcFile, destFile, type) => {
const abiword = spawn(settings.abiword, [`--to=${task.destFile}`, task.srcFile]); const abiword = spawn(settings.abiword, [`--to=${destFile}`, srcFile]);
let stdoutBuffer = ''; let stdoutBuffer = '';
abiword.stdout.on('data', (data) => { stdoutBuffer += data.toString(); }); abiword.stdout.on('data', (data) => { stdoutBuffer += data.toString(); });
abiword.stderr.on('data', (data) => { stdoutBuffer += data.toString(); }); abiword.stderr.on('data', (data) => { stdoutBuffer += data.toString(); });
await new Promise((resolve, reject) => {
abiword.on('exit', (code) => { abiword.on('exit', (code) => {
if (code !== 0) return callback(new Error(`Abiword died with exit code ${code}`)); if (code !== 0) return reject(new Error(`Abiword died with exit code ${code}`));
if (stdoutBuffer !== '') { if (stdoutBuffer !== '') {
console.log(stdoutBuffer); console.log(stdoutBuffer);
} }
callback(); resolve();
});
}); });
};
exports.convertFile = (srcFile, destFile, type, callback) => {
doConvertTask({srcFile, destFile, type}, callback);
}; };
// on unix operating systems, we can start abiword with abicommand and // on unix operating systems, we can start abiword with abicommand and
// communicate with it via stdin/stdout // communicate with it via stdin/stdout
@ -85,7 +81,7 @@ if (os.type().indexOf('Windows') > -1) {
}; };
}, 1); }, 1);
exports.convertFile = (srcFile, destFile, type, callback) => { exports.convertFile = async (srcFile, destFile, type) => {
queue.push({srcFile, destFile, type}, callback); await queue.pushAsync({srcFile, destFile, type});
}; };
} }

View file

@ -18,7 +18,7 @@
*/ */
const async = require('async'); const async = require('async');
const fs = require('fs'); const fs = require('fs').promises;
const log4js = require('log4js'); const log4js = require('log4js');
const os = require('os'); const os = require('os');
const path = require('path'); const path = require('path');
@ -27,14 +27,11 @@ const spawn = require('child_process').spawn;
const libreOfficeLogger = log4js.getLogger('LibreOffice'); const libreOfficeLogger = log4js.getLogger('LibreOffice');
const doConvertTask = (task, callback) => { const doConvertTask = async (task) => {
const tmpDir = os.tmpdir(); const tmpDir = os.tmpdir();
async.series([
(callback) => {
libreOfficeLogger.debug( libreOfficeLogger.debug(
`Converting ${task.srcFile} to format ${task.type}. The result will be put in ${tmpDir}` `Converting ${task.srcFile} to format ${task.type}. The result will be put in ${tmpDir}`);
);
const soffice = spawn(settings.soffice, [ const soffice = spawn(settings.soffice, [
'--headless', '--headless',
'--invisible', '--invisible',
@ -56,28 +53,29 @@ const doConvertTask = (task, callback) => {
let stdoutBuffer = ''; let stdoutBuffer = '';
soffice.stdout.on('data', (data) => { stdoutBuffer += data.toString(); }); soffice.stdout.on('data', (data) => { stdoutBuffer += data.toString(); });
soffice.stderr.on('data', (data) => { stdoutBuffer += data.toString(); }); soffice.stderr.on('data', (data) => { stdoutBuffer += data.toString(); });
await new Promise((resolve, reject) => {
soffice.on('exit', (code) => { soffice.on('exit', (code) => {
clearTimeout(hangTimeout); clearTimeout(hangTimeout);
if (code !== 0) { if (code !== 0) {
return callback( return reject(
new Error(`LibreOffice died with exit code ${code} and message: ${stdoutBuffer}`)); new Error(`LibreOffice died with exit code ${code} and message: ${stdoutBuffer}`));
} }
callback(); resolve();
});
}); });
},
(callback) => {
const filename = path.basename(task.srcFile); const filename = path.basename(task.srcFile);
const sourceFile = `${filename.substr(0, filename.lastIndexOf('.'))}.${task.fileExtension}`; const sourceFile = `${filename.substr(0, filename.lastIndexOf('.'))}.${task.fileExtension}`;
const sourcePath = path.join(tmpDir, sourceFile); const sourcePath = path.join(tmpDir, sourceFile);
libreOfficeLogger.debug(`Renaming ${sourcePath} to ${task.destFile}`); libreOfficeLogger.debug(`Renaming ${sourcePath} to ${task.destFile}`);
fs.rename(sourcePath, task.destFile, callback); await fs.rename(sourcePath, task.destFile);
},
], callback);
}; };
// Conversion tasks will be queued up, so we don't overload the system // Conversion tasks will be queued up, so we don't overload the system
const queue = async.queue(doConvertTask, 1); const queue = async.queue(
// For some reason util.callbackify() throws "TypeError [ERR_INVALID_ARG_TYPE]: The last
// argument must be of type Function. Received type object" on Node.js 10.x.
(task, cb) => doConvertTask(task).then(() => cb(), (err) => cb(err || new Error(err))), 1);
/** /**
* Convert a file from one type to another * Convert a file from one type to another
@ -87,7 +85,7 @@ const queue = async.queue(doConvertTask, 1);
* @param {String} type The type to convert into * @param {String} type The type to convert into
* @param {Function} callback Standard callback function * @param {Function} callback Standard callback function
*/ */
exports.convertFile = (srcFile, destFile, type, callback) => { exports.convertFile = async (srcFile, destFile, type) => {
// Used for the moving of the file, not the conversion // Used for the moving of the file, not the conversion
const fileExtension = type; const fileExtension = type;
@ -107,21 +105,9 @@ exports.convertFile = (srcFile, destFile, type, callback) => {
// to avoid `Error: no export filter for /tmp/xxxx.doc` error // to avoid `Error: no export filter for /tmp/xxxx.doc` error
if (type === 'doc') { if (type === 'doc') {
const intermediateFile = destFile.replace(/\.doc$/, '.odt'); const intermediateFile = destFile.replace(/\.doc$/, '.odt');
async.series([ await queue.pushAsync({srcFile, destFile: intermediateFile, type: 'odt', fileExtension: 'odt'});
(callback) => queue.push({ await queue.pushAsync({srcFile: intermediateFile, destFile, type, fileExtension});
srcFile,
destFile: intermediateFile,
type: 'odt',
fileExtension: 'odt',
}, callback),
(callback) => queue.push({
srcFile: intermediateFile,
destFile,
type,
fileExtension,
}, callback),
], callback);
} else { } else {
queue.push({srcFile, destFile, type, fileExtension}, callback); await queue.pushAsync({srcFile, destFile, type, fileExtension});
} }
}; };