Files
liberama/server/core/Reader/BookConverter/ConvertBase.js

123 lines
4.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const fs = require('fs-extra');
const iconv = require('iconv-lite');
const he = require('he');
const LimitedQueue = require('../../LimitedQueue');
const textUtils = require('./textUtils');
const utils = require('../../utils');
const xmlParser = require('../../xmlParser');
const queue = new LimitedQueue(3, 20, 2*60*1000);//2 минуты ожидание подвижек
class ConvertBase {
constructor(config) {
this.config = config;
this.calibrePath = `${config.dataDir}/calibre/ebook-convert`;
this.sofficePath = '/usr/bin/soffice';
}
async run(data, opts) {// eslint-disable-line no-unused-vars
//override
}
async checkExternalConverterPresent() {
if (!await fs.pathExists(this.calibrePath))
throw new Error('Внешний конвертер calibre не найден');
if (!await fs.pathExists(this.sofficePath))
throw new Error('Внешний конвертер LibreOffice не найден');
}
async execConverter(path, args, onData, abort) {
onData = (onData ? onData : () => {});
let q = null;
try {
q = await queue.get(() => {onData();});
} catch (e) {
throw new Error('Слишком большая очередь конвертирования. Пожалуйста, попробуйте позже.');
}
abort = (abort ? abort : () => false);
const myAbort = () => {
return q.abort() || abort();
}
try {
if (myAbort())
throw new Error('abort');
const result = await utils.spawnProcess(path, {
killAfter: 3600,//1 час
args,
onData: (data) => {
if (queue.freed > 0)
q.resetTimeout();
onData(data);
},
//будем периодически проверять работу конвертера и если очереди нет, то разрешаем работу пинком onData
onUsage: (stats) => {
if (queue.freed > 0 && stats.cpu >= 10) {
q.resetTimeout();
onData('.');
}
},
onUsageInterval: 10,
abort: myAbort
});
if (result.code != 0) {
const error = `${result.code}|FORLOG|, exec: ${path}, args: ${args.join(' ')}, stdout: ${result.stdout}, stderr: ${result.stderr}`;
throw new Error(`Внешний конвертер завершился с ошибкой: ${error}`);
}
return result;
} catch(e) {
if (e.status == 'killed') {
throw new Error('Слишком долгое ожидание конвертера');
} else if (e.status == 'abort') {
throw new Error('abort');
} else if (e.status == 'error') {
throw new Error(e.error);
} else {
throw new Error(e);
}
} finally {
q.ret();
}
}
decode(data) {
let selected = textUtils.getEncoding(data);
if (selected.toLowerCase() != 'utf-8')
return iconv.decode(data, selected);
else
return data;
}
repSpaces(text) {
return text.replace(/ |[\t\n\r]/g, ' ');
}
escapeEntities(text) {
return he.escape(he.decode(text.replace(/ /g, ' ')));
}
isDataXml(data) {
const str = data.slice(0, 100).toString().trim();
return (str.indexOf('<?xml version="1.0"') == 0 || str.indexOf('<?xml version=\'1.0\'') == 0 );
}
formatFb2(fb2) {
const out = xmlParser.formatXml({
FictionBook: {
_attrs: {xmlns: 'http://www.gribuser.ru/xml/fictionbook/2.0', 'xmlns:l': 'http://www.w3.org/1999/xlink'},
_a: [fb2],
}
}, 'utf-8', this.repSpaces);
return out.replace(/<p>\s*?<\/p>/g, '<empty-line/>');
}
}
module.exports = ConvertBase;