Ver Fonte

Добавлена поддержка rar-архивов с помощью внешнего архиватора

Book Pauk há 4 anos atrás
pai
commit
2f82b0db34
3 ficheiros alterados com 71 adições e 5 exclusões
  1. 1 0
      docs/omnireader.ru/README.md
  2. 51 4
      server/core/FileDecompressor.js
  3. 19 1
      server/core/utils.js

+ 1 - 0
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
 ### external converters
 ```
 ```
+sudo apt install rar
 sudo apt install libreoffice
 sudo apt install libreoffice
 sudo apt install poppler-utils
 sudo apt install poppler-utils
 ```
 ```

+ 51 - 4
server/core/FileDecompressor.js

@@ -15,6 +15,13 @@ class FileDecompressor {
     constructor(limitFileSize = 0) {
     constructor(limitFileSize = 0) {
         this.detector = new FileDetector();
         this.detector = new FileDetector();
         this.limitFileSize = limitFileSize;
         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) {
     async decompressNested(filename, outputDir) {
@@ -30,7 +37,11 @@ class FileDecompressor {
             files: []
             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;
             return result;
         }
         }
 
 
@@ -94,6 +105,11 @@ class FileDecompressor {
     async decompress(fileExt, filename, outputDir) {
     async decompress(fileExt, filename, outputDir) {
         let files = [];
         let files = [];
 
 
+        if (fileExt == 'rar' && this.rarExists) {
+            files = await this.unRar(filename, outputDir);
+            return files;
+        }
+
         switch (fileExt) {
         switch (fileExt) {
             case 'zip':
             case 'zip':
                 files = await this.unZip(filename, outputDir);
                 files = await this.unZip(filename, outputDir);
@@ -123,8 +139,7 @@ class FileDecompressor {
                 decodeEntryNameCallback: (nameRaw) => {
                 decodeEntryNameCallback: (nameRaw) => {
                     return utils.bufferRemoveZeroes(nameRaw);
                     return utils.bufferRemoveZeroes(nameRaw);
                 }
                 }
-            }
-);
+            });
         } catch (e) {
         } catch (e) {
             fs.emptyDir(outputDir);
             fs.emptyDir(outputDir);
             return await zip.unpack(filename, outputDir, {
             return await zip.unpack(filename, outputDir, {
@@ -222,7 +237,39 @@ class FileDecompressor {
         
         
             inputStream.pipe(stream).pipe(outputStream);
             inputStream.pipe(stream).pipe(outputStream);
         })().catch(reject); });
         })().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) {
     async gzipBuffer(buf) {
         return new Promise((resolve, reject) => {
         return new Promise((resolve, reject) => {

+ 19 - 1
server/core/utils.js

@@ -1,5 +1,6 @@
 const { spawn } = require('child_process');
 const { spawn } = require('child_process');
 const fs = require('fs-extra');
 const fs = require('fs-extra');
+const path = require('path');
 const crypto = require('crypto');
 const crypto = require('crypto');
 const baseX = require('base-x');
 const baseX = require('base-x');
 
 
@@ -91,6 +92,22 @@ function spawnProcess(cmd, opts) {
     })().catch(reject); });
     })().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 = {
 module.exports = {
     toBase36,
     toBase36,
     fromBase36,
     fromBase36,
@@ -99,5 +116,6 @@ module.exports = {
     sleep,
     sleep,
     randomHexString,
     randomHexString,
     touchFile,
     touchFile,
-    spawnProcess
+    spawnProcess,
+    findFiles
 };
 };