diff --git a/client/components/ExternalLibs/ExternalLibs.vue b/client/components/ExternalLibs/ExternalLibs.vue index 9054a401..e12fa314 100644 --- a/client/components/ExternalLibs/ExternalLibs.vue +++ b/client/components/ExternalLibs/ExternalLibs.vue @@ -408,7 +408,7 @@ class ExternalLibs extends Vue { } get header() { - let result = (this.ready ? 'Библиотека' : 'Загрузка...'); + let result = (this.ready ? 'Сетевая библиотека' : 'Загрузка...'); if (this.ready && this.selectedLink) { result += ` | ${(this.libs.comment ? this.libs.comment + ' ': '') + lu.removeProtocol(this.libs.startLink)}`; } @@ -787,12 +787,17 @@ class ExternalLibs extends Vue { showHelp() { this.$root.stdDialog.alert(` -

Окно 'Библиотека' позволяет открывать ссылки в читалке без переключения между окнами, -что особенно актуально для мобильных устройств.

- -

'Библиотека' разрешает свободный доступ к сайту flibusta.is. Имеется возможность управлять закладками +

Окно 'Сетевая библиотека' позволяет открывать ссылки в читалке без переключения между окнами, +что особенно актуально для мобильных устройств. Имеется возможность управлять закладками на понравившиеся ресурсы, книги или страницы авторов. Открытие ссылок и навигация происходят во фрейме, но, -к сожалению, в нем открываются не все страницы. +к сожалению, в нем открываются не все страницы.

+ +

Доступ к сайтам http://flibusta.is и http://fantasy-worlds.org работает через прокси. + +
ПРЕДУПРЕЖДЕНИЕ! +Доступ предназначен только для просмотра и скачивания книг. Авторизоваться на этих сайтах +из фрейма категорически не рекомендуется, т.к. ваше подключение не защищено и данные могут попасть +к третьим лицам.

Из-за проблем с безопасностью, навигация 'вперед-назад' во фрейме осуществляется с помощью контекстного меню правой кнопкой мыши. diff --git a/client/components/Reader/ContentsPage/ContentsPage.vue b/client/components/Reader/ContentsPage/ContentsPage.vue index 8144fdfa..9a34364a 100644 --- a/client/components/Reader/ContentsPage/ContentsPage.vue +++ b/client/components/Reader/ContentsPage/ContentsPage.vue @@ -50,6 +50,9 @@ +

+ Оглавление отсутствует +
diff --git a/client/components/Reader/Reader.vue b/client/components/Reader/Reader.vue index 9dc3a02f..f2b1d395 100644 --- a/client/components/Reader/Reader.vue +++ b/client/components/Reader/Reader.vue @@ -250,11 +250,11 @@ class Reader extends Vue { await this.$nextTick(); this.paramPosIgnore = false; } - }, 500, {'maxWait':5000}); + }, 500, {maxWait: 5000}); this.scrollingSetRecentBook = _.debounce((newValue) => { this.debouncedSetRecentBook(newValue); - }, 15000, {'maxWait':20000}); + }, 15000, {maxWait: 20000}); document.addEventListener('fullscreenchange', () => { this.fullScreenActive = (document.fullscreenElement !== null); diff --git a/client/components/Reader/RecentBooksPage/RecentBooksPage.vue b/client/components/Reader/RecentBooksPage/RecentBooksPage.vue index 96f677b0..c7eec977 100644 --- a/client/components/Reader/RecentBooksPage/RecentBooksPage.vue +++ b/client/components/Reader/RecentBooksPage/RecentBooksPage.vue @@ -59,7 +59,7 @@
Оригинал
- Скачать FB2 + Скачать FB2
@@ -87,7 +87,7 @@ import Vue from 'vue'; import Component from 'vue-class-component'; import path from 'path'; -import _ from 'lodash'; +//import _ from 'lodash'; import * as utils from '../../../share/utils'; import Window from '../../share/Window.vue'; @@ -222,30 +222,11 @@ class RecentBooksPage extends Vue { textLen = ` ${Math.round(book.textLength/1000)}k`; } - const fb2 = (book.fb2 ? book.fb2 : {}); + const bt = utils.getBookTitle(book.fb2); - let title = fb2.bookTitle; - if (title) - title = `"${title}"`; - else - title = ''; - - let author = ''; - if (fb2.author) { - const authorNames = fb2.author.map(a => _.compact([ - a.lastName, - a.firstName, - a.middleName - ]).join(' ')); - author = authorNames.join(', '); - } else {//TODO: убрать в будущем - author = _.compact([ - fb2.lastName, - fb2.firstName, - fb2.middleName - ]).join(' '); - } - author = (author ? author : (fb2.bookTitle ? fb2.bookTitle : book.url)); + let title = bt.bookTitle; + title = (title ? `"${title}"`: ''); + const author = (bt.author ? bt.author : (bt.bookTitle ? bt.bookTitle : book.url)); result.push({ num, @@ -256,9 +237,10 @@ class RecentBooksPage extends Vue { author, title: `${title}${perc}${textLen}`, }, - descString: `${author}${title}${perc}${textLen}`, + descString: `${author}${title}${perc}${textLen}`,//для сортировки url: book.url, path: book.path, + fullTitle: bt.fullTitle, key: book.key, }); } @@ -291,13 +273,18 @@ class RecentBooksPage extends Vue { return `${(this.search ? 'Найдено' : 'Всего')} ${len} книг${this.wordEnding(len)}`; } - async downloadBook(fb2path) { + async downloadBook(fb2path, fullTitle) { try { await readerApi.checkCachedBook(fb2path); const d = this.$refs.download; d.href = fb2path; - d.download = path.basename(fb2path).substr(0, 10) + '.fb2'; + try { + const fn = utils.makeValidFilename(fullTitle); + d.download = fn.substring(0, 100) + '.fb2'; + } catch(e) { + d.download = path.basename(fb2path).substr(0, 10) + '.fb2'; + } d.click(); } catch (e) { @@ -308,14 +295,6 @@ class RecentBooksPage extends Vue { } } - openOriginal(url) { - window.open(url, '_blank'); - } - - openFb2(path) { - window.open(path, '_blank'); - } - async handleDel(key) { await bookManager.delRecentBook({key}); //this.updateTableData();//обновление уже происходит Reader.bookManagerEvent diff --git a/client/components/Reader/ServerStorage/ServerStorage.vue b/client/components/Reader/ServerStorage/ServerStorage.vue index 7a17af30..0bd9cb8b 100644 --- a/client/components/Reader/ServerStorage/ServerStorage.vue +++ b/client/components/Reader/ServerStorage/ServerStorage.vue @@ -507,10 +507,10 @@ class ServerStorage extends Vue { if (md.key && result[md.key]) result[md.key] = utils.applyObjDiff(result[md.key], md.mod, {isAddChanged: true}); - if (!bookManager.loaded) { + /*if (!bookManager.loaded) { this.warning('Ожидание загрузки списка книг перед синхронизацией'); while (!bookManager.loaded) await utils.sleep(100); - } + }*/ if (newRecent.rev != this.cachedRecent.rev) await this.setCachedRecent(newRecent); diff --git a/client/components/Reader/TextPage/TextPage.vue b/client/components/Reader/TextPage/TextPage.vue index 04c3d897..7685e294 100644 --- a/client/components/Reader/TextPage/TextPage.vue +++ b/client/components/Reader/TextPage/TextPage.vue @@ -40,7 +40,7 @@ import Component from 'vue-class-component'; import {loadCSS} from 'fg-loadcss'; import _ from 'lodash'; -import {sleep} from '../../../share/utils'; +import * as utils from '../../../share/utils'; import bookManager from '../share/bookManager'; import DrawHelper from './DrawHelper'; import rstore from '../../../store/modules/reader'; @@ -134,7 +134,7 @@ class TextPage extends Vue { this.$root.$on('resize', async() => { this.$nextTick(this.onResize); - await sleep(500); + await utils.sleep(500); this.$nextTick(this.onResize); }); } @@ -285,7 +285,7 @@ class TextPage extends Vue { let close = null; (async() => { - await sleep(500); + await utils.sleep(500); if (this.fontsLoading) close = this.$root.notify.info('Загрузка шрифта  '); })(); @@ -342,7 +342,7 @@ class TextPage extends Vue { let i = 0; const t = this.parsed.testText; while (i++ < 50 && this.parsed === parsed && this.drawHelper.measureText(t, {}) === this.parsed.testWidth) - await sleep(100); + await utils.sleep(100); if (this.parsed === parsed) { this.parsed.testWidth = this.drawHelper.measureText(t, {}); @@ -366,7 +366,6 @@ class TextPage extends Vue { this.updateLayout(); this.book = null; this.meta = null; - this.fb2 = null; this.parsed = null; this.linesUp = null; @@ -383,7 +382,7 @@ class TextPage extends Vue { try { //подождем ленивый парсинг this.stopLazyParse = true; - while (this.doingLazyParse) await sleep(10); + while (this.doingLazyParse) await utils.sleep(10); const isParsed = await bookManager.hasBookParsed(this.lastBook); if (!isParsed) { @@ -392,21 +391,9 @@ class TextPage extends Vue { this.book = await bookManager.getBook(this.lastBook); this.meta = bookManager.metaOnly(this.book); - this.fb2 = this.meta.fb2; + const bt = utils.getBookTitle(this.meta.fb2); - let authorNames = []; - if (this.fb2.author) { - authorNames = this.fb2.author.map(a => _.compact([ - a.lastName, - a.firstName, - a.middleName - ]).join(' ')); - } - - this.title = _.compact([ - authorNames.join(', '), - this.fb2.bookTitle - ]).join(' - '); + this.title = bt.fullTitle; this.$root.$emit('set-app-title', this.title); @@ -493,7 +480,7 @@ class TextPage extends Vue { let wait = (timeout + 201)/100; while (wait > 0 && !this[stopPropertyName]) { wait--; - await sleep(100); + await utils.sleep(100); } resolve(); })().catch(reject); }); @@ -509,7 +496,7 @@ class TextPage extends Vue { } //ждем анимацию - while (this.inAnimation) await sleep(10); + while (this.inAnimation) await utils.sleep(10); this.stopScrolling = false; this.doingScrolling = true; @@ -520,7 +507,7 @@ class TextPage extends Vue { this.page1 = this.page2; this.toggleLayout = true; await this.$nextTick(); - await sleep(50); + await utils.sleep(50); this.cachedPos = -1; this.draw(); @@ -557,7 +544,7 @@ class TextPage extends Vue { page.style.transform = 'none'; page.offsetHeight; - while (this.doingScrolling) await sleep(10); + while (this.doingScrolling) await utils.sleep(10); } draw() { @@ -766,7 +753,7 @@ class TextPage extends Vue { for (let i = 0; i < this.parsed.para.length; i++) { j++; if (j > 1) { - await sleep(1); + await utils.sleep(1); j = 0; } if (this.stopLazyParse) @@ -788,7 +775,7 @@ class TextPage extends Vue { async refreshTime() { if (!this.timeRefreshing) { this.timeRefreshing = true; - await sleep(60*1000); + await utils.sleep(60*1000); if (this.book && this.parsed.textLength) { this.debouncedDrawStatusBar(); @@ -905,7 +892,7 @@ class TextPage extends Vue { this.settingsChanging = true; const newSize = (this.settings.fontSize + 1 < 200 ? this.settings.fontSize + 1 : 100); this.commit('reader/setSettings', {fontSize: newSize}); - await sleep(50); + await utils.sleep(50); this.settingsChanging = false; } } @@ -915,7 +902,7 @@ class TextPage extends Vue { this.settingsChanging = true; const newSize = (this.settings.fontSize - 1 > 5 ? this.settings.fontSize - 1 : 5); this.commit('reader/setSettings', {fontSize: newSize}); - await sleep(50); + await utils.sleep(50); this.settingsChanging = false; } } @@ -925,7 +912,7 @@ class TextPage extends Vue { this.settingsChanging = true; const newDelay = (this.settings.scrollingDelay - 50 > 1 ? this.settings.scrollingDelay - 50 : 1); this.commit('reader/setSettings', {scrollingDelay: newDelay}); - await sleep(50); + await utils.sleep(50); this.settingsChanging = false; } } @@ -935,7 +922,7 @@ class TextPage extends Vue { this.settingsChanging = true; const newDelay = (this.settings.scrollingDelay + 50 < 10000 ? this.settings.scrollingDelay + 50 : 10000); this.commit('reader/setSettings', {scrollingDelay: newDelay}); - await sleep(50); + await utils.sleep(50); this.settingsChanging = false; } } @@ -949,7 +936,7 @@ class TextPage extends Vue { let delay = 400; while (this.repDoing) { this.handleClick(pointX, pointY); - await sleep(delay); + await utils.sleep(delay); if (delay > 15) delay *= 0.8; } diff --git a/client/components/Reader/share/BookParser.js b/client/components/Reader/share/BookParser.js index f368b6be..7c11260f 100644 --- a/client/components/Reader/share/BookParser.js +++ b/client/components/Reader/share/BookParser.js @@ -216,12 +216,32 @@ export default class BookParser { } } - if (tag == 'author' && path.indexOf('/fictionbook/description/title-info/author') == 0) { + if (path == '/fictionbook/description/title-info/author') { if (!fb2.author) fb2.author = []; + fb2.author.push({}); } + const isPublishSequence = (path == '/fictionbook/description/publish-info/sequence'); + if (path == '/fictionbook/description/title-info/sequence' || isPublishSequence) { + if (!fb2.sequence) + fb2.sequence = []; + + if (!isPublishSequence || !fb2.sequence.length) { + const attrs = sax.getAttrsSync(tail); + const seq = {}; + if (attrs.name && attrs.name.value) { + seq.name = attrs.name.value; + } + if (attrs.number && attrs.number.value) { + seq.number = attrs.number.value; + } + + fb2.sequence.push(seq); + } + } + if (path.indexOf('/fictionbook/body') == 0) { if (tag == 'body') { if (isFirstBody && fb2.annotation) { @@ -233,6 +253,14 @@ export default class BookParser { newParagraph(' ', 1); } + if (isFirstBody && fb2.sequence && fb2.sequence.length) { + const bt = utils.getBookTitle(fb2); + if (bt.sequence) { + newParagraph(bt.sequence, bt.sequence.length); + newParagraph(' ', 1); + } + } + if (!isFirstBody) newParagraph(' ', 1); isFirstBody = false; diff --git a/client/components/Reader/share/bookManager.js b/client/components/Reader/share/bookManager.js index 71c77381..abfe8cdc 100644 --- a/client/components/Reader/share/bookManager.js +++ b/client/components/Reader/share/bookManager.js @@ -6,17 +6,23 @@ import BookParser from './BookParser'; const maxDataSize = 300*1024*1024;//compressed bytes +//локальный кэш метаданных книг, ограничение maxDataSize const bmMetaStore = localForage.createInstance({ name: 'bmMetaStore' }); +//локальный кэш самих книг, ограничение maxDataSize const bmDataStore = localForage.createInstance({ name: 'bmDataStore' }); -const bmRecentStore = localForage.createInstance({ +//список недавно открытых книг +const bmRecentStoreOld = localForage.createInstance({ name: 'bmRecentStore' }); +const bmRecentStoreNew = localForage.createInstance({ + name: 'bmRecentStoreNew' +}); class BookManager { async init(settings) { @@ -25,15 +31,74 @@ class BookManager { this.eventListeners = []; this.books = {}; - this.recent = {}; - this.recentLast = await bmRecentStore.getItem('recent-last'); - if (this.recentLast) { - this.recent[this.recentLast.key] = this.recentLast; - const meta = await bmMetaStore.getItem(`bmMeta-${this.recentLast.key}`); - if (_.isObject(meta)) { - this.books[meta.key] = meta; + this.recent = {}; + this.saveRecent = _.debounce(() => { + bmRecentStoreNew.setItem('recent', this.recent); + }, 300, {maxWait: 800}); + + this.saveRecentItem = _.debounce(() => { + bmRecentStoreNew.setItem('recent-item', this.recentItem); + this.recentRev++; + bmRecentStoreNew.setItem('rev', this.recentRev); + }, 200, {maxWait: 300}); + + //загрузка bmRecentStore + this.recentRev = await bmRecentStoreNew.getItem('rev') || 0; + if (this.recentRev) { + this.recent = await bmRecentStoreNew.getItem('recent'); + if (!this.recent) + this.recent = {}; + + this.recentItem = await bmRecentStoreNew.getItem('recent-item'); + if (this.recentItem) + this.recent[this.recentItem.key] = this.recentItem; + + this.recentLastKey = await bmRecentStoreNew.getItem('recent-last-key'); + if (this.recentLastKey) { + const meta = await bmMetaStore.getItem(`bmMeta-${this.recentLastKey}`); + if (_.isObject(meta)) { + this.books[meta.key] = meta; + } } + + await this.cleanRecentBooks(); + + } else {//TODO: убрать после 06.2021, когда bmRecentStoreOld устареет + this.recentLast = await bmRecentStoreOld.getItem('recent-last'); + if (this.recentLast) { + this.recent[this.recentLast.key] = this.recentLast; + const meta = await bmMetaStore.getItem(`bmMeta-${this.recentLast.key}`); + if (_.isObject(meta)) { + this.books[meta.key] = meta; + } + } + + let key = null; + const len = await bmRecentStoreOld.length(); + for (let i = len - 1; i >= 0; i--) { + key = await bmRecentStoreOld.key(i); + if (key) { + let r = await bmRecentStoreOld.getItem(key); + if (_.isObject(r) && r.key) { + this.recent[r.key] = r; + } + } else { + await bmRecentStoreOld.removeItem(key); + } + } + + //размножение для дебага + /*if (key) { + for (let i = 0; i < 1000; i++) { + const k = this.keyFromUrl(i.toString()); + this.recent[k] = Object.assign({}, _.cloneDeep(this.recent[key]), {key: k, touchTime: Date.now() - 1000000, url: utils.randomHexString(300)}); + } + }*/ + + await bmRecentStoreNew.setItem('recent', this.recent); + this.recentRev = 1; + await bmRecentStoreNew.setItem('rev', this.recentRev); } this.recentChanged = true; @@ -41,9 +106,7 @@ class BookManager { this.loadStored();//no await } - //Долгая асинхронная загрузка из хранилища. - //Хранение в отдельных записях дает относительно - //нормальное поведение при нескольких вкладках с читалкой в браузере. + //Ленивая асинхронная загрузка bmMetaStore async loadStored() { //даем время для загрузки последней читаемой книги, чтобы не блокировать приложение await utils.sleep(2000); @@ -70,32 +133,7 @@ class BookManager { } } - let key = null; - len = await bmRecentStore.length(); - for (let i = len - 1; i >= 0; i--) { - key = await bmRecentStore.key(i); - if (key) { - let r = await bmRecentStore.getItem(key); - if (_.isObject(r) && r.key) { - this.recent[r.key] = r; - } - } else { - await bmRecentStore.removeItem(key); - } - } - - //размножение для дебага - /*if (key) { - for (let i = 0; i < 1000; i++) { - const k = this.keyFromUrl(i.toString()); - this.recent[k] = Object.assign({}, _.cloneDeep(this.recent[key]), {key: k, touchTime: Date.now() - 1000000, url: utils.randomHexString(300)}); - } - }*/ - await this.cleanBooks(); - await this.cleanRecentBooks(); - - this.recentChanged = true; this.loaded = true; this.emit('load-stored-finish'); } @@ -238,7 +276,7 @@ class BookManager { let book = this.books[meta.key]; if (!book && !this.loaded) { - book = await bmDataStore.getItem(`bmMeta-${meta.key}`); + book = await bmMetaStore.getItem(`bmMeta-${meta.key}`); if (book) this.books[meta.key] = book; } @@ -254,7 +292,7 @@ class BookManager { result = this.books[meta.key]; if (!result) { - result = await bmDataStore.getItem(`bmMeta-${meta.key}`); + result = await bmMetaStore.getItem(`bmMeta-${meta.key}`); if (result) this.books[meta.key] = result; } @@ -328,6 +366,43 @@ class BookManager { } //-- recent -------------------------------------------------------------- + async recentSetItem(item = null, skipCheck = false) { + const rev = await bmRecentStoreNew.getItem('rev'); + if (rev != this.recentRev && !skipCheck) { + const newRecent = await bmRecentStoreNew.getItem('recent'); + Object.assign(this.recent, newRecent); + this.recentItem = await bmRecentStoreNew.getItem('recent-item'); + this.recentRev = rev; + } + + const prevKey = (this.recentItem ? this.recentItem.key : ''); + if (item) { + this.recent[item.key] = item; + this.recentItem = item; + } else { + this.recentItem = null; + } + + this.saveRecentItem(); + + if (!item || prevKey != item.key) { + this.saveRecent(); + } + + this.recentChanged = true; + + if (item) { + this.emit('recent-changed', item.key); + } else { + this.emit('recent-changed'); + } + } + + async recentSetLastKey(key) { + this.recentLastKey = key; + await bmRecentStoreNew.setItem('recent-last-key', this.recentLastKey); + } + async setRecentBook(value) { const result = this.metaOnly(value); result.touchTime = Date.now(); @@ -341,38 +416,25 @@ class BookManager { result.bookPosSeen = this.recent[result.key].bookPosSeen; } - this.recent[result.key] = result; - - await bmRecentStore.setItem(result.key, result); - - this.recentLast = result; - await bmRecentStore.setItem('recent-last', this.recentLast); - - this.recentChanged = true; - this.emit('recent-changed', result.key); + await this.recentSetLastKey(result.key); + await this.recentSetItem(result); return result; } async getRecentBook(value) { - let result = this.recent[value.key]; - if (!result) { - result = await bmRecentStore.getItem(value.key); - if (result) - this.recent[value.key] = result; - } - return result; + return this.recent[value.key]; } async delRecentBook(value) { - this.recent[value.key].deleted = 1; - await bmRecentStore.setItem(value.key, this.recent[value.key]); + const item = this.recent[value.key]; + item.deleted = 1; - if (this.recentLast.key == value.key) { - this.recentLast = null; - await bmRecentStore.setItem('recent-last', this.recentLast); + if (this.recentLastKey == value.key) { + await this.recentSetLastKey(null); } + + await this.recentSetItem(item); this.emit('recent-deleted', value.key); - this.emit('recent-changed', value.key); } async cleanRecentBooks() { @@ -380,24 +442,22 @@ class BookManager { let isDel = false; for (let i = 1000; i < sorted.length; i++) { - await bmRecentStore.removeItem(sorted[i].key); delete this.recent[sorted[i].key]; - await bmRecentStore.removeItem(sorted[i].key); isDel = true; } this.sortedRecentCached = null; if (isDel) - this.emit('recent-changed'); + await this.recentSetItem(); return isDel; } mostRecentBook() { - if (this.recentLast) { - return this.recentLast; + if (this.recentLastKey) { + return this.recent[this.recentLastKey]; } - const oldRecentLast = this.recentLast; + const oldKey = this.recentLastKey; let max = 0; let result = null; @@ -408,10 +468,11 @@ class BookManager { result = book; } } - this.recentLast = result; - bmRecentStore.setItem('recent-last', this.recentLast);//no await + + const newRecentLastKey = (result ? result.key : null); + this.recentSetLastKey(newRecentLastKey);//no await - if (this.recentLast !== oldRecentLast) + if (newRecentLastKey !== oldKey) this.emit('recent-changed'); return result; @@ -442,24 +503,12 @@ class BookManager { delete mergedRecent[i]; } - //"ленивое" обновление хранилища - (async() => { - for (const rec of Object.values(mergedRecent)) { - if (rec.key) { - await bmRecentStore.setItem(rec.key, rec); - await utils.sleep(1); - } - } - })(); - this.recent = mergedRecent; - this.recentLast = null; - await bmRecentStore.setItem('recent-last', this.recentLast); + await this.recentSetLastKey(null); + await this.recentSetItem(null, true); - this.recentChanged = true; this.emit('set-recent'); - this.emit('recent-changed'); } addEventListener(listener) { diff --git a/client/components/Reader/versionHistory.js b/client/components/Reader/versionHistory.js index 3df6ae1c..57394587 100644 --- a/client/components/Reader/versionHistory.js +++ b/client/components/Reader/versionHistory.js @@ -1,4 +1,15 @@ export const versionHistory = [ +{ + showUntil: '2020-11-20', + header: '0.9.9 (2020-11-21)', + content: +` + +` +}, + { showUntil: '2020-11-12', header: '0.9.8 (2020-11-13)', diff --git a/client/share/utils.js b/client/share/utils.js index c773159b..c68d48a7 100644 --- a/client/share/utils.js +++ b/client/share/utils.js @@ -308,3 +308,51 @@ export function userHotKeysObjectSwap(userHotKeys) { export function removeHtmlTags(s) { return s.replace(/(<([^>]+)>)/ig, ''); } + +export function makeValidFilename(filename, repl = '_') { + let f = filename.replace(/[\x00\\/:*"<>|]/g, repl); // eslint-disable-line no-control-regex + f = f.trim(); + while (f.length && (f[f.length - 1] == '.' || f[f.length - 1] == '_')) { + f = f.substring(0, f.length - 1); + } + + if (f) + return f; + else + throw new Error('Invalid filename'); +} + +export function getBookTitle(fb2) { + fb2 = (fb2 ? fb2 : {}); + const result = {}; + + if (fb2.author) { + const authorNames = fb2.author.map(a => _.compact([ + a.lastName, + a.firstName, + a.middleName + ]).join(' ')); + + result.author = authorNames.join(', '); + } + + if (fb2.sequence) { + const seqs = fb2.sequence.map(s => _.compact([ + s.name, + (s.number ? `#${s.number}` : null), + ]).join(' ')); + + result.sequence = seqs.join(', '); + if (result.sequence) + result.sequenceTitle = `(${result.sequence})`; + } + + result.bookTitle = _.compact([result.sequenceTitle, fb2.bookTitle]).join(' '); + + result.fullTitle = _.compact([ + result.author, + result.bookTitle + ]).join(' - '); + + return result; +} \ No newline at end of file diff --git a/client/store/modules/reader.js b/client/store/modules/reader.js index 59eb206c..f99d79c8 100644 --- a/client/store/modules/reader.js +++ b/client/store/modules/reader.js @@ -16,7 +16,7 @@ const readerActions = { 'refresh': 'Принудительно обновить книгу', 'offlineMode': 'Автономный режим (без интернета)', 'contents': 'Оглавление/закладки', - 'libs': 'Библиотека', + 'libs': 'Сетевая библиотека', 'recentBooks': 'Открыть недавние', 'switchToolbar': 'Показать/скрыть панель управления', 'donate': '', diff --git a/package-lock.json b/package-lock.json index ebebb4ef..8919bab9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "Liberama", - "version": "0.9.8", + "version": "0.9.9", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 655745d3..3174b1ba 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "Liberama", - "version": "0.9.8", + "version": "0.9.9", "author": "Book Pauk ", "license": "CC0-1.0", "repository": "bookpauk/liberama", diff --git a/server/core/Reader/ReaderWorker.js b/server/core/Reader/ReaderWorker.js index 3e76f8aa..76ca0bfe 100644 --- a/server/core/Reader/ReaderWorker.js +++ b/server/core/Reader/ReaderWorker.js @@ -171,6 +171,7 @@ class ReaderWorker { } catch (e) { log(LM_ERR, e.stack); + log(LM_ERR, `downloadedFilename: ${downloadedFilename}`); if (e.message == 'abort') e.message = overLoadMes; wState.set({state: 'error', error: e.message});