Compare commits

...

50 Commits
0.7.0 ... 0.7.2

Author SHA1 Message Date
Book Pauk
f7eb576d0d Merge branch 'release/0.7.2' 2019-10-18 09:17:45 +07:00
Book Pauk
34f1ad8fae Версия 0.7.2 2019-10-18 09:16:52 +07:00
Book Pauk
c60f0991df Правка багов 2019-10-18 09:06:04 +07:00
Book Pauk
d505fd0795 Мелкая поправка 2019-10-17 21:38:13 +07:00
Book Pauk
93cf506535 Поправка бага 2019-10-17 20:38:52 +07:00
Book Pauk
bfb37e55d4 Поправка отображения прогрессбара загрузки книги 2019-10-17 19:18:56 +07:00
Book Pauk
92afc5cb33 Поправки багов 2019-10-17 19:09:05 +07:00
Book Pauk
75cb611701 Поправки багов 2019-10-17 17:20:43 +07:00
Book Pauk
2ec1dd58a5 Добавлена ссылка "Старая версия" 2019-10-17 13:49:24 +07:00
Book Pauk
7d59af54de Улучшил определение кодировки 2019-10-16 21:48:41 +07:00
Book Pauk
2b5f47b3de Замена decompress-zip на adm-zip 2019-10-16 21:08:34 +07:00
Book Pauk
16eebfb9a4 Поправил текст ссылки 2019-10-16 16:41:26 +07:00
Book Pauk
9025218671 Мелкая поправка 2019-10-16 11:19:40 +07:00
Book Pauk
6bccb546bb Merge tag '0.7.1e' into develop
0.7.1e
2019-09-21 11:29:57 +07:00
Book Pauk
29d49046a0 Merge branch 'release/0.7.1e' 2019-09-21 11:29:46 +07:00
Book Pauk
717af9ffaf Изменил местоположение кнопки "Автономный режим" 2019-09-21 11:28:31 +07:00
Book Pauk
00060c9f43 Merge tag '0.7.1d' into develop
0.7.1d
2019-09-21 00:25:17 +07:00
Book Pauk
759ff46c92 Merge branch 'release/0.7.1d' 2019-09-21 00:25:08 +07:00
Book Pauk
41957cdceb Актуализирована справка, доделки 2019-09-21 00:24:27 +07:00
Book Pauk
d418e3a1c9 Merge tag '0.7.1c' into develop
0.7.1c
2019-09-20 23:54:50 +07:00
Book Pauk
f650124428 Merge branch 'release/0.7.1c' 2019-09-20 23:54:38 +07:00
Book Pauk
795d109c76 Поправил описание 2019-09-20 23:54:02 +07:00
Book Pauk
6868b3effc Добавлена кнопка offlineMode 2019-09-20 23:52:45 +07:00
Book Pauk
26747b7013 Небольшие поправки 2019-09-20 22:44:36 +07:00
Book Pauk
5198f8aa60 Merge tag '0.7.1b' into develop
0.7.1b
2019-09-20 22:18:09 +07:00
Book Pauk
552da48a32 Merge branch 'release/0.7.1b' 2019-09-20 22:17:58 +07:00
Book Pauk
db8a688620 Манипуляции с appcache 2019-09-20 22:17:28 +07:00
Book Pauk
3088028d05 Поправки багов 2019-09-20 21:45:29 +07:00
Book Pauk
fd62ef865d Merge tag '0.7.1a' into develop
0.7.1a
2019-09-20 20:37:33 +07:00
Book Pauk
ed74ed00ed Merge branch 'release/0.7.1a' 2019-09-20 20:37:23 +07:00
Book Pauk
741317aaaf К предыдущему 2019-09-20 20:36:31 +07:00
Book Pauk
9b6ecd4e6b Убрал вычисление диффа 2019-09-20 20:35:12 +07:00
Book Pauk
7863b3358e Убрал appcache 2019-09-20 20:34:42 +07:00
Book Pauk
e1be68ec3d Поправка бага 2019-09-20 20:20:11 +07:00
Book Pauk
a054186d4b Merge tag '0.7.1' into develop
Версия 0.7.1
2019-09-20 19:58:36 +07:00
Book Pauk
2d5c549c83 Merge branch 'release/0.7.1' 2019-09-20 19:58:22 +07:00
Book Pauk
9f6072dfe1 Версия 0.7.1 2019-09-20 19:54:59 +07:00
Book Pauk
69c44fe1ab Откатил новые версии pkg и sqlite, новый pkg глючит 2019-09-20 19:53:55 +07:00
Book Pauk
4fa7b2443e Добавил дебаг-лог в periodicCleanDir 2019-09-20 19:35:22 +07:00
Book Pauk
25a69592bb Правка багов 2019-09-20 19:14:14 +07:00
Book Pauk
44e0b26990 Поправка бага 2019-09-20 18:58:36 +07:00
Book Pauk
c4496f8dc8 Улучшение механизма синхронизации 2019-09-20 18:53:21 +07:00
Book Pauk
9e296231d9 Поправки багов 2019-09-20 16:54:03 +07:00
Book Pauk
49b3f05d65 Поправки багов 2019-09-20 16:38:33 +07:00
Book Pauk
f124b9c050 Переделки синхронизации, замена diff на delta 2019-09-20 16:29:19 +07:00
Book Pauk
63a86f7c06 Версия 0.7.1 2019-09-20 14:04:17 +07:00
Book Pauk
fd0f523c64 Улучшение синхронизации 2019-09-19 20:39:01 +07:00
Book Pauk
487e605520 Поправлен баг 2019-09-19 18:19:14 +07:00
Book Pauk
9e169e1f4b Улучшение синхронизации 2019-09-19 17:51:04 +07:00
Book Pauk
9612e7ebcd Merge tag '0.7.0' into develop
0.7.0
2019-09-07 22:15:32 +07:00
21 changed files with 1483 additions and 1189 deletions

View File

@@ -24,8 +24,8 @@ async function main() {
await fs.ensureDir(tempDownloadDir);
//sqlite3
const sqliteRemoteUrl = 'https://mapbox-node-binary.s3.amazonaws.com/sqlite3/v4.1.0/node-v72-linux-x64.tar.gz';
const sqliteDecompressedFilename = `${tempDownloadDir}/node-v72-linux-x64/node_sqlite3.node`;
const sqliteRemoteUrl = 'https://mapbox-node-binary.s3.amazonaws.com/sqlite3/v4.0.4/node-v64-linux-x64.tar.gz';
const sqliteDecompressedFilename = `${tempDownloadDir}/node-v64-linux-x64/node_sqlite3.node`;
if (!await fs.pathExists(sqliteDecompressedFilename)) {
// Скачиваем node_sqlite3.node для винды, т.к. pkg не включает его в сборку

View File

@@ -55,6 +55,6 @@ module.exports = merge(baseWpConfig, {
filename: `${publicDir}/index.html`
}),
new CopyWebpackPlugin([{from: `${clientDir}/assets/*`, to: `${publicDir}/`, flatten: true}]),
new AppCachePlugin({})
new AppCachePlugin({exclude: ['../index.html']})
]
});

View File

@@ -24,8 +24,8 @@ async function main() {
await fs.ensureDir(tempDownloadDir);
//sqlite3
const sqliteRemoteUrl = 'https://mapbox-node-binary.s3.amazonaws.com/sqlite3/v4.1.0/node-v72-win32-x64.tar.gz';
const sqliteDecompressedFilename = `${tempDownloadDir}/node-v72-win32-x64/node_sqlite3.node`;
const sqliteRemoteUrl = 'https://mapbox-node-binary.s3.amazonaws.com/sqlite3/v4.0.4/node-v64-win32-x64.tar.gz';
const sqliteDecompressedFilename = `${tempDownloadDir}/node-v64-win32-x64/node_sqlite3.node`;
if (!await fs.pathExists(sqliteDecompressedFilename)) {
// Скачиваем node_sqlite3.node для винды, т.к. pkg не включает его в сборку

View File

@@ -1,6 +1,4 @@
import _ from 'lodash';
import axios from 'axios';
import {Buffer} from 'safe-buffer';
import * as utils from '../share/utils';
@@ -66,12 +64,13 @@ class Reader {
estSize = response.headers['content-length'];
}
callback({state: 'loading', progress: 0});
const options = {
onDownloadProgress: progress => {
while (progress.loaded > estSize) estSize *= 1.5;
if (callback)
callback({state: 'loading', progress: Math.round((progress.loaded*100)/estSize)});
callback({progress: Math.round((progress.loaded*100)/estSize)});
}
}
//загрузка

View File

@@ -3,6 +3,7 @@
<h4>Возможности читалки:</h4>
<ul>
<li>загрузка любой страницы интернета</li>
<li>работа в автономном режиме (без связи)</li>
<li>изменение цвета фона, текста, размер и тип шрифта и прочее</li>
<li>установка и запоминание текущей позиции и настроек в браузере и на сервере</li>
<li>синхронизация данных (настроек и читаемых книг) между различными устройствами</li>
@@ -13,7 +14,6 @@
<li>поиск по тексту и копирование фрагмента</li>
<li>запоминание недавних книг, скачивание книги из читалки в формате fb2</li>
<li>управление кликом и с клавиатуры</li>
<li>подключение к интернету не обязательно для чтения книги после ее загрузки</li>
<li>регистрация не требуется</li>
<li>поддерживаемые браузеры: Google Chrome, Mozilla Firefox последних версий</li>
</ul>
@@ -22,18 +22,17 @@
на файл из онлайн-библиотеки (например, скопировав адрес ссылки или кнопки "скачать fb2").</p>
<p>Поддерживаемые форматы: <b>fb2, fb2.zip, html, txt</b> и другие.</p>
<p>Для автономной загрузки читалки (без интернета):<br>
В Google Chrome можно установить флаг <span class="clickable" @click="copyText('chrome://flags/#show-saved-copy', 'Ссылка на флаг успешно скопирована в буфер обмена. Можно открыть ее в новой вкладке.')">chrome://flags/#show-saved-copy</span>
в значение "Primary". В этом случае на стандартной странице "нет соединения" появится кнопка для автономной загрузки сайта из кэша.<br>
В Mozilla Firefox в автономном режиме сайт загружается из кэша автоматически. Если этого не происходит, можно установить опцию
"Веб-разработка" -> "Работать автономно".</p>
<div v-show="mode == 'omnireader'">
<p>Вы также можете добавить в свой браузер закладку, указав в ее свойствах вместо адреса следующий код:
<br><span class="clickable" @click="copyText('javascript:location.href=\'https://omnireader.ru/?url=\'+location.href;', 'Код для адреса закладки успешно скопирован в буфер обмена')">
<strong>javascript:location.href='https://omnireader.ru/?url='+location.href;</strong>
<p>Вы можете добавить в свой браузер закладку, указав в ее свойствах вместо адреса следующий код:
<br><strong>javascript:location.href='https://omnireader.ru/?url='+location.href;</strong>
&nbsp;
<span class="clickable" @click="copyText('javascript:location.href=\'https://omnireader.ru/?url=\'+location.href;', 'Код для адреса закладки успешно скопирован в буфер обмена')">
(скопировать)
</span>
<br>Тогда, нажав на получившуюся кнопку на любой странице интернета, вы автоматически откроете ее в Omni Reader.
<br>или перетащив на панель закладок следующую ссылку:
<br><a style="margin-left: 50px" href="javascript:location.href='https://omnireader.ru/?url='+location.href;">Omni Reader</a>
<br>Тогда, активировав получившуюся закладку на любой странице интернета, вы автоматически загрузите эту страницу в Omni Reader.
<br>В Chrome для Android можно вызывать такую закладку по имени прямо в адресной строке браузера (имя стоит сделать попроще).
</p>
</div>
<p>Связаться с разработчиком: <a href="mailto:bookpauk@gmail.com">bookpauk@gmail.com</a></p>

View File

@@ -20,6 +20,7 @@
<li><b>Ctrl+C</b> - скопировать текст со страницы</li>
<li><b>R</b> - принудительно обновить книгу в обход кэша</li>
<li><b>X</b> - открыть недавние</li>
<li><b>O</b> - автономный режим</li>
<li><b>S</b> - открыть окно настроек</li>
</ul>
</div>

View File

@@ -31,6 +31,7 @@
</div>
<div class="space"></div>
<span v-if="mode == 'omnireader'" class="bottom-span clickable" @click="openComments">Отзывы о читалке</span>
<span v-if="mode == 'omnireader'" class="bottom-span clickable" @click="openOldVersion">Старая версия</span>
</div>
<div class="part bottom">
@@ -137,6 +138,10 @@ class LoaderPage extends Vue {
window.open('http://samlib.ru/comment/b/bookpauk/bookpauk_reader', '_blank');
}
openOldVersion() {
window.open('http://old.omnireader.ru', '_blank');
}
keyHook(event) {
if (this.pasteTextActive) {
return this.$refs.pasteTextPage.keyHook(event);

View File

@@ -35,6 +35,9 @@
</el-button>
</el-tooltip>
<div class="space"></div>
<el-tooltip v-show="showToolButton['offlineMode']" content="Автономный режим (без интернета)" :open-delay="1000" effect="light">
<el-button ref="offlineMode" class="tool-button" :class="buttonActiveClass('offlineMode')" @click="buttonClick('offlineMode')"><i class="el-icon-connection"></i></el-button>
</el-tooltip>
<el-tooltip v-show="showToolButton['recentBooks']" content="Открыть недавние" :open-delay="1000" effect="light">
<el-button ref="recentBooks" class="tool-button" :class="buttonActiveClass('recentBooks')" @click="buttonClick('recentBooks')"><i class="el-icon-document"></i></el-button>
</el-tooltip>
@@ -257,6 +260,7 @@ class Reader extends Vue {
searchActive = false;
copyTextActive = false;
recentBooksActive = false;
offlineModeActive = false;
settingsActive = false;
helpActive = false;
clickMapActive = false;
@@ -506,27 +510,35 @@ class Reader extends Vue {
}
async bookManagerEvent(eventName) {
if (eventName == 'recent-changed') {
if (this.recentBooksActive) {
await this.$refs.recentBooksPage.updateTableData();
if (eventName == 'set-recent' || eventName == 'recent-deleted') {
const oldBook = (this.textPage ? this.textPage.lastBook : null);
const oldPos = (this.textPage ? this.textPage.bookPos : null);
const newBook = bookManager.mostRecentBook();
if (!(oldBook && newBook && oldBook.key == newBook.key)) {
this.mostRecentBook();
}
const oldBook = this.mostRecentBookReactive;
const newBook = bookManager.mostRecentBook();
if (oldBook && newBook) {
if (oldBook.key != newBook.key) {
if (oldBook.key != newBook.key || oldBook.path != newBook.path) {
this.loadingBook = true;
try {
await this.loadBook(newBook);
} finally {
this.loadingBook = false;
}
} else if (oldBook.bookPos != newBook.bookPos) {
} else if (oldPos != newBook.bookPos) {
while (this.loadingBook) await utils.sleep(100);
this.bookPosChanged({bookPos: newBook.bookPos});
}
}
}
if (eventName == 'recent-changed') {
if (this.recentBooksActive) {
await this.$refs.recentBooksPage.updateTableData();
}
}
}
get toolBarActive() {
@@ -694,6 +706,11 @@ class Reader extends Vue {
}
}
offlineModeToggle() {
this.offlineModeActive = !this.offlineModeActive;
this.$refs.serverStorage.offlineModeActive = this.offlineModeActive;
}
settingsToggle() {
this.settingsActive = !this.settingsActive;
if (this.settingsActive) {
@@ -779,11 +796,14 @@ class Reader extends Vue {
case 'copyText':
this.copyTextToggle();
break;
case 'refresh':
this.refreshBook();
break;
case 'recentBooks':
this.recentBooksToggle();
break;
case 'refresh':
this.refreshBook();
case 'offlineMode':
this.offlineModeToggle();
break;
case 'settings':
this.settingsToggle();
@@ -804,6 +824,7 @@ class Reader extends Vue {
case 'search':
case 'copyText':
case 'recentBooks':
case 'offlineMode':
case 'settings':
if (this[`${button}Active`])
classResult = classActive;
@@ -888,6 +909,7 @@ class Reader extends Vue {
this.updateRoute();
const textPage = this.$refs.page;
if (textPage.showBook) {
this.textPage = textPage;
textPage.lastBook = last;
textPage.bookPos = (last.bookPos !== undefined ? last.bookPos : 0);
@@ -912,8 +934,10 @@ class Reader extends Vue {
url = 'http://' + url;
// уже просматривается сейчас
const lastBook = (this.$refs.page ? this.$refs.page.lastBook : null);
if (!opts.force && lastBook && lastBook.url == url && await bookManager.hasBookParsed(lastBook)) {
const lastBook = (this.textPage ? this.textPage.lastBook : null);
if (!opts.force && lastBook && lastBook.url == url &&
(!opts.path || opts.path == lastBook.path) &&
await bookManager.hasBookParsed(lastBook)) {
this.loaderActive = false;
return;
}
@@ -942,7 +966,7 @@ class Reader extends Vue {
if (!opts.force) {
// пытаемся загрузить и распарсить книгу в менеджере из локального кэша
const bookParsed = await bookManager.getBook({url}, (prog) => {
const bookParsed = await bookManager.getBook({url, path: opts.path}, (prog) => {
progress.setState({progress: prog});
});
@@ -963,6 +987,7 @@ class Reader extends Vue {
// иначе идем на сервер
// пытаемся загрузить готовый файл с сервера
if (wasOpened.path) {
progress.setState({totalSteps: 5});
try {
const resp = await readerApi.loadCachedBook(wasOpened.path, (state) => {
progress.setState(state);
@@ -975,7 +1000,6 @@ class Reader extends Vue {
}
progress.setState({totalSteps: 5});
// не удалось, скачиваем книгу полностью с конвертацией
let loadCached = true;
if (!book) {
@@ -1049,7 +1073,7 @@ class Reader extends Vue {
let page = this.$refs.page;
while (this.blinkCount) {
this.showRefreshIcon = !this.showRefreshIcon;
if (page.blinkCachedLoadMessage)
if (page && page.blinkCachedLoadMessage)
page.blinkCachedLoadMessage(this.showRefreshIcon);
await utils.sleep(500);
if (this.stopBlink)
@@ -1059,7 +1083,7 @@ class Reader extends Vue {
}
this.showRefreshIcon = true;
this.inBlink = false;
if (page.blinkCachedLoadMessage)
if (page && page.blinkCachedLoadMessage)
page.blinkCachedLoadMessage('finish');
});
}
@@ -1129,6 +1153,9 @@ class Reader extends Vue {
event.preventDefault();
event.stopPropagation();
break;
case 'KeyO':
this.offlineModeToggle();
break;
case 'KeyS':
this.settingsToggle();
break;

View File

@@ -251,6 +251,7 @@ class RecentBooksPage extends Vue {
}
if (this.tableData.length > result.length)
this.tableData.splice(result.length);*/
this.tableData = result;
this.updating = false;
}

View File

@@ -71,8 +71,24 @@ class ServerStorage extends Vue {
}
async bookManagerEvent(eventName, itemKey) {
if (eventName == 'recent-changed') {
this.debouncedSaveRecent(itemKey);
if (!this.serverSyncEnabled)
return;
if (eventName == 'recent-changed') {
if (itemKey) {
if (!this.recentDeltaInited) {
await this.loadRecent();
this.warning('Функции сохранения на сервер пока недоступны');
return;
}
if (!this.recentDelta)
this.recentDelta = {};
this.recentDelta[itemKey] = _.cloneDeep(bookManager.recent[itemKey]);
this.debouncedSaveRecent(itemKey);
}
}
}
@@ -103,7 +119,7 @@ class ServerStorage extends Vue {
await this.loadProfiles(force);
this.checkCurrentProfile();
await this.currentProfileChanged(force);
await this.loadRecent(force);
await this.loadRecent();
if (force)
await this.saveRecent();
}
@@ -160,12 +176,12 @@ class ServerStorage extends Vue {
}
warning(message) {
if (this.showServerStorageMessages)
if (this.showServerStorageMessages && !this.offlineModeActive)
this.$notify.warning({message});
}
error(message) {
if (this.showServerStorageMessages)
if (this.showServerStorageMessages && !this.offlineModeActive)
this.$notify.error({message});
}
@@ -324,55 +340,85 @@ class ServerStorage extends Vue {
}
}
async loadRecent(force = false, doNotifySuccess = true) {
if (!this.keyInited || !this.serverSyncEnabled)
async initRecentDelta() {
let recentDelta = null;
try {
recentDelta = await this.storageGet({recentDelta: {}});
} catch(e) {
this.error(`Ошибка соединения с сервером: ${e.message}`);
return;
const oldRecentRev = bookManager.recentRev;
const oldRecentDiffRev = bookManager.recentDiffRev;
//проверим ревизию на сервере
let revs = null;
if (!force) {
try {
revs = await this.storageCheck({recent: {}, recentDiff: {}});
if (revs.state == 'success' && revs.items.recent.rev == oldRecentRev &&
revs.items.recentDiff.rev == oldRecentDiffRev) {
return;
}
} catch(e) {
this.error(`Ошибка соединения с сервером: ${e.message}`);
return;
}
}
let recent = null;
if (force || revs.items.recent.rev != oldRecentRev || revs.items.recentDiff.rev != oldRecentDiffRev) {
if (recentDelta.state == 'success') {
recentDelta = recentDelta.items.recentDelta;
if (recentDelta.rev == 0)
recentDelta.data = {};
this.recentDelta = recentDelta.data;
this.recentDeltaInited = true;
} else {
this.warning(`Неверный ответ сервера: ${recentDelta.state}`);
}
}
async loadRecent(skipRevCheck = false, doNotifySuccess = true) {
if (!this.keyInited || !this.serverSyncEnabled || this.loadingRecent)
return;
this.loadingRecent = true;
try {
const oldRecentRev = bookManager.recentRev;
const oldRecentDeltaRev = bookManager.recentDeltaRev;
//проверим ревизию на сервере
let revs = null;
if (!skipRevCheck) {
try {
revs = await this.storageCheck({recent: {}, recentDelta: {}});
if (revs.state == 'success' && revs.items.recent.rev == oldRecentRev &&
revs.items.recentDelta.rev == oldRecentDeltaRev) {
if (!this.recentDeltaInited)
await this.initRecentDelta();
return;
}
} catch(e) {
this.error(`Ошибка соединения с сервером: ${e.message}`);
return;
}
}
let recent = null;
try {
recent = await this.storageGet({recent: {}, recentDiff: {}});
recent = await this.storageGet({recent: {}, recentDelta: {}});
} catch(e) {
this.error(`Ошибка соединения с сервером: ${e.message}`);
return;
}
if (recent.state == 'success') {
let recentDiff = recent.items.recentDiff;
let recentDelta = recent.items.recentDelta;
recent = recent.items.recent;
if (recent.rev == 0)
recent.data = {};
this.oldRecent = _.cloneDeep(recent.data);
let newRecent = {};
if (recentDiff && recentDiff.data) {
newRecent = utils.applyObjDiff(recent.data, recentDiff.data);
this.recentDiff = _.cloneDeep(recentDiff.data);
if (!utils.isObjDiff(this.recentDiff))
this.recentDiff = null;
if (recentDelta && recentDelta.data) {
if (recentDelta.data.diff) {
newRecent = recent.data;
const key = recentDelta.data.diff.key;
if (newRecent[key])
newRecent[key] = utils.applyObjDiff(newRecent[key], recentDelta.data.diff);
} else {
newRecent = Object.assign(recent.data, recentDelta.data);
}
this.recentDelta = recentDelta.data;
} else {
newRecent = recent.data;
this.recentDiff = null;
this.recentDelta = {};
}
this.recentDeltaInited = true;
if (!bookManager.loaded) {
this.warning('Ожидание загрузки списка книг перед синхронизацией');
@@ -380,114 +426,108 @@ class ServerStorage extends Vue {
}
await bookManager.setRecent(newRecent);
await bookManager.setRecentRev(recent.rev);
await bookManager.setRecentDiffRev(recentDiff.rev);
await bookManager.setRecentDeltaRev(recentDelta.rev);
} else {
this.warning(`Неверный ответ сервера: ${recent.state}`);
}
}
if (doNotifySuccess)
this.debouncedNotifySuccess();
if (doNotifySuccess)
this.debouncedNotifySuccess();
} finally {
this.loadingRecent = false;
}
}
async saveRecent(itemKey) {
async saveRecent(itemKey, recurse) {
if (!this.keyInited || !this.serverSyncEnabled || this.savingRecent)
return;
const bm = bookManager;
/*if (!bookManager.loaded) {
this.warning('Функции сохранения на сервер пока недоступны');
return;
}*/
//несколько замудреная инициализация oldRecent
if (!this.oldRecent) {
this.oldRecent = _.cloneDeep(bookManager.recent);
}
if (bookManager.loaded && !this.oldRecentInited) {
this.oldRecent = _.cloneDeep(bookManager.recent);
this.oldRecentInited = true;
}
//вычисляем дифф
let diff = null;
if (itemKey) {//ускоренное вычисления диффа
let itemDiff;
if (this.oldRecent[itemKey]) {
itemDiff = utils.getObjDiff({[itemKey]: (this.oldRecentInited ? this.oldRecent[itemKey] : {})}, {[itemKey]: bm.recent[itemKey]});
} else {
itemDiff = utils.getObjDiff({}, {[itemKey]: bm.recent[itemKey]});
}
if (this.recentDiff) {
diff = this.recentDiff;
if (itemDiff.change[itemKey])
diff.change[itemKey] = itemDiff.change[itemKey];
if (itemDiff.add[itemKey])
diff.add[itemKey] = itemDiff.add[itemKey];
} else {
diff = itemDiff;
}
} else {//медленное вычисление диффа
if (this.oldRecentInited) {
diff = utils.getObjDiff(this.oldRecent, bm.recent);
} else
return;
}
if (utils.isEmptyObjDiff(diff))
return;
//вычисление критерия сохранения целиком
let forceSaveRecent = JSON.stringify(diff).length > 2000;
if (!forceSaveRecent && itemKey) {
if (!this.sameKeyCount)
this.sameKeyCount = 0;
if (this.prevItemKey == itemKey)
this.sameKeyCount++;
forceSaveRecent = this.sameKeyCount > 5 && (Object.keys(diff.change).length > 1);
this.sameKeyCount = (!forceSaveRecent ? this.sameKeyCount : 0);
this.prevItemKey = itemKey;
if (!this.sameKeyCount)
this.sameKeyCount = 0;
if (this.prevItemKey == itemKey) {
this.sameKeyCount++;
} else {
this.sameKeyCount = 0;
}
this.recentDiff = diff;
const l = Object.keys(this.recentDelta).length - (1*(!!this.recentDelta.diff));
this.makeDeltaDiff = (l == 1 && this.prevItemKey == itemKey ? this.makeDeltaDiff : false);
const forceSaveRecent = l > 20 || (this.sameKeyCount > 5 && (l > 1)) || (l == 1 && this.sameKeyCount > 10 && !this.makeDeltaDiff);
this.sameKeyCount = (!forceSaveRecent ? this.sameKeyCount : 0);
this.prevItemKey = itemKey;
//дифф от дельты для уменьшения размера передаваемых данных в частном случае
if (this.makeDeltaDiff) {
this.recentDelta.diff = utils.getObjDiff(this.prevSavedItem, bm.recent[itemKey]);
this.recentDelta.diff.key = itemKey;
delete this.recentDelta[itemKey];
} else if (this.recentDelta.diff) {
const key = this.recentDelta.diff.key;
if (!this.prevSavedItem && bm.recent[key])
this.prevSavedItem = _.cloneDeep(bm.recent[key]);
if (this.prevSavedItem) {
this.recentDelta[key] = utils.applyObjDiff(this.prevSavedItem, this.recentDelta.diff);
}
delete this.recentDelta.diff;
}
//сохранение
this.savingRecent = true;
try {
if (forceSaveRecent) {//сохраняем recent целиком
let result = {state: ''};
try {
result = await this.storageSet({recent: {rev: bm.recentRev + 1, data: bm.recent}, recentDiff: {rev: bm.recentDiffRev + 1, data: {}}});
result = await this.storageSet({recent: {rev: bm.recentRev + 1, data: bm.recent}, recentDelta: {rev: bm.recentDeltaRev + 1, data: {}}});
} catch(e) {
this.error(`Ошибка соединения с сервером (${e.message}). Данные не сохранены и могут быть перезаписаны.`);
}
if (result.state == 'reject') {
await this.loadRecent(true, false);
this.warning(`Последние изменения отменены. Данные синхронизированы с сервером.`);
if (!recurse) {
this.savingRecent = false;
this.recentDelta[itemKey] = _.cloneDeep(bm.recent[itemKey]);
this.saveRecent(itemKey, true);
return;
}
} else if (result.state == 'success') {
this.oldRecent = _.cloneDeep(bm.recent);
this.recentDiff = null;
this.makeDeltaDiff = true;
this.prevSavedItem = _.cloneDeep(bm.recent[itemKey]);
this.recentDelta = {};
await bm.setRecentRev(bm.recentRev + 1);
await bm.setRecentDiffRev(bm.recentDiffRev + 1);
await bm.setRecentDeltaRev(bm.recentDeltaRev + 1);
}
} else {//сохраняем только дифф
let result = {state: ''};
try {
result = await this.storageSet({recentDiff: {rev: bm.recentDiffRev + 1, data: this.recentDiff}});
result = await this.storageSet({recentDelta: {rev: bm.recentDeltaRev + 1, data: this.recentDelta}});
} catch(e) {
this.error(`Ошибка соединения с сервером (${e.message}). Данные не сохранены и могут быть перезаписаны.`);
}
if (result.state == 'reject') {
await this.loadRecent(true, false);
this.warning(`Последние изменения отменены. Данные синхронизированы с сервером.`);
if (!recurse) {
this.savingRecent = false;
this.recentDelta[itemKey] = _.cloneDeep(bm.recent[itemKey]);
this.saveRecent(itemKey, true);
return;
}
} else if (result.state == 'success') {
await bm.setRecentDiffRev(bm.recentDiffRev + 1);
await bm.setRecentDeltaRev(bm.recentDeltaRev + 1);
}
}
} finally {

View File

@@ -37,7 +37,7 @@ class BookManager {
}
this.recentRev = await bmRecentStore.getItem('recent-rev') || 0;
this.recentDiffRev = await bmRecentStore.getItem('recent-diff-rev') || 0;
this.recentDeltaRev = await bmRecentStore.getItem('recent-delta-rev') || 0;
this.recentChanged = true;
@@ -262,6 +262,11 @@ class BookManager {
this.books[meta.key] = result;
}
//Если файл на сервере изменился, считаем, что в кеше его нету
if (meta.path && result && meta.path != result.path) {
return;
}
if (result && !result.parsed) {
let data = await bmDataStore.getItem(`bmData-${meta.key}`);
callback(5);
@@ -356,7 +361,8 @@ class BookManager {
let result = this.recent[value.key];
if (!result) {
result = await bmRecentStore.getItem(value.key);
this.recent[value.key] = result;
if (result)
this.recent[value.key] = result;
}
return result;
}
@@ -369,6 +375,7 @@ class BookManager {
this.recentLast = null;
await bmRecentStore.setItem('recent-last', this.recentLast);
}
this.emit('recent-deleted', value.key);
this.emit('recent-changed', value.key);
}
@@ -410,6 +417,7 @@ class BookManager {
if (this.recentLast !== oldRecentLast)
this.emit('recent-changed');
return result;
}
@@ -431,6 +439,12 @@ class BookManager {
const mergedRecent = _.cloneDeep(this.recent);
Object.assign(mergedRecent, value);
//подстраховка
for (let i of Object.keys(mergedRecent)) {
if (!mergedRecent[i].key || mergedRecent[i].key !== i)
delete mergedRecent[i];
}
//"ленивое" обновление хранилища
(async() => {
@@ -448,6 +462,7 @@ class BookManager {
await bmRecentStore.setItem('recent-last', this.recentLast);
this.recentChanged = true;
this.emit('set-recent');
this.emit('recent-changed');
}
@@ -456,12 +471,11 @@ class BookManager {
this.recentRev = value;
}
async setRecentDiffRev(value) {
await bmRecentStore.setItem('recent-diff-rev', value);
this.recentDiffRev = value;
async setRecentDeltaRev(value) {
await bmRecentStore.setItem('recent-delta-rev', value);
this.recentDeltaRev = value;
}
addEventListener(listener) {
if (this.eventListeners.indexOf(listener) < 0)
this.eventListeners.push(listener);

View File

@@ -1,4 +1,28 @@
export const versionHistory = [
{
showUntil: '2019-10-17',
header: '0.7.2 (2019-10-18)',
content:
`
<ul>
<li>исправления багов</li>
</ul>
`
},
{
showUntil: '2019-09-19',
header: '0.7.1 (2019-09-20)',
content:
`
<ul>
<li>исправления багов</li>
<li>на панель управления добавлена кнопка "Автономный режим"</li>
<li>актуализирована справка</li>
</ul>
`
},
{
showUntil: '2019-10-01',
header: '0.7.0 (2019-09-07)',

View File

@@ -8,6 +8,7 @@ const toolButtons = [
{name: 'search', show: true, text: 'Найти в тексте'},
{name: 'copyText', show: false, text: 'Скопировать текст со страницы'},
{name: 'refresh', show: true, text: 'Принудительно обновить книгу'},
{name: 'offlineMode', show: false, text: 'Автономный режим (без интернета)'},
{name: 'recentBooks', show: true, text: 'Открыть недавние'},
];

Binary file not shown.

File diff suppressed because one or more lines are too long

2239
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "Liberama",
"version": "0.7.0",
"version": "0.7.2",
"engines": {
"node": ">=10.0.0"
},
@@ -41,7 +41,7 @@
"mini-css-extract-plugin": "^0.5.0",
"null-loader": "^0.1.1",
"optimize-css-assets-webpack-plugin": "^5.0.3",
"pkg": "^4.4.0",
"pkg": "4.3.7",
"terser-webpack-plugin": "^1.4.1",
"url-loader": "^1.1.2",
"vue-class-component": "^6.3.2",
@@ -55,12 +55,12 @@
"webpack-merge": "^4.2.2"
},
"dependencies": {
"adm-zip": "^0.4.13",
"appcache-webpack-plugin": "^1.4.0",
"axios": "^0.18.1",
"base-x": "^3.0.6",
"chardet": "^0.7.0",
"compression": "^1.7.4",
"decompress-zip": "^0.2.2",
"element-ui": "^2.12.0",
"express": "^4.17.1",
"fg-loadcss": "^2.1.0",
@@ -77,7 +77,7 @@
"safe-buffer": "^5.2.0",
"sjcl": "^1.0.8",
"sql-template-strings": "^2.2.2",
"sqlite": "^3.0.3",
"sqlite": "3.0.0",
"tar-fs": "^2.0.0",
"unbzip2-stream": "^1.3.3",
"vue": "^2.6.10",

View File

@@ -74,7 +74,7 @@ function getEncoding(buf, returnAll) {
if (returnAll)
return sorted;
else if (sorted[0].c > 0)
else if (sorted[0].c > 0 && sorted[0].c > sorted[0].totalChecked/2)
return sorted[0].codePage;
else
return 'ISO-8859-5';

View File

@@ -4,7 +4,7 @@ const crypto = require('crypto');
const path = require('path');
const unbzip2Stream = require('unbzip2-stream');
const tar = require('tar-fs');
const DecompressZip = require('decompress-zip');
const AdmZip = require('adm-zip');
const utils = require('./utils');
const FileDetector = require('./FileDetector');
@@ -112,26 +112,17 @@ class FileDecompressor {
}
async unZip(filename, outputDir) {
return new Promise((resolve, reject) => {
return new Promise((resolve) => {
const files = [];
const unzipper = new DecompressZip(filename);
const zip = new AdmZip(filename);
unzipper.on('error', function(err) {
reject(err);
zip.getEntries().forEach(function(zipEntry) {
files.push({path: zipEntry.entryName, size: zipEntry.header.size});
});
unzipper.on('extract', function() {
resolve(files);
});
zip.extractAllTo(outputDir, true);
unzipper.extract({
path: outputDir,
filter: function(file) {
if (file.type == 'File')
files.push({path: file.path, size: file.uncompressedSize});
return true;
}
});
resolve(files);
});
}

View File

@@ -7,6 +7,7 @@ const FileDownloader = require('./FileDownloader');
const FileDecompressor = require('./FileDecompressor');
const BookConverter = require('./BookConverter');
const utils = require('./utils');
const log = require('./getLogger').getLog();
let singleCleanExecute = false;
@@ -133,6 +134,7 @@ class ReaderWorker {
async periodicCleanDir(dir, maxSize, timeout) {
try {
log(`Start clean dir: ${dir}, maxSize=${maxSize}`);
const list = await fs.readdir(dir);
let size = 0;
@@ -144,16 +146,21 @@ class ReaderWorker {
files.push({name, stat});
}
}
log(`found ${files.length} files in dir ${dir}`);
files.sort((a, b) => a.stat.mtimeMs - b.stat.mtimeMs);
let i = 0;
while (i < files.length && size > maxSize) {
const file = files[i];
log(`rm ${dir}/${file.name}`);
await fs.remove(`${dir}/${file.name}`);
size -= file.stat.size;
i++;
}
log(`removed ${i} files`);
} catch(e) {
log(LM_ERR, e.message);
} finally {
setTimeout(() => {
this.periodicCleanDir(dir, maxSize, timeout);