From 8aa1da36b67e29e90273e960f82fb46e533d5188 Mon Sep 17 00:00:00 2001 From: Book Pauk Date: Sun, 13 Dec 2020 16:21:21 +0700 Subject: [PATCH 01/11] =?UTF-8?q?=D0=9D=D0=B5=D0=B1=D0=BE=D0=BB=D1=8C?= =?UTF-8?q?=D1=88=D0=B8=D0=B5=20=D0=BF=D0=BE=D0=BF=D1=80=D0=B0=D0=B2=D0=BA?= =?UTF-8?q?=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/core/Reader/BookConverter/ConvertDjvu.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/core/Reader/BookConverter/ConvertDjvu.js b/server/core/Reader/BookConverter/ConvertDjvu.js index 28e75d8e..ec645223 100644 --- a/server/core/Reader/BookConverter/ConvertDjvu.js +++ b/server/core/Reader/BookConverter/ConvertDjvu.js @@ -42,7 +42,7 @@ class ConvertDjvu extends ConvertBase { }, abort); const tifFileSize = (await fs.stat(tifFile)).size; - let limitSize = 3*this.config.maxUploadFileSize; + let limitSize = 4*this.config.maxUploadFileSize; if (tifFileSize > limitSize) { throw new Error(`Файл для конвертирования слишком большой|FORLOG| ${tifFileSize} > ${limitSize}`); } @@ -53,7 +53,7 @@ class ConvertDjvu extends ConvertBase { await fs.remove(tifFile); //конвертируем в jpg - await this.execConverter(mogrifyPath, ['-quality', '20', '-scale', '2048', '-verbose', '-format', 'jpg', `${dir}*.tif`], () => { + await this.execConverter(mogrifyPath, ['-quality', '20', '-scale', '2048>', '-verbose', '-format', 'jpg', `${dir}*.tif`], () => { perc = (perc < 100 ? perc + 1 : 40); callback(perc); }, abort); From 57b01dd2042d77f51f0903227aa048a28655ddcd Mon Sep 17 00:00:00 2001 From: Book Pauk Date: Sun, 13 Dec 2020 17:03:47 +0700 Subject: [PATCH 02/11] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3,=20=D0=B4=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BF=D0=BE=D0=B4=D0=B4=D0=B5?= =?UTF-8?q?=D1=80=D0=B6=D0=BA=D0=B0=20jpeg,=20png?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/Reader/BookConverter/ConvertDjvu.js | 62 ++---------- .../Reader/BookConverter/ConvertJpegPng.js | 95 +++++++++++++++++++ server/core/Reader/BookConverter/index.js | 1 + 3 files changed, 105 insertions(+), 53 deletions(-) create mode 100644 server/core/Reader/BookConverter/ConvertJpegPng.js diff --git a/server/core/Reader/BookConverter/ConvertDjvu.js b/server/core/Reader/BookConverter/ConvertDjvu.js index ec645223..22f899f2 100644 --- a/server/core/Reader/BookConverter/ConvertDjvu.js +++ b/server/core/Reader/BookConverter/ConvertDjvu.js @@ -2,9 +2,9 @@ const fs = require('fs-extra'); const path = require('path'); const utils = require('../../utils'); -const ConvertBase = require('./ConvertBase'); +const ConvertJpegPng = require('./ConvertJpegPng'); -class ConvertDjvu extends ConvertBase { +class ConvertDjvu extends ConvertJpegPng { check(data, opts) { const {inputFiles} = opts; @@ -16,7 +16,7 @@ class ConvertDjvu extends ConvertBase { if (!this.check(data, opts)) return false; - const {inputFiles, callback, abort, uploadFileName} = opts; + const {inputFiles, callback, abort} = opts; const ddjvuPath = '/usr/bin/ddjvu'; if (!await fs.pathExists(ddjvuPath)) @@ -31,8 +31,8 @@ class ConvertDjvu extends ConvertBase { throw new Error('Внешний конвертер mogrifyPath не найден'); const dir = `${inputFiles.filesDir}/`; - const inpFile = `${dir}${path.basename(inputFiles.sourceFile)}`; - const tifFile = `${inpFile}.tif`; + const baseFile = `${dir}${path.basename(inputFiles.sourceFile)}`; + const tifFile = `${baseFile}.tif`; //конвертируем в tiff let perc = 0; @@ -44,7 +44,7 @@ class ConvertDjvu extends ConvertBase { const tifFileSize = (await fs.stat(tifFile)).size; let limitSize = 4*this.config.maxUploadFileSize; if (tifFileSize > limitSize) { - throw new Error(`Файл для конвертирования слишком большой|FORLOG| ${tifFileSize} > ${limitSize}`); + throw new Error(`Файл для конвертирования слишком большой|FORLOG| tifFileSize: ${tifFileSize} > ${limitSize}`); } //разбиваем на файлы @@ -58,20 +58,7 @@ class ConvertDjvu extends ConvertBase { callback(perc); }, abort); - //читаем изображения - limitSize = 2*this.config.maxUploadFileSize; - let imagesSize = 0; - - const loadImage = async(image) => { - image.data = (await fs.readFile(image.file)).toString('base64'); - image.name = path.basename(image.file); - - imagesSize += image.data.length; - if (imagesSize > limitSize) { - throw new Error(`Файл для конвертирования слишком большой|FORLOG| imagesSize: ${imagesSize} > ${limitSize}`); - } - } - + //ищем изображения let files = []; await utils.findFiles(async(file) => { if (path.extname(file) == '.jpg') @@ -80,39 +67,8 @@ class ConvertDjvu extends ConvertBase { files.sort((a, b) => a.base.localeCompare(b.base)); - let images = []; - let loading = []; - files.forEach(f => { - const image = {file: f.name}; - images.push(image); - loading.push(loadImage(image)); - }); - - await Promise.all(loading); - - //формируем fb2 - let titleInfo = {}; - let desc = {_n: 'description', 'title-info': titleInfo}; - let pars = []; - let body = {_n: 'body', section: {_a: [pars]}}; - let binary = []; - let fb2 = [desc, body, binary]; - - let title = ''; - if (uploadFileName) - title = uploadFileName; - - titleInfo['book-title'] = title; - - for (const image of images) { - const img = {_n: 'binary', _attrs: {id: image.name, 'content-type': 'image/jpeg'}, _t: image.data}; - binary.push(img); - - pars.push({_n: 'p', _t: ''}); - pars.push({_n: 'image', _attrs: {'l:href': `#${image.name}`}}); - } - - return this.formatFb2(fb2); + await utils.sleep(100); + return await super.run(data, Object.assign({}, opts, {imageFiles: files.map(f => f.name)})); } } diff --git a/server/core/Reader/BookConverter/ConvertJpegPng.js b/server/core/Reader/BookConverter/ConvertJpegPng.js new file mode 100644 index 00000000..8a8debeb --- /dev/null +++ b/server/core/Reader/BookConverter/ConvertJpegPng.js @@ -0,0 +1,95 @@ +const fs = require('fs-extra'); +const path = require('path'); +//const utils = require('../../utils'); + +const ConvertBase = require('./ConvertBase'); + +class ConvertJpegPng extends ConvertBase { + check(data, opts) { + const {inputFiles} = opts; + + return this.config.useExternalBookConverter && + inputFiles.sourceFileType && + (inputFiles.sourceFileType.ext == 'jpg' || inputFiles.sourceFileType.ext == 'png' ); + } + + async run(data, opts) { + const {inputFiles, uploadFileName, imageFiles} = opts; + + if (!imageFiles) { + if (!this.check(data, opts)) + return false; + } + + let files = []; + if (imageFiles) { + files = imageFiles; + } else { + const imageFile = `${inputFiles.filesDir}/${path.basename(inputFiles.sourceFile)}.${inputFiles.sourceFileType.ext}`; + await fs.copy(inputFiles.sourceFile, imageFile); + files.push(imageFile); + } + + //читаем изображения + const limitSize = 2*this.config.maxUploadFileSize; + let imagesSize = 0; + + const loadImage = async(image) => { + const src = path.parse(image.src); + let type = 'unknown'; + switch (src.ext) { + case '.jpg': type = 'image/jpeg'; break; + case '.png': type = 'image/png'; break; + } + if (type != 'unknown') { + image.data = (await fs.readFile(image.src)).toString('base64'); + image.type = type; + image.name = src.base; + + imagesSize += image.data.length; + if (imagesSize > limitSize) { + throw new Error(`Файл для конвертирования слишком большой|FORLOG| imagesSize: ${imagesSize} > ${limitSize}`); + } + } + } + + let images = []; + let loading = []; + files.forEach(f => { + const image = {src: f}; + images.push(image); + loading.push(loadImage(image)); + }); + + await Promise.all(loading); + + //формируем fb2 + let titleInfo = {}; + let desc = {_n: 'description', 'title-info': titleInfo}; + let pars = []; + let body = {_n: 'body', section: {_a: [pars]}}; + let binary = []; + let fb2 = [desc, body, binary]; + + let title = ''; + if (uploadFileName) + title = uploadFileName; + + titleInfo['book-title'] = title; + + for (const image of images) { + if (image.type) { + const img = {_n: 'binary', _attrs: {id: image.name, 'content-type': image.type}, _t: image.data}; + binary.push(img); + + pars.push({_n: 'p', _t: ''}); + pars.push({_n: 'image', _attrs: {'l:href': `#${image.name}`}}); + } + } + pars.push({_n: 'p', _t: ''}); + + return this.formatFb2(fb2); + } +} + +module.exports = ConvertJpegPng; diff --git a/server/core/Reader/BookConverter/index.js b/server/core/Reader/BookConverter/index.js index ae753bac..2f9e9abb 100644 --- a/server/core/Reader/BookConverter/index.js +++ b/server/core/Reader/BookConverter/index.js @@ -3,6 +3,7 @@ const FileDetector = require('../../FileDetector'); //порядок важен const convertClassFactory = [ + require('./ConvertJpegPng'), require('./ConvertEpub'), require('./ConvertDjvu'), require('./ConvertPdf'), From 8850a89aa797a3e6d8b25c48e37ffe2d3a95c762 Mon Sep 17 00:00:00 2001 From: Book Pauk Date: Sun, 13 Dec 2020 18:51:13 +0700 Subject: [PATCH 03/11] =?UTF-8?q?=D0=9F=D0=BE=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20=D0=B1=D0=B0=D0=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/components/Reader/share/bookManager.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/client/components/Reader/share/bookManager.js b/client/components/Reader/share/bookManager.js index 76412c9c..9a183de3 100644 --- a/client/components/Reader/share/bookManager.js +++ b/client/components/Reader/share/bookManager.js @@ -410,16 +410,19 @@ class BookManager { } async setRecentBook(value) { - const result = this.metaOnly(value); + let result = this.metaOnly(value); result.touchTime = Date.now(); result.deleted = 0; - if (this.recent[result.key] && this.recent[result.key].deleted) { + if (this.recent[result.key]) { //восстановим из небытия пользовательские данные - if (!result.bookPos) - result.bookPos = this.recent[result.key].bookPos; - if (!result.bookPosSeen) - result.bookPosSeen = this.recent[result.key].bookPosSeen; + if (this.recent[result.key].deleted) { + if (!result.bookPos) + result.bookPos = this.recent[result.key].bookPos; + if (!result.bookPosSeen) + result.bookPosSeen = this.recent[result.key].bookPosSeen; + } + result = Object.assign({}, this.recent[result.key], result); } await this.recentSetLastKey(result.key); From 7d692dd730cf973d5329c51928948a3d1e1e51a9 Mon Sep 17 00:00:00 2001 From: Book Pauk Date: Sun, 13 Dec 2020 18:55:56 +0700 Subject: [PATCH 04/11] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/components/Reader/share/bookManager.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/client/components/Reader/share/bookManager.js b/client/components/Reader/share/bookManager.js index 9a183de3..0c93379e 100644 --- a/client/components/Reader/share/bookManager.js +++ b/client/components/Reader/share/bookManager.js @@ -415,13 +415,6 @@ class BookManager { result.deleted = 0; if (this.recent[result.key]) { - //восстановим из небытия пользовательские данные - if (this.recent[result.key].deleted) { - if (!result.bookPos) - result.bookPos = this.recent[result.key].bookPos; - if (!result.bookPosSeen) - result.bookPosSeen = this.recent[result.key].bookPosSeen; - } result = Object.assign({}, this.recent[result.key], result); } From b2ca84bb7ec265bad95f32575e457f1ea07a60ef Mon Sep 17 00:00:00 2001 From: Book Pauk Date: Sun, 13 Dec 2020 19:07:15 +0700 Subject: [PATCH 05/11] =?UTF-8?q?=D0=9F=D0=BE=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D0=BB=20readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/omnireader.ru/README.md | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/docs/omnireader.ru/README.md b/docs/omnireader.ru/README.md index 2f9aa452..45772780 100644 --- a/docs/omnireader.ru/README.md +++ b/docs/omnireader.ru/README.md @@ -32,23 +32,11 @@ sudo -u www-data mkdir -p /home/liberama/data/calibre sudo -u www-data tar xvf calibre-5.5.0-x86_64.txz -C /home/liberama/data/calibre ``` -### external converter `pdfalto`, github https://github.com/kermitt2/pdfalto -``` -git clone https://github.com/kermitt2/pdfalto -cd pdfalto -git submodule update --init --recursive -cmake ./ -добавить в начало CMakeLists.txt строчку: set(CMAKE_EXE_LINKER_FLAGS "-no-pie") -make - -sudo -u www-data mkdir -p /home/liberama/data/pdfalto -sudo -u www-data cp pdfalto /home/liberama/data/pdfalto -``` - ### external converters ``` sudo apt install rar sudo apt install libreoffice +sudo apt install poppler-utils sudo apt install djvulibre-bin sudo apt install libtiff-tools sudo apt install graphicsmagick-imagemagick-compat From 3137b86cee278163067837fa038bf05425986604 Mon Sep 17 00:00:00 2001 From: Book Pauk Date: Sun, 13 Dec 2020 21:54:03 +0700 Subject: [PATCH 06/11] =?UTF-8?q?=D0=A0=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=20?= =?UTF-8?q?=D0=BD=D0=B0=D0=B4=20=D0=BA=D0=BE=D0=BD=D0=B2=D0=B5=D1=80=D1=82?= =?UTF-8?q?=D0=B5=D1=80=D0=BE=D0=BC=20Pdf?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/Reader/BookConverter/ConvertBase.js | 1 + .../core/Reader/BookConverter/ConvertPdf.js | 50 ++++++++++++------- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/server/core/Reader/BookConverter/ConvertBase.js b/server/core/Reader/BookConverter/ConvertBase.js index 0f2ba697..069830ea 100644 --- a/server/core/Reader/BookConverter/ConvertBase.js +++ b/server/core/Reader/BookConverter/ConvertBase.js @@ -70,6 +70,7 @@ class ConvertBase { const error = `${result.code}|FORLOG|, exec: ${path}, args: ${args.join(' ')}, stdout: ${result.stdout}, stderr: ${result.stderr}`; throw new Error(`Внешний конвертер завершился с ошибкой: ${error}`); } + return result; } catch(e) { if (e.status == 'killed') { throw new Error('Слишком долгое ожидание конвертера'); diff --git a/server/core/Reader/BookConverter/ConvertPdf.js b/server/core/Reader/BookConverter/ConvertPdf.js index cc122a6e..7645713b 100644 --- a/server/core/Reader/BookConverter/ConvertPdf.js +++ b/server/core/Reader/BookConverter/ConvertPdf.js @@ -5,7 +5,6 @@ const path = require('path'); const sax = require('../../sax'); const utils = require('../../utils'); const ConvertHtml = require('./ConvertHtml'); -const xmlParser = require('../../xmlParser'); class ConvertPdf extends ConvertHtml { check(data, opts) { @@ -26,16 +25,15 @@ class ConvertPdf extends ConvertHtml { const inpFile = inputFiles.sourceFile; const outBasename = `${inputFiles.filesDir}/${utils.randomHexString(10)}`; const outFile = `${outBasename}.xml`; - const metaFile = `${outBasename}_metadata.xml`; - const pdfaltoPath = `${this.config.dataDir}/pdfalto/pdfalto`; + const pdftohtmlPath = '/usr/bin/pdftohtml'; - if (!await fs.pathExists(pdfaltoPath)) - throw new Error('Внешний конвертер pdfalto не найден'); + if (!await fs.pathExists(pdftohtmlPath)) + throw new Error('Внешний конвертер pdftohtml не найден'); //конвертируем в xml let perc = 0; - await this.execConverter(pdfaltoPath, [inpFile, outFile], () => { + await this.execConverter(pdftohtmlPath, ['-nodrm', '-c', '-s', '-xml', inpFile, outFile], () => { perc = (perc < 80 ? perc + 10 : 40); callback(perc); }, abort); @@ -57,8 +55,6 @@ class ConvertPdf extends ConvertHtml { let images = []; let loading = []; - let title = ''; - let author = ''; let i = -1; const loadImage = async(image) => { @@ -277,16 +273,8 @@ class ConvertPdf extends ConvertHtml { } indents[0] = 0; - //title - if (fs.pathExists(metaFile)) { - const metaXmlString = (await fs.readFile(metaFile)).toString(); - let metaXmlParsed = xmlParser.parseXml(metaXmlString); - metaXmlParsed = xmlParser.simplifyXmlParsed(metaXmlParsed); - if (metaXmlParsed.metadata) { - title = (metaXmlParsed.metadata.title ? metaXmlParsed.metadata.title._t : ''); - author = (metaXmlParsed.metadata.author ? metaXmlParsed.metadata.author._t : ''); - } - } + //author & title + let {author, title} = await this.getPdfTitleAndAuthor(inpFile); if (!title && uploadFileName) title = uploadFileName; @@ -343,6 +331,32 @@ class ConvertPdf extends ConvertHtml { await utils.sleep(100); return await super.run(Buffer.from(text), {skipCheck: true, isText: true}); } + + async getPdfTitleAndAuthor(pdfFile) { + const result = {author: '', title: ''}; + + const pdfinfoPath = '/usr/bin/pdfinfo'; + + if (!await fs.pathExists(pdfinfoPath)) + throw new Error('Внешний конвертер pdfinfo не найден'); + + const execResult = await this.execConverter(pdfinfoPath, [pdfFile]); + + const titlePrefix = 'Title:'; + const authorPrefix = 'Author:'; + + const stdout = execResult.stdout.split("\n"); + stdout.forEach(line => { + if (line.indexOf(titlePrefix) == 0) + result.title = line.substring(titlePrefix.length).trim(); + + if (line.indexOf(authorPrefix) == 0) + result.author = line.substring(authorPrefix.length).trim(); + }); + + return result; + } } + module.exports = ConvertPdf; From 7460ff705513188b3675e358349acc949b757f4d Mon Sep 17 00:00:00 2001 From: Book Pauk Date: Sun, 13 Dec 2020 22:50:24 +0700 Subject: [PATCH 07/11] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D0=BA?= =?UTF-8?q?=D0=B0=20=D0=B2=D1=8B=D1=85=D0=BE=D0=B4=D0=B0=20=D0=BD=D0=BE?= =?UTF-8?q?=D0=B2=D0=BE=D0=B9=20=D0=B2=D0=B5=D1=80=D1=81=D0=B8=D0=B8=20?= =?UTF-8?q?=D1=87=D0=B8=D1=82=D0=B0=D0=BB=D0=BA=D0=B8=20=D0=B8=20=D1=83?= =?UTF-8?q?=D0=B2=D0=B5=D0=B4=D0=BE=D0=BC=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20?= =?UTF-8?q?=D0=BE=D0=B1=20=D1=8D=D1=82=D0=BE=D0=BC,=20=D0=B7=D0=B0=D0=BE?= =?UTF-8?q?=D0=B4=D0=BD=D0=BE=20=D0=BF=D0=BE=D0=BF=D1=83=D1=82=D0=BD=D1=8B?= =?UTF-8?q?=D0=B9=20=D1=80=D0=B5=D1=84=D0=B0=D0=BA=D1=82=D0=BE=D1=80=D0=B8?= =?UTF-8?q?=D0=BD=D0=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/components/App.vue | 34 +++++++++------ client/components/Reader/Reader.vue | 41 ++++++++++++++++++- .../Reader/SettingsPage/include/OthersTab.inc | 13 +++++- client/store/modules/config.js | 13 +----- client/store/modules/reader.js | 4 +- 5 files changed, 78 insertions(+), 27 deletions(-) diff --git a/client/components/App.vue b/client/components/App.vue index 44859c56..9232d1c1 100644 --- a/client/components/App.vue +++ b/client/components/App.vue @@ -2,7 +2,7 @@
- +
@@ -12,8 +12,11 @@ //----------------------------------------------------------------------------- import Vue from 'vue'; import Component from 'vue-class-component'; + import Notify from './share/Notify.vue'; import StdDialog from './share/StdDialog.vue'; + +import miscApi from '../api/misc'; import * as utils from '../share/utils'; export default @Component({ @@ -30,6 +33,8 @@ export default @Component({ }) class App extends Vue { + showPage = false; + itemRuText = { '/cardindex': 'Картотека', '/reader': 'Читалка', @@ -42,7 +47,6 @@ class App extends Vue { created() { this.commit = this.$store.commit; - this.dispatch = this.$store.dispatch; this.state = this.$store.state; this.uistate = this.$store.state.uistate; this.config = this.$store.state.config; @@ -116,18 +120,24 @@ class App extends Vue { this.$root.notify = this.$refs.notify; this.$root.stdDialog = this.$refs.stdDialog; - this.dispatch('config/loadConfig'); - this.$watch('apiError', function(newError) { - if (newError) { - let mes = newError.message; - if (newError.response && newError.response.config) - mes = newError.response.config.url + '
' + newError.response.statusText; - this.$root.notify.error(mes, 'Ошибка API'); - } - }); - this.setAppTitle(); (async() => { + //загрузим конфиг сревера + try { + const config = await miscApi.loadConfig(); + this.commit('config/setConfig', config); + this.showPage = true; + } catch(e) { + //проверим, не получен ли конфиг ранее + if (!this.mode) { + this.$root.notify.error(e.message, 'Ошибка API'); + } else { + //вероятно, работаем в оффлайне + this.showPage = true; + } + console.error(e); + } + //запросим persistent storage if (navigator.storage && navigator.storage.persist) { navigator.storage.persist(); diff --git a/client/components/Reader/Reader.vue b/client/components/Reader/Reader.vue index 09a46a36..fea20b06 100644 --- a/client/components/Reader/Reader.vue +++ b/client/components/Reader/Reader.vue @@ -133,6 +133,9 @@ import ReaderDialogs from './ReaderDialogs/ReaderDialogs.vue'; import bookManager from './share/bookManager'; import rstore from '../../store/modules/reader'; import readerApi from '../../api/reader'; +import miscApi from '../../api/misc'; + +import {versionHistory} from './versionHistory'; import * as utils from '../../share/utils'; export default @Component({ @@ -229,7 +232,6 @@ class Reader extends Vue { this.rstore = rstore; this.loading = true; this.commit = this.$store.commit; - this.dispatch = this.$store.dispatch; this.reader = this.$store.state.reader; this.config = this.$store.state.config; @@ -292,6 +294,32 @@ class Reader extends Vue { this.updateRoute(); await this.$refs.dialogs.init(); + + await utils.sleep(15*1000); //подождем 15 секунд, чтобы прогрузился ServiceWorker при выходе новой версии + this.isFirstNeedUpdateNotify = true; + //вечный цикл, запрашиваем периодически конфиг для проверки выхода новой версии читалки + while (true) {// eslint-disable-line no-constant-condition + if (this.showNeedUpdateNotify) { + try { + const config = await miscApi.loadConfig(); + this.commit('config/setConfig', config); + + let againMes = ''; + if (this.isFirstNeedUpdateNotify) { + againMes = ' ЕЩЕ один раз'; + } + + if (this.version != this.clientVersion) + this.$root.notify.info(`Вышла новая версия (v${this.version}) читалки.
Пожалуйста, обновите страницу${againMes}.`, 'Обновление'); + } catch(e) { + // + } + } + + await utils.sleep(3600*1000); //каждый час + this.isFirstNeedUpdateNotify = false; + } + //дальше кода нет })(); } @@ -304,6 +332,7 @@ class Reader extends Vue { this.blinkCachedLoad = settings.blinkCachedLoad; this.showToolButton = settings.showToolButton; this.enableSitesFilter = settings.enableSitesFilter; + this.showNeedUpdateNotify = settings.showNeedUpdateNotify; this.readerActionByKeyCode = utils.userHotKeysObjectSwap(settings.userHotKeys); this.$root.readerActionByKeyEvent = (event) => { @@ -394,6 +423,16 @@ class Reader extends Vue { return this.$store.state.config.mode; } + get version() { + return this.$store.state.config.version; + } + + get clientVersion() { + let v = versionHistory[0].header; + v = v.split(' ')[0]; + return v; + } + get routeParamUrl() { let result = ''; const path = this.$route.fullPath; diff --git a/client/components/Reader/SettingsPage/include/OthersTab.inc b/client/components/Reader/SettingsPage/include/OthersTab.inc index 46f3427b..5bc4f21b 100644 --- a/client/components/Reader/SettingsPage/include/OthersTab.inc +++ b/client/components/Reader/SettingsPage/include/OthersTab.inc @@ -36,7 +36,18 @@ Показывать уведомление "Что нового" Показывать уведомления "Что нового"
- при каждом выходе новой версии читалки + при появлении новой версии читалки +
+ + + +
+
Уведомление
+ + Показывать уведомление о новой версии + + Напоминать о необходимости обновления страницы
+ при появлении новой версии читалки
diff --git a/client/store/modules/config.js b/client/store/modules/config.js index a9c7c1e7..32852f7f 100644 --- a/client/store/modules/config.js +++ b/client/store/modules/config.js @@ -10,18 +10,7 @@ const state = { const getters = {}; // actions -const actions = { - async loadConfig({ commit, state }) { - commit('setApiError', null, { root: true }); - commit('setConfig', {}); - try { - const config = await miscApi.loadConfig(); - commit('setConfig', config); - } catch (e) { - commit('setApiError', e, { root: true }); - } - }, -}; +const actions = {}; // mutations const mutations = { diff --git a/client/store/modules/reader.js b/client/store/modules/reader.js index f99d79c8..ed3d10d9 100644 --- a/client/store/modules/reader.js +++ b/client/store/modules/reader.js @@ -251,11 +251,13 @@ const settingDefaults = { compactTextPerc: 0, imageHeightLines: 100, imageFitWidth: true, + enableSitesFilter: true, + showServerStorageMessages: true, showWhatsNewDialog: true, showDonationDialog2020: true, showLiberamaTopDialog2020: true, - enableSitesFilter: true, + showNeedUpdateNotify: true, fontShifts: {}, showToolButton: {}, From 4184fda24742547eac7e7ce76058cef7734e3151 Mon Sep 17 00:00:00 2001 From: Book Pauk Date: Sun, 13 Dec 2020 22:53:47 +0700 Subject: [PATCH 08/11] =?UTF-8?q?=D0=9C=D0=B5=D0=BB=D0=BA=D0=B0=D1=8F=20?= =?UTF-8?q?=D0=BF=D0=BE=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/components/Reader/Reader.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/components/Reader/Reader.vue b/client/components/Reader/Reader.vue index fea20b06..f64f9908 100644 --- a/client/components/Reader/Reader.vue +++ b/client/components/Reader/Reader.vue @@ -312,7 +312,7 @@ class Reader extends Vue { if (this.version != this.clientVersion) this.$root.notify.info(`Вышла новая версия (v${this.version}) читалки.
Пожалуйста, обновите страницу${againMes}.`, 'Обновление'); } catch(e) { - // + console.error(e); } } From 80a29e654dbe464d2f45c7277f074c5f95256dc4 Mon Sep 17 00:00:00 2001 From: Book Pauk Date: Mon, 14 Dec 2020 00:21:48 +0700 Subject: [PATCH 09/11] =?UTF-8?q?=D0=9F=D0=BE=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BA=D0=B8=20=D0=BC=D0=B5=D1=85=D0=B0=D0=BD=D0=B8=D0=B7=D0=BC?= =?UTF-8?q?=D0=B0=20=D0=BE=D0=BF=D0=BE=D0=B2=D0=B5=D1=89=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F=20=D0=BE=20=D0=B2=D1=8B=D1=85=D0=BE=D0=B4=D0=B5=20=D0=BD?= =?UTF-8?q?=D0=BE=D0=B2=D0=BE=D0=B9=20=D0=B2=D0=B5=D1=80=D1=81=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/components/Reader/Reader.vue | 48 +++++++++++++++++------------ 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/client/components/Reader/Reader.vue b/client/components/Reader/Reader.vue index f64f9908..f1ee8e8b 100644 --- a/client/components/Reader/Reader.vue +++ b/client/components/Reader/Reader.vue @@ -294,30 +294,14 @@ class Reader extends Vue { this.updateRoute(); await this.$refs.dialogs.init(); + })(); - await utils.sleep(15*1000); //подождем 15 секунд, чтобы прогрузился ServiceWorker при выходе новой версии + (async() => { this.isFirstNeedUpdateNotify = true; //вечный цикл, запрашиваем периодически конфиг для проверки выхода новой версии читалки while (true) {// eslint-disable-line no-constant-condition - if (this.showNeedUpdateNotify) { - try { - const config = await miscApi.loadConfig(); - this.commit('config/setConfig', config); - - let againMes = ''; - if (this.isFirstNeedUpdateNotify) { - againMes = ' ЕЩЕ один раз'; - } - - if (this.version != this.clientVersion) - this.$root.notify.info(`Вышла новая версия (v${this.version}) читалки.
Пожалуйста, обновите страницу${againMes}.`, 'Обновление'); - } catch(e) { - console.error(e); - } - } - + await this.checkNewVersionAvailable(); await utils.sleep(3600*1000); //каждый час - this.isFirstNeedUpdateNotify = false; } //дальше кода нет })(); @@ -342,6 +326,30 @@ class Reader extends Vue { this.updateHeaderMinWidth(); } + async checkNewVersionAvailable() { + if (!this.checkingNewVersion && this.showNeedUpdateNotify) { + this.checkingNewVersion = true; + try { + await utils.sleep(15*1000); //подождем 15 секунд, чтобы прогрузился ServiceWorker при выходе новой версии + const config = await miscApi.loadConfig(); + this.commit('config/setConfig', config); + + let againMes = ''; + if (this.isFirstNeedUpdateNotify) { + againMes = ' ЕЩЕ один раз'; + } + + if (this.version != this.clientVersion) + this.$root.notify.info(`Вышла новая версия (v${this.version}) читалки.
Пожалуйста, обновите страницу${againMes}.`, 'Обновление'); + } catch(e) { + console.error(e); + } finally { + this.checkingNewVersion = false; + } + } + this.isFirstNeedUpdateNotify = false; + } + updateHeaderMinWidth() { const showButtonCount = Object.values(this.showToolButton).reduce((a, b) => a + (b ? 1 : 0), 0); if (this.$refs.buttons) @@ -1002,6 +1010,8 @@ class Reader extends Vue { progress.hide(); this.progressActive = false; this.loaderActive = true; this.$root.stdDialog.alert(e.message, 'Ошибка', {color: 'negative'}); + } finally { + this.checkNewVersionAvailable(); } } From 17699f66f86afe6ccc09d7dd46ac650244589a38 Mon Sep 17 00:00:00 2001 From: Book Pauk Date: Mon, 14 Dec 2020 02:07:20 +0700 Subject: [PATCH 10/11] =?UTF-8?q?=D0=9D=D0=B5=D0=B1=D0=BE=D0=BB=D1=8C?= =?UTF-8?q?=D1=88=D0=BE=D0=B5=20=D1=83=D0=BB=D1=83=D1=87=D1=88=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=BF=D0=B0=D1=80=D1=81=D0=B8=D0=BD=D0=B3=D0=B0?= =?UTF-8?q?=20=D0=BE=D0=B3=D0=BB=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/components/Reader/share/BookParser.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client/components/Reader/share/BookParser.js b/client/components/Reader/share/BookParser.js index 028991c0..0e2bc97d 100644 --- a/client/components/Reader/share/BookParser.js +++ b/client/components/Reader/share/BookParser.js @@ -304,6 +304,11 @@ export default class BookParser { bold = true; center = true; + if (curTitle.paraIndex < 0) { + curTitle = {paraIndex, title: 'Оглавление', inset: sectionLevel, bodyIndex, subtitles: []}; + this.contents.push(curTitle); + } + inSubtitle = true; curSubtitle = {paraIndex, inset: sectionLevel, title: ''}; curTitle.subtitles.push(curSubtitle); From 9906dd43c791d5f5085a9e6575bb515b0d9df416 Mon Sep 17 00:00:00 2001 From: Book Pauk Date: Mon, 14 Dec 2020 02:22:38 +0700 Subject: [PATCH 11/11] =?UTF-8?q?=D0=A0=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=20?= =?UTF-8?q?=D0=BD=D0=B0=D0=B4=20=D0=BA=D0=BE=D0=BD=D0=B2=D0=B5=D1=80=D1=82?= =?UTF-8?q?=D0=B5=D1=80=D0=BE=D0=BC=20pdf?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/Reader/BookConverter/ConvertPdf.js | 177 ++++++++++-------- 1 file changed, 95 insertions(+), 82 deletions(-) diff --git a/server/core/Reader/BookConverter/ConvertPdf.js b/server/core/Reader/BookConverter/ConvertPdf.js index 7645713b..a41a31e6 100644 --- a/server/core/Reader/BookConverter/ConvertPdf.js +++ b/server/core/Reader/BookConverter/ConvertPdf.js @@ -55,6 +55,10 @@ class ConvertPdf extends ConvertHtml { let images = []; let loading = []; + let inText = false; + let bold = false; + let italic = false; + let i = -1; const loadImage = async(image) => { @@ -81,22 +85,30 @@ class ConvertPdf extends ConvertHtml { } }; + const isTextBold = (text) => { + const m = text.trim().match(/^(.*)<\/b>$/); + return m && !m[1].match(/|<\/b>||<\/i>/g); + }; + + const isTextEmpty = (text) => { + return text.replace(/|<\/b>||<\/i>/g, '').trim() == ''; + }; + const putPageLines = () => { - pagelines.sort((a, b) => (a.top - b.top)*10000 + (a.left - b.left)) + pagelines.sort((a, b) => (Math.abs(a.top - b.top) > 3 ? a.top - b.top : 0)*10000 + (a.left - b.left)) //объединяем в одну строку равные по высоте const pl = []; let pt = 0; let j = -1; pagelines.forEach(line => { - //добавим закрывающий тег стиля - line.text += line.tClose; + if (isTextEmpty(line.text)) + return; //проверим, возможно это заголовок - if (line.fonts.length == 1 && line.pageWidth) { - const f = (line.fonts.length ? fonts[line.fonts[0]] : null); + if (line.fontId && line.pageWidth) { const centerLeft = (line.pageWidth - line.width)/2; - if (f && f.isBold && Math.abs(centerLeft - line.left) < 3) { + if (isTextBold(line.text) && Math.abs(centerLeft - line.left) < 10) { if (!sectionTitleFound) { line.isSectionTitle = true; sectionTitleFound = true; @@ -124,8 +136,8 @@ class ConvertPdf extends ConvertHtml { //добавим пустую строку, если надо const prevLine = (i > lastIndex ? lines[i] : {fonts: [], top: 0}); if (prevLine && !prevLine.isImage) { - const f = (prevLine.fonts.length ? fonts[prevLine.fonts[0]] : (line.fonts.length ? fonts[line.fonts[0]] : null)); - if (f && f.fontSize && !line.isImage && line.top - prevLine.top > f.fontSize*1.8) { + const f = (prevLine.fontId ? fonts[prevLine.fontId] : (line.fontId ? fonts[line.fontId] : null)); + if (f && f.fontSize && !line.isImage && line.top - prevLine.top > f.fontSize * 1.8) { i++; lines[i] = {text: '
'}; } @@ -138,29 +150,26 @@ class ConvertPdf extends ConvertHtml { putImage(100000); }; + const onTextNode = (text, cutCounter, cutTag) => {// eslint-disable-line no-unused-vars + if (!cutCounter && inText) { + let tOpen = (bold ? '' : ''); + tOpen += (italic ? '' : ''); + let tClose = (italic ? '' : ''); + tClose += (bold ? '' : ''); + + line.text += ` ${tOpen}${text}${tClose}`; + } + }; + const onStartNode = (tag, tail, singleTag, cutCounter, cutTag) => {// eslint-disable-line no-unused-vars - if (tag == 'textstyle') { - const attrs = sax.getAttrsSync(tail); - const fontId = (attrs.id && attrs.id.value ? attrs.id.value : ''); - const fontStyle = (attrs.fontstyle && attrs.fontstyle.value ? attrs.fontstyle.value : ''); - const fontSize = (attrs.fontsize && attrs.fontsize.value ? attrs.fontsize.value : ''); - - if (fontId) { - const styleTags = {bold: 'b', italics: 'i', superscript: 'sup', subscript: 'sub'}; - const f = fonts[fontId] = {tOpen: '', tClose: '', isBold: false, fontSize}; - - if (fontStyle) { - const styles = fontStyle.split(' '); - styles.forEach(style => { - const s = styleTags[style]; - if (s) { - f.tOpen += `<${s}>`; - f.tClose = `${f.tClose}`; - if (s == 'b') - f.isBold = true; - } - }); - } + if (inText) { + switch (tag) { + case 'i': + italic = true; + break; + case 'b': + bold = true; + break; } } @@ -173,80 +182,78 @@ class ConvertPdf extends ConvertHtml { putPageLines(); } - if (tag == 'textline') { + if (tag == 'fontspec') { + const attrs = sax.getAttrsSync(tail); + const fontId = (attrs.id && attrs.id.value ? attrs.id.value : ''); + const fontSize = (attrs.size && attrs.size.value ? attrs.size.value : ''); + + if (fontId) { + fonts[fontId] = {fontSize}; + + } + } + + if (tag == 'text' && !inText) { const attrs = sax.getAttrsSync(tail); line = { text: '', - top: parseInt((attrs.vpos && attrs.vpos.value ? attrs.vpos.value : null), 10), - left: parseInt((attrs.hpos && attrs.hpos.value ? attrs.hpos.value : null), 10), + top: parseInt((attrs.top && attrs.top.value ? attrs.top.value : null), 10), + left: parseInt((attrs.left && attrs.left.value ? attrs.left.value : null), 10), width: parseInt((attrs.width && attrs.width.value ? attrs.width.value : null), 10), height: parseInt((attrs.height && attrs.height.value ? attrs.height.value : null), 10), - tOpen: '', - tClose: '', isSectionTitle: false, isSubtitle: false, pageWidth: page.width, - fonts: [], + fontId: (attrs.font && attrs.font.value ? attrs.font.value : ''), }; if (line.width != 0 || line.height != 0) { + inText = true; pagelines.push(line); } } - if (tag == 'string') { + if (tag == 'image') { const attrs = sax.getAttrsSync(tail); - if (attrs.content && attrs.content.value) { + let src = (attrs.src && attrs.src.value ? attrs.src.value : ''); + if (src) { + const image = { + isImage: true, + src, + data: '', + type: '', + top: parseInt((attrs.top && attrs.top.value ? attrs.top.value : null), 10) || 0, + left: parseInt((attrs.left && attrs.left.value ? attrs.left.value : null), 10) || 0, + width: parseInt((attrs.width && attrs.width.value ? attrs.width.value : null), 10) || 0, + height: parseInt((attrs.height && attrs.height.value ? attrs.height.value : null), 10) || 0, + }; - let tOpen = ''; - let tClose = ''; - const fontId = (attrs.stylerefs && attrs.stylerefs.value ? attrs.stylerefs.value : ''); - if (fontId && fonts[fontId]) { - tOpen = fonts[fontId].tOpen; - tClose = fonts[fontId].tClose; - if (!line.fonts.length || line.fonts[0] != fontId) - line.fonts.push(fontId); - } - - if (line.tOpen != tOpen) { - line.text += line.tClose + tOpen; - line.tOpen = tOpen; - line.tClose = tClose; - } - - line.text += `${line.text.length ? ' ' : ''}${attrs.content.value}`; - } - } - - if (tag == 'illustration') { - const attrs = sax.getAttrsSync(tail); - if (attrs.type && attrs.type.value == 'image') { - let src = (attrs.fileid && attrs.fileid.value ? attrs.fileid.value : ''); - if (src) { - const image = { - isImage: true, - src, - data: '', - type: '', - top: parseInt((attrs.vpos && attrs.vpos.value ? attrs.vpos.value : null), 10) || 0, - left: parseInt((attrs.hpos && attrs.hpos.value ? attrs.hpos.value : null), 10) || 0, - width: parseInt((attrs.width && attrs.width.value ? attrs.width.value : null), 10) || 0, - height: parseInt((attrs.height && attrs.height.value ? attrs.height.value : null), 10) || 0, - }; - const exists = images.filter(img => (img.top == image.top && img.left == image.left && img.width == image.width && img.height == image.height)); - if (!exists.length) { - loading.push(loadImage(image)); - images.push(image); - images.sort((a, b) => (a.top - b.top)*10000 + (a.left - b.left)); - } - } + loading.push(loadImage(image)); + images.push(image); + images.sort((a, b) => (a.top - b.top)*10000 + (a.left - b.left)); } } }; + const onEndNode = (tag, tail, singleTag, cutCounter, cutTag) => {// eslint-disable-line no-unused-vars + if (inText) { + switch (tag) { + case 'i': + italic = false; + break; + case 'b': + bold = false; + break; + } + } + + if (tag == 'text') + inText = false; + }; + let buf = this.decode(data).toString(); sax.parseSync(buf, { - onStartNode + onStartNode, onEndNode, onTextNode }); putPageLines(); @@ -290,6 +297,7 @@ class ConvertPdf extends ConvertHtml { let concat = ''; let sp = ''; + let firstLine = true; for (const line of lines) { if (text.length > limitSize) { throw new Error(`Файл для конвертирования слишком большой|FORLOG| text.length: ${text.length} > ${limitSize}`); @@ -301,10 +309,15 @@ class ConvertPdf extends ConvertHtml { } if (line.isSectionTitle) { - text += `${line.text.trim()}`; + if (firstLine) + text += `${line.text.trim()}`; + else + text += `${line.text.trim()}`; continue; } + firstLine = false; + if (line.isSubtitle) { text += `
${line.text.trim()}`; continue;