浏览代码

Работа над проектом

Book Pauk 2 年之前
父节点
当前提交
e6d0c6e519

+ 10 - 0
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'});
 

+ 2 - 0
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});
     }
 }
 

+ 40 - 25
client/components/Search/Search.vue

@@ -148,8 +148,8 @@
                 </div>
 
                 <div v-if="isExpanded(item) && item.books">
-                    <div v-for="book in item.books" :key="book.key" class="book-row column">
-                        <div v-if="book.type == 'series'" class="column">
+                    <div v-for="book in item.books" :key="book._key" class="book-row column">
+                        <div v-if="book._type == 'series'" class="column">
                             <div class="row items-center q-mr-xs no-wrap text-grey-9">
                                 <div class="row items-center clickable2 q-py-xs no-wrap" @click="expandSeries(book)">
                                     <div style="min-width: 30px">
@@ -168,7 +168,7 @@
                             </div>
 
                             <div v-if="isExpandedSeries(book) && book.books" class="book-row column">
-                                <BookView v-for="subbook in book.books" :key="subbook.key" :book="subbook" :genre-tree="genreTree" @book-event="bookEvent" />
+                                <BookView v-for="subbook in book.books" :key="subbook._key" :book="subbook" :genre-tree="genreTree" @book-event="bookEvent" />
                             </div>
                         </div>
                         <BookView v-else :book="book" :genre-tree="genreTree" @book-event="bookEvent" />
@@ -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]);
             }
 

+ 1 - 1
server/core/InpxParser.js

@@ -80,7 +80,7 @@ class InpxParser {
             }
             
         } finally {
-            zipReader.close();
+            await zipReader.close();
         }
     }
 

+ 62 - 35
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 = '';
-
-        //найдем хеш
-        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}`;
-
-            if (!await fs.pathExists(publicPath)) {
-                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 = `${this.config.publicDir}${link}`;
+
+                if (!await fs.pathExists(publicPath)) {
+                    link = '';
+                }
             }
-        }
 
-        if (!link) {
-            link = await this.restoreBook(bookPath)
-        }
+            if (!link) {
+                link = await this.restoreBook(bookPath)
+            }
 
-        if (!link)
-            throw new Error('404 Файл не найден');
+            if (!link)
+                throw new Error('404 Файл не найден');
 
-        return {link};
+            return {link};
+        } catch(e) {
+            log(LM_ERR, `getBookLink error: ${e.message}`);
+            if (e.message.indexOf('ENOENT') >= 0)
+                throw new Error('404 Файл не найден');
+            throw e;
+        }
     }
 
     async restoreBookFile(publicPath) {
-        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 Файл не найден');
+        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 Файл не найден');
+            }
+        } catch(e) {
+            log(LM_ERR, `restoreBookFile error: ${e.message}`);
+            if (e.message.indexOf('ENOENT') >= 0)
+                throw new Error('404 Файл не найден');
+            throw e;
         }
     }
 

+ 2 - 2
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;
         }

+ 1 - 1
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();