diff --git a/client/components/Api/Api.vue b/client/components/Api/Api.vue index 924c2cf..014c0c1 100644 --- a/client/components/Api/Api.vue +++ b/client/components/Api/Api.vue @@ -166,6 +166,16 @@ class Api { return response; } + async getBookLink(bookPath) { + const response = await this.request({action: 'get-book-link', bookPath}); + + if (response.error) { + throw new Error(response.error); + } + + return response; + } + async getConfig() { const response = await this.request({action: 'get-config'}); diff --git a/client/components/Search/BookView/BookView.vue b/client/components/Search/BookView/BookView.vue index f5e7208..6694e7a 100644 --- a/client/components/Search/BookView/BookView.vue +++ b/client/components/Search/BookView/BookView.vue @@ -89,9 +89,11 @@ class BookView { } download() { + this.$emit('bookEvent', {action: 'download', book: this.book}); } copyLink() { + this.$emit('bookEvent', {action: 'copyLink', book: this.book}); } } diff --git a/client/components/Search/Search.vue b/client/components/Search/Search.vue index 567e816..b9f16d8 100644 --- a/client/components/Search/Search.vue +++ b/client/components/Search/Search.vue @@ -148,8 +148,8 @@
-
-
+
+
@@ -168,7 +168,7 @@
- +
@@ -528,7 +528,7 @@ class Search { if (item.books) { let count = 0; for (const book of item.books) { - if (book.type == 'series') + if (book._type == 'series') count += book.books.length; else count++; @@ -553,11 +553,31 @@ class Search { this.search.title = ''; } + async download(book, copy = false) { + try { + const bookPath = `${book.folder}/${book.file}.${book.ext}`; + const response = await this.api.getBookLink(bookPath); + + if (!copy) { + // + } +console.log(response); + } catch(e) { + this.$root.stdDialog.alert(e.message, 'Ошибка'); + } + } + bookEvent(event) { switch (event.action) { case 'titleClick': this.search.title = `=${event.book.title}`; break; + case 'download': + this.download(event.book);//no await + break; + case 'copyLink': + this.download(event.book, true);//no await + break; } } @@ -566,7 +586,7 @@ class Search { } isExpandedSeries(seriesItem) { - return this.expandedSeries.indexOf(seriesItem.key) >= 0; + return this.expandedSeries.indexOf(seriesItem._key) >= 0; } setSetting(name, newValue) { @@ -650,7 +670,7 @@ class Search { expandSeries(seriesItem) { const expandedSeries = _.cloneDeep(this.expandedSeries); - const key = seriesItem.key; + const key = seriesItem._key; if (!this.isExpandedSeries(seriesItem)) { expandedSeries.push(key); @@ -815,18 +835,13 @@ class Search { const filtered = this.filterBooks(loadedBooks); const prepareBook = (book) => { - return { - key: book.id, - type: 'book', - title: book.title, - series: book.series, - serno: book.serno, - genre: book.genre, - size: book.size, - ext: book.ext, - - src: book, - } + return Object.assign( + { + _key: book.id, + _type: 'book', + }, + book + ); }; //объединение по сериям @@ -838,8 +853,8 @@ class Search { if (index === undefined) { index = books.length; books.push({ - key: `${item.author}-${book.series}`, - type: 'series', + _key: `${item.author}-${book.series}`, + _type: 'series', series: book.series, books: [], @@ -856,16 +871,16 @@ class Search { //сортировка books.sort((a, b) => { - if (a.type == 'series') { - return (b.type == 'series' ? a.key.localeCompare(b.key) : -1); + if (a._type == 'series') { + return (b._type == 'series' ? a._key.localeCompare(b._key) : -1); } else { - return (b.type == 'book' ? a.title.localeCompare(b.title) : 1); + return (b._type == 'book' ? a.title.localeCompare(b.title) : 1); } }); //сортировка внутри серий for (const book of books) { - if (book.type == 'series') { + if (book._type == 'series') { book.books.sort((a, b) => { const dserno = (a.serno || Number.MAX_VALUE) - (b.serno || Number.MAX_VALUE); const dtitle = a.title.localeCompare(b.title); @@ -875,7 +890,7 @@ class Search { } } - if (books.length == 1 && books[0].type == 'series' && !this.isExpandedSeries(books[0])) { + if (books.length == 1 && books[0]._type == 'series' && !this.isExpandedSeries(books[0])) { this.expandSeries(books[0]); } diff --git a/server/core/InpxParser.js b/server/core/InpxParser.js index ae455fa..68dc179 100644 --- a/server/core/InpxParser.js +++ b/server/core/InpxParser.js @@ -80,7 +80,7 @@ class InpxParser { } } finally { - zipReader.close(); + await zipReader.close(); } } diff --git a/server/core/WebWorker.js b/server/core/WebWorker.js index cca0a6a..6940403 100644 --- a/server/core/WebWorker.js +++ b/server/core/WebWorker.js @@ -1,6 +1,7 @@ const os = require('os'); const path = require('path'); const fs = require('fs-extra'); +const zlib = require('zlib'); const _ = require('lodash'); const ZipReader = require('./ZipReader'); @@ -269,7 +270,7 @@ class WebWorker { const tempDir = this.config.tempDir; const outFile = `${tempDir}/${utils.randomHexString(30)}`; - const folder = path.dirname(bookPath); + const folder = `${this.config.libDir}/${path.dirname(bookPath)}`; const file = path.basename(bookPath); const zipReader = new ZipReader(); @@ -279,22 +280,35 @@ class WebWorker { await zipReader.extractToFile(file, outFile); return outFile; } finally { - zipReader.close(); + await zipReader.close(); } } + async gzipFile(inputFile, outputFile, level = 1) { + return new Promise((resolve, reject) => { + const gzip = zlib.createGzip({level}); + const input = fs.createReadStream(inputFile); + const output = fs.createWriteStream(outputFile); + + input.pipe(gzip).pipe(output).on('finish', (err) => { + if (err) reject(err); + else resolve(); + }); + }); + } + async restoreBook(bookPath) { const db = this.db; - const publicDir = this.config.publicDir; const extractedFile = await this.extractBook(bookPath); const hash = await utils.getFileHash(extractedFile, 'sha256', 'hex'); const link = `/files/${hash}`; - const publicPath = `${publicDir}${link}`; + const publicPath = `${this.config.publicDir}${link}`; if (!await fs.pathExists(publicPath)) { - await fs.move(extractedFile, publicPath); + await fs.ensureDir(path.dirname(publicPath)); + await this.gzipFile(extractedFile, publicPath, 4); } else { await fs.remove(extractedFile); } @@ -314,43 +328,56 @@ class WebWorker { async getBookLink(bookPath) { this.checkMyState(); - const db = this.db; - const publicDir = this.config.publicDir; - let link = ''; + try { + const db = this.db; + let link = ''; - //найдем хеш - const rows = await db.select({table: 'file_hash', where: `@@id(${db.esc(bookPath)})`}); - if (rows.length) {//хеш найден по bookPath - const hash = rows[0].hash; - link = `/files/${hash}`; - const publicPath = `${publicDir}${link}`; + //найдем хеш + const rows = await db.select({table: 'file_hash', where: `@@id(${db.esc(bookPath)})`}); + if (rows.length) {//хеш найден по bookPath + const hash = rows[0].hash; + link = `/files/${hash}`; + const publicPath = `${this.config.publicDir}${link}`; - if (!await fs.pathExists(publicPath)) { - link = ''; + if (!await fs.pathExists(publicPath)) { + link = ''; + } } + + if (!link) { + link = await this.restoreBook(bookPath) + } + + if (!link) + throw new Error('404 Файл не найден'); + + return {link}; + } catch(e) { + log(LM_ERR, `getBookLink error: ${e.message}`); + if (e.message.indexOf('ENOENT') >= 0) + throw new Error('404 Файл не найден'); + throw e; } - - if (!link) { - link = await this.restoreBook(bookPath) - } - - if (!link) - throw new Error('404 Файл не найден'); - - return {link}; } async restoreBookFile(publicPath) { - const db = this.db; - const hash = path.basename(publicPath); + try { + const db = this.db; + const hash = path.basename(publicPath); - //найдем bookPath - const rows = await db.select({table: 'file_hash', where: `@@id(${db.esc(hash)})`}); - if (rows.length) {//bookPath найден по хешу - const bookPath = rows[0].bookPath; - await this.restoreBook(bookPath); - } else {//bookPath не найден - throw new Error('404 Файл не найден'); + //найдем bookPath + const rows = await db.select({table: 'file_hash', where: `@@id(${db.esc(hash)})`}); + if (rows.length) {//bookPath найден по хешу + const bookPath = rows[0].bookPath; + await this.restoreBook(bookPath); + } else {//bookPath не найден + throw new Error('404 Файл не найден'); + } + } catch(e) { + log(LM_ERR, `restoreBookFile error: ${e.message}`); + if (e.message.indexOf('ENOENT') >= 0) + throw new Error('404 Файл не найден'); + throw e; } } diff --git a/server/core/ZipReader.js b/server/core/ZipReader.js index 1693144..698baa4 100644 --- a/server/core/ZipReader.js +++ b/server/core/ZipReader.js @@ -46,9 +46,9 @@ class ZipReader { await this.zip.extract(null, outputDir); } - close() { + async close() { if (this.zip) { - this.zip.close(); + await this.zip.close(); this.zip = null; this.zipEntries = undefined; } diff --git a/server/index.js b/server/index.js index 222667f..e68f80f 100644 --- a/server/index.js +++ b/server/index.js @@ -169,7 +169,7 @@ function initStatic(app, config) { await webWorker.restoreBookFile(publicPath); } } catch(e) { - log(LM_ERR, `static::restoreBookFile ${req.path} > ${e.message}`); + //quiet } return next();