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, ' '))); } 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(/
\s*?<\/p>/g, '