Procházet zdrojové kódy

Merge branch 'release/0.9.1'

Book Pauk před 5 roky
rodič
revize
34d9466d09

+ 28 - 12
client/components/Reader/SettingsPage/include/OthersTab.inc

@@ -54,6 +54,34 @@
 <!---------------------------------------------->
 <div class="part-header">Другое</div>
 
+<div class="item row">
+    <div class="label-6">Обработка</div>
+    <div class="col row">
+        <q-checkbox v-model="enableSitesFilter" @input="needTextReload" size="xs" label="Включить html-фильтр для сайтов">
+            <q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%">
+                Html-фильтр вырезает лишние элементы со<br>
+                страницы для определенных сайтов, таких как:<br>
+                samlib.ru<br>
+                www.fanfiction.net<br>
+                archiveofourown.org<br>
+                и других
+            </q-tooltip>
+        </q-checkbox>
+    </div>
+</div>
+
+<div class="item row">
+    <div class="label-6">Обработка</div>
+    <q-checkbox size="xs" v-model="lazyParseEnabled" label="Предварительная подготовка текста">
+        <q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%">
+            Включение этой опции позволяет делать предварительную<br>
+            подготовку всего текста в ленивом режиме сразу после<br>
+            загрузки книги. Это может повысить отзывчивость читалки,<br>
+            но нагружает процессор каждый раз при открытии книги.
+        </q-tooltip>
+    </q-checkbox>
+</div>
+
 <div class="item row">
     <div class="label-6">Парам. в URL</div>
     <q-checkbox size="xs" v-model="allowUrlParamBookPos">
@@ -68,18 +96,6 @@
     </q-checkbox>
 </div>
 
-<div class="item row">
-    <div class="label-6">Парсинг</div>
-    <q-checkbox size="xs" v-model="lazyParseEnabled" label="Предварительная обработка текста">
-        <q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%">
-            Включение этой опции позволяет делать предварительную<br>
-            обработку текста в ленивом режиме сразу после загрузки<br>
-            книги. Это может повысить отзывчивость читалки, но<br>
-            нагружает процессор каждый раз при открытии книги.
-        </q-tooltip>
-    </q-checkbox>
-</div>
-
 <div class="item row">
     <div class="label-6">Копирование</div>
     <q-checkbox size="xs" v-model="copyFullText" label="Загружать весь текст">

+ 12 - 0
client/components/Reader/SettingsPage/include/ViewTab/Status.inc

@@ -22,3 +22,15 @@
         <NumInput class="col-left" v-model="statusBarColorAlpha" :min="0" :max="1" :digits="2" :step="0.1" :disable="!showStatusBar"/>
     </div>
 </div>
+
+<div class="item row">
+    <div class="label-2"></div>
+    <div class="col row">
+        <q-checkbox v-model="statusBarClickOpen" size="xs" label="Открывать оригинал по клику">
+            <q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%">
+                По клику на автора-название в строке статуса<br>
+                открывать оригинал произведения в новой вкладке
+            </q-tooltip>
+        </q-checkbox>
+    </div>
+</div>

+ 0 - 16
client/components/Reader/SettingsPage/include/ViewTab/Text.inc

@@ -108,22 +108,6 @@
     <NumInput class="col" v-model="addEmptyParagraphs" :min="0" :max="2"/>
 </div>
 
-<div class="item row">
-    <div class="label-2"></div>
-    <div class="col row">
-        <q-checkbox v-model="enableSitesFilter" @input="needTextReload" size="xs" label="Включить html-фильтр для сайтов">
-            <q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%">
-                Html-фильтр вырезает лишние элементы со<br>
-                страницы для определенных сайтов, таких как:<br>
-                samlib.ru<br>
-                www.fanfiction.net<br>
-                archiveofourown.org<br>
-                и других
-            </q-tooltip>
-        </q-checkbox>
-    </div>
-</div>
-
 <div class="item row">
     <div class="label-2">Изображения</div>
     <div class="col row">

+ 4 - 3
client/components/Reader/TextPage/TextPage.vue

@@ -21,11 +21,12 @@
             @wheel.prevent.stop="onMouseWheel"
             @touchstart.stop="onTouchStart" @touchend.stop="onTouchEnd" @touchmove.stop="onTouchMove" @touchcancel.prevent.stop="onTouchCancel"
             oncontextmenu="return false;">
-            <div v-show="showStatusBar" v-html="statusBarClickable" @mousedown.prevent.stop @touchstart.stop
+            <div v-show="showStatusBar && statusBarClickOpen" v-html="statusBarClickable" @mousedown.prevent.stop @touchstart.stop
                 @click.prevent.stop="onStatusBarClick"></div>
         </div>
-        <div v-show="!clickControl && showStatusBar" class="layout" v-html="statusBarClickable" @mousedown.prevent.stop @touchstart.stop
-            @click.prevent.stop="onStatusBarClick"></div>
+        <div v-show="!clickControl && showStatusBar && statusBarClickOpen" class="layout" v-html="statusBarClickable" @mousedown.prevent.stop @touchstart.stop
+            @click.prevent.stop="onStatusBarClick">
+        </div>
         <!-- невидимым делать нельзя, вовремя не подгружаютя шрифты -->
         <canvas ref="offscreenCanvas" class="layout" style="visibility: hidden"></canvas>
         <div ref="measureWidth" style="position: absolute; visibility: hidden"></div>

+ 12 - 0
client/components/Reader/versionHistory.js

@@ -1,4 +1,16 @@
 export const versionHistory = [
+{
+    showUntil: '2020-03-02',
+    header: '0.9.1 (2020-03-03)',
+    content:
+`
+<ul>
+    <li>улучшение работы серверной части</li>
+    <li>незначительные изменения интерфейса</li>
+</ul>
+`
+},
+
 {
     showUntil: '2020-02-25',
     header: '0.9.0 (2020-02-26)',

+ 0 - 1
client/quasar.js

@@ -80,7 +80,6 @@ Vue.use(Quasar, { config, components, directives, plugins });
 //import '@quasar/extras/material-icons-outlined/material-icons-outlined.css';
 //import '@quasar/extras/fontawesome-v5/fontawesome-v5.css';
 
-//import '@quasar/extras/material-icons-outlined/material-icons-outlined.css';
 import '@quasar/extras/line-awesome/line-awesome.css';
 
 //import fontawesomeV5 from 'quasar/icon-set/fontawesome-v5.js'

+ 1 - 0
client/store/modules/reader.js

@@ -160,6 +160,7 @@ const settingDefaults = {
     statusBarTop: false,// top, bottom
     statusBarHeight: 19,// px
     statusBarColorAlpha: 0.4,
+    statusBarClickOpen: true,
 
     scrollingDelay: 3000,// замедление, ms
     scrollingType: 'ease-in-out', //linear, ease, ease-in, ease-out, ease-in-out

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 480 - 221
package-lock.json


+ 18 - 20
package.json

@@ -1,6 +1,6 @@
 {
   "name": "Liberama",
-  "version": "0.9.0",
+  "version": "0.9.1",
   "author": "Book Pauk <bookpauk@gmail.com>",
   "license": "CC0-1.0",
   "repository": "bookpauk/liberama",
@@ -22,7 +22,7 @@
   },
   "devDependencies": {
     "babel-core": "^6.22.1",
-    "babel-eslint": "^10.0.3",
+    "babel-eslint": "^10.1.0",
     "babel-loader": "^7.1.1",
     "babel-plugin-component": "^1.1.1",
     "babel-plugin-syntax-dynamic-import": "^6.18.0",
@@ -32,7 +32,6 @@
     "clean-webpack-plugin": "^1.0.1",
     "copy-webpack-plugin": "^5.1.1",
     "css-loader": "^1.0.0",
-    "element-theme-chalk": "^2.12.0",
     "eslint": "^5.16.0",
     "eslint-plugin-html": "^5.0.5",
     "eslint-plugin-node": "^8.0.0",
@@ -41,27 +40,26 @@
     "html-webpack-plugin": "^3.2.0",
     "mini-css-extract-plugin": "^0.5.0",
     "optimize-css-assets-webpack-plugin": "^5.0.3",
-    "pkg": "^4.4.2",
+    "pkg": "^4.4.4",
     "terser-webpack-plugin": "^1.4.1",
     "url-loader": "^1.1.2",
     "vue-class-component": "^6.3.2",
-    "vue-loader": "^15.7.1",
+    "vue-loader": "^15.9.0",
     "vue-style-loader": "^4.1.2",
-    "vue-template-compiler": "^2.6.10",
-    "webpack": "^4.39.3",
-    "webpack-cli": "^3.3.7",
-    "webpack-dev-middleware": "^3.7.1",
+    "vue-template-compiler": "^2.6.11",
+    "webpack": "^4.42.0",
+    "webpack-cli": "^3.3.11",
+    "webpack-dev-middleware": "^3.7.2",
     "webpack-hot-middleware": "^2.25.0",
     "webpack-merge": "^4.2.2"
   },
   "dependencies": {
-    "@quasar/extras": "^1.5.0",
+    "@quasar/extras": "^1.5.2",
     "appcache-webpack-plugin": "^1.4.0",
     "axios": "^0.18.1",
-    "base-x": "^3.0.6",
+    "base-x": "^3.0.8",
     "chardet": "^0.7.0",
     "compression": "^1.7.4",
-    "element-ui": "^2.12.0",
     "express": "^4.17.1",
     "fg-loadcss": "^2.1.0",
     "fs-extra": "^7.0.1",
@@ -72,21 +70,21 @@
     "lodash": "^4.17.15",
     "minimist": "^1.2.0",
     "multer": "^1.4.2",
-    "pako": "^1.0.10",
+    "pako": "^1.0.11",
     "path-browserify": "^1.0.0",
-    "quasar": "^1.8.5",
+    "quasar": "^1.9.6",
     "safe-buffer": "^5.2.0",
     "sjcl": "^1.0.8",
     "sql-template-strings": "^2.2.2",
     "sqlite": "^3.0.3",
     "tar-fs": "^2.0.0",
     "unbzip2-stream": "^1.3.3",
-    "vue": "github:paulkamer/vue#fix_palemoon_clickhandlers_dist",
-    "vue-router": "^3.1.3",
-    "vuex": "^3.1.1",
-    "vuex-persistedstate": "^2.5.4",
-    "webdav": "^2.10.1",
+    "vue": "github:bookpauk/vue",
+    "vue-router": "^3.1.6",
+    "vuex": "^3.1.2",
+    "vuex-persistedstate": "^2.7.1",
+    "webdav": "^2.10.2",
     "ws": "^7.2.1",
-    "zip-stream": "^2.1.2"
+    "zip-stream": "^2.1.3"
   }
 }

+ 14 - 12
server/core/FileDecompressor.js

@@ -241,27 +241,29 @@ class FileDecompressor {
         });
     }
 
-    async gzipFileIfNotExists(filename, outDir) {
+    async gzipFileIfNotExists(filename, outDir, isMaxCompression) {
         const hash = await utils.getFileHash(filename, 'sha256', 'hex');
 
         const outFilename = `${outDir}/${hash}`;
 
         if (!await fs.pathExists(outFilename)) {
-            await this.gzipFile(filename, outFilename, 1);
+            await this.gzipFile(filename, outFilename, (isMaxCompression ? 9 : 1));
 
-            // переупакуем через некоторое время на максималках
-            const filenameCopy = `${filename}.copy`;
-            await fs.copy(filename, filenameCopy);
+            // переупакуем через некоторое время на максималках, если упаковали плохо
+            if (!isMaxCompression) {
+                const filenameCopy = `${filename}.copy`;
+                await fs.copy(filename, filenameCopy);
 
-            (async() => {
-                await utils.sleep(5000);
-                const filenameGZ = `${filename}.gz`;
-                await this.gzipFile(filenameCopy, filenameGZ, 9);
+                (async() => {
+                    await utils.sleep(5000);
+                    const filenameGZ = `${filename}.gz`;
+                    await this.gzipFile(filenameCopy, filenameGZ, 9);
 
-                await fs.move(filenameGZ, outFilename, {overwrite: true});
+                    await fs.move(filenameGZ, outFilename, {overwrite: true});
 
-                await fs.remove(filenameCopy);
-            })().catch((e) => { if (appLogger.inited) appLogger.log(LM_ERR, `FileDecompressor.gzipFileIfNotExists: ${e.message}`) });
+                    await fs.remove(filenameCopy);
+                })().catch((e) => { if (appLogger.inited) appLogger.log(LM_ERR, `FileDecompressor.gzipFileIfNotExists: ${e.message}`) });
+            }
         } else {
             await utils.touchFile(outFilename);
         }

+ 47 - 17
server/core/Reader/ReaderWorker.js

@@ -54,6 +54,7 @@ class ReaderWorker {
         let decompDir = '';
         let downloadedFilename = '';
         let isUploaded = false;
+        let isRestored = false;
         let convertFilename = '';
 
         const overLoadMes = 'Слишком большая очередь загрузки. Пожалуйста, попробуйте позже.';
@@ -88,9 +89,17 @@ class ReaderWorker {
                 downloadedFilename = `${this.config.tempDownloadDir}/${tempFilename}`;
                 await fs.writeFile(downloadedFilename, downdata);
             } else {//uploaded file
-                downloadedFilename = `${this.config.uploadDir}/${url.substr(7)}`;
-                if (!await fs.pathExists(downloadedFilename)) 
-                    throw new Error('Файл не найден на сервере (возможно был удален как устаревший). Пожалуйста, загрузите файл с диска на сервер заново.');
+                const fileHash = url.substr(7);
+                downloadedFilename = `${this.config.uploadDir}/${fileHash}`;
+                if (!await fs.pathExists(downloadedFilename)) {
+                    //если удалено из upload, попробуем восстановить из удаленного хранилища
+                    try {
+                        downloadedFilename = await this.restoreRemoteFile(fileHash);
+                        isRestored = true;
+                    } catch(e) {
+                        throw new Error('Файл не найден на сервере (возможно был удален как устаревший). Пожалуйста, загрузите файл с диска на сервер заново.');
+                    }
+                }
                 await utils.touchFile(downloadedFilename);
                 isUploaded = true;
             }
@@ -146,6 +155,20 @@ class ReaderWorker {
                 })();
             }
 
+            //лениво сохраним downloadedFilename в tmp и в удаленном хранилище в случае isUploaded
+            if (this.remoteWebDavStorage && isUploaded && !isRestored) {
+                (async() => {
+                    await utils.sleep(30*1000);
+                    try {
+                        //сжимаем файл в tmp, если там уже нет с тем же именем-sha256
+                        const compDownloadedFilename = await this.decomp.gzipFileIfNotExists(downloadedFilename, this.config.tempPublicDir, true);
+                        await this.remoteWebDavStorage.putFile(compDownloadedFilename);
+                    } catch (e) {
+                        log(LM_ERR, e.stack);
+                    }
+                })();
+            }
+
         } catch (e) {
             log(LM_ERR, e.stack);
             if (e.message == 'abort')
@@ -188,6 +211,24 @@ class ReaderWorker {
         return `file://${hash}`;
     }
 
+    async restoreRemoteFile(filename) {
+        const basename = path.basename(filename);
+        const targetName = `${this.config.tempPublicDir}/${basename}`;
+
+        if (!await fs.pathExists(targetName)) {
+            let found = false;
+            if (this.remoteWebDavStorage) {
+                found = await this.remoteWebDavStorage.getFileSuccess(targetName);
+            }
+
+            if (!found) {
+                throw new Error('404 Файл не найден');
+            }
+        }
+
+        return targetName;
+    }
+
     restoreCachedFile(filename) {
         const workerId = this.workerState.generateWorkerId();
         const wState = this.workerState.getControl(workerId);
@@ -197,21 +238,10 @@ class ReaderWorker {
             try {
                 wState.set({state: 'download', step: 1, totalSteps: 1, path: filename, progress: 0});
 
-                const basename = path.basename(filename);
-                const targetName = `${this.config.tempPublicDir}/${basename}`;
-
-                if (!await fs.pathExists(targetName)) {
-                    let found = false;
-                    if (this.remoteWebDavStorage) {
-                        found = await this.remoteWebDavStorage.getFileSuccess(targetName);
-                    } 
-
-                    if (!found) {
-                        throw new Error('404 Файл не найден');
-                    }
-                }
-
+                const targetName = await this.restoreRemoteFile(filename);
                 const stat = await fs.stat(targetName);
+
+                const basename = path.basename(filename);
                 wState.finish({path: `/tmp/${basename}`, size: stat.size, progress: 100});
             } catch (e) {
                 if (e.message.indexOf('404') < 0)

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů