diff --git a/docs/omnireader.ru/README.md b/docs/omnireader.ru/README.md index b5149069..2df98c7b 100644 --- a/docs/omnireader.ru/README.md +++ b/docs/omnireader.ru/README.md @@ -34,6 +34,7 @@ sudo -u www-data tar xvf calibre-5.5.0-x86_64.txz -C /home/liberama/data/calibre ### external converters ``` +sudo apt install rar sudo apt install libreoffice sudo apt install poppler-utils ``` diff --git a/server/core/FileDecompressor.js b/server/core/FileDecompressor.js index ab2e58b2..65000227 100644 --- a/server/core/FileDecompressor.js +++ b/server/core/FileDecompressor.js @@ -15,6 +15,13 @@ class FileDecompressor { constructor(limitFileSize = 0) { this.detector = new FileDetector(); this.limitFileSize = limitFileSize; + + this.rarPath = '/usr/bin/rar'; + this.rarExists = false; + (async() => { + if (await fs.pathExists(this.rarPath)) + this.rarExists = true; + })(); } async decompressNested(filename, outputDir) { @@ -30,7 +37,11 @@ class FileDecompressor { files: [] }; - if (!fileType || !(fileType.ext == 'zip' || fileType.ext == 'bz2' || fileType.ext == 'gz' || fileType.ext == 'tar')) { + if (!fileType || !( + fileType.ext == 'zip' || fileType.ext == 'bz2' || fileType.ext == 'gz' + || fileType.ext == 'tar' || (this.rarExists && fileType.ext == 'rar') + ) + ) { return result; } @@ -94,6 +105,11 @@ class FileDecompressor { async decompress(fileExt, filename, outputDir) { let files = []; + if (fileExt == 'rar' && this.rarExists) { + files = await this.unRar(filename, outputDir); + return files; + } + switch (fileExt) { case 'zip': files = await this.unZip(filename, outputDir); @@ -123,8 +139,7 @@ class FileDecompressor { decodeEntryNameCallback: (nameRaw) => { return utils.bufferRemoveZeroes(nameRaw); } - } -); + }); } catch (e) { fs.emptyDir(outputDir); return await zip.unpack(filename, outputDir, { @@ -222,7 +237,39 @@ class FileDecompressor { inputStream.pipe(stream).pipe(outputStream); })().catch(reject); }); - } + } + + async unRar(filename, outputDir) { + try { + const args = ['e', '-y', filename, `${outputDir}/`]; + const result = await utils.spawnProcess(this.rarPath, { + killAfter: 60, + args + }); + + if (result.code == 0) { + const files = []; + await utils.findFiles(async(file) => { + const stat = await fs.stat(file); + files.push({path: path.relative(outputDir, file), size: stat.size}); + }, outputDir); + + return files; + + } else { + const error = `${result.code}|FORLOG|, exec: ${this.rarPath}, args: ${args.join(' ')}, stdout: ${result.stdout}, stderr: ${result.stderr}`; + throw new Error(`Архиватор Rar завершился с ошибкой: ${error}`); + } + } catch(e) { + if (e.status == 'killed') { + throw new Error('Слишком долгое ожидание архиватора Rar'); + } else if (e.status == 'error') { + throw new Error(e.error); + } else { + throw new Error(e); + } + } + } async gzipBuffer(buf) { return new Promise((resolve, reject) => { diff --git a/server/core/utils.js b/server/core/utils.js index f5190ded..df27a487 100644 --- a/server/core/utils.js +++ b/server/core/utils.js @@ -1,5 +1,6 @@ const { spawn } = require('child_process'); const fs = require('fs-extra'); +const path = require('path'); const crypto = require('crypto'); const baseX = require('base-x'); @@ -91,6 +92,22 @@ function spawnProcess(cmd, opts) { })().catch(reject); }); } +async function findFiles(callback, dir) { + if (!(callback && dir)) + return; + let result = true; + const files = await fs.readdir(dir, { withFileTypes: true }); + + for (const file of files) { + const found = path.resolve(dir, file.name); + if (file.isDirectory()) + result = await findFiles(callback, found); + else + await callback(found); + } + return result; +} + module.exports = { toBase36, fromBase36, @@ -99,5 +116,6 @@ module.exports = { sleep, randomHexString, touchFile, - spawnProcess + spawnProcess, + findFiles }; \ No newline at end of file