Merge branch 'release/1.2.1'
This commit is contained in:
@@ -39,6 +39,7 @@ export default class DrawHelper {
|
||||
let center = false;
|
||||
let space = 0;
|
||||
let j = 0;
|
||||
const pad = this.fontSize/2;
|
||||
//формируем строку
|
||||
for (const part of line.parts) {
|
||||
let tOpen = '';
|
||||
@@ -46,7 +47,12 @@ export default class DrawHelper {
|
||||
tOpen += (part.style.italic ? '<i>' : '');
|
||||
tOpen += (part.style.sup ? '<span style="vertical-align: baseline; position: relative; line-height: 0; top: -0.3em">' : '');
|
||||
tOpen += (part.style.sub ? '<span style="vertical-align: baseline; position: relative; line-height: 0; top: 0.3em">' : '');
|
||||
tOpen += (part.style.note ? `<span style="position: relative;">` +
|
||||
`<span style="position: absolute; background-color: ${this.textColor}; opacity: 0.1; cursor: pointer; pointer-events: auto; ` +
|
||||
`height: ${this.fontSize + pad*2}px; padding: ${pad}px; left: -${pad}px; top: -${pad*0.9}px; border-radius: ${this.fontSize}px;" ` +
|
||||
`onclick="onNoteClickLiberama('${part.style.note.id}', ${part.style.note.orig ? 1 : 0})"><span style="visibility: hidden;">__TEXT</span></span>` : '');
|
||||
let tClose = '';
|
||||
tClose += (part.style.note ? '</span>' : '');
|
||||
tClose += (part.style.sub ? '</span>' : '');
|
||||
tClose += (part.style.sup ? '</span>' : '');
|
||||
tClose += (part.style.italic ? '</i>' : '');
|
||||
@@ -64,6 +70,9 @@ export default class DrawHelper {
|
||||
if (text && text.trim() == '')
|
||||
text = `<span style="white-space: pre">${text}</span>`;
|
||||
|
||||
if (part.style.note)
|
||||
tOpen = tOpen.replace('__TEXT', text);
|
||||
|
||||
lineText += `${tOpen}${text}${tClose}`;
|
||||
|
||||
center = center || part.style.center;
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
<div class="absolute" v-html="background"></div>
|
||||
<div class="absolute" v-html="pageDivider"></div>
|
||||
</div>
|
||||
<div ref="scrollBox1" class="layout over-hidden" @wheel.prevent.stop="onMouseWheel">
|
||||
<div ref="scrollBox1" class="scroll-box layout over-hidden" @wheel.prevent.stop="onMouseWheel">
|
||||
<div ref="scrollingPage1" class="layout over-hidden" @transitionend="onPage1TransitionEnd" @animationend="onPage1AnimationEnd">
|
||||
<div @copy.prevent="copyText" v-html="page1"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div ref="scrollBox2" class="layout over-hidden" @wheel.prevent.stop="onMouseWheel">
|
||||
<div ref="scrollBox2" class="scroll-box layout over-hidden" @wheel.prevent.stop="onMouseWheel">
|
||||
<div ref="scrollingPage2" class="layout over-hidden" @transitionend="onPage2TransitionEnd" @animationend="onPage2AnimationEnd">
|
||||
<div @copy.prevent="copyText" v-html="page2"></div>
|
||||
</div>
|
||||
@@ -24,14 +24,9 @@
|
||||
@wheel.prevent.stop="onMouseWheel"
|
||||
@touchstart.stop="onTouchStart" @touchend.stop="onTouchEnd" @touchmove.stop="onTouchMove" @touchcancel.prevent.stop="onTouchCancel"
|
||||
>
|
||||
<div
|
||||
v-show="showStatusBar && statusBarClickOpen" @mousedown.prevent.stop @touchstart.stop
|
||||
@click.prevent.stop="onStatusBarClick"
|
||||
v-html="statusBarClickable"
|
||||
></div>
|
||||
</div>
|
||||
<div
|
||||
v-show="!clickControl && showStatusBar && statusBarClickOpen" class="layout"
|
||||
v-show="showStatusBar && statusBarClickOpen" class="layout"
|
||||
@mousedown.prevent.stop @touchstart.stop
|
||||
@click.prevent.stop="onStatusBarClick"
|
||||
v-html="statusBarClickable"
|
||||
@@ -40,6 +35,29 @@
|
||||
<!-- невидимым делать нельзя (display: none), вовремя не подгружаютя шрифты -->
|
||||
<canvas ref="offscreenCanvas" class="layout" style="visibility: hidden"></canvas>
|
||||
<div ref="measureWidth" style="position: absolute; visibility: hidden"></div>
|
||||
|
||||
<!-- Примечание -->
|
||||
<Dialog ref="dialog1" v-model="noteDialogVisible">
|
||||
<!--template #header>
|
||||
Примечание
|
||||
</template-->
|
||||
|
||||
<div class="column col" style="line-height: 20px; max-width: 400px; max-height: 200px; overflow-x: hidden; overflow-y: auto">
|
||||
<div v-html="noteHtml"></div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div class="row col">
|
||||
<q-btn class="q-px-md q-mr-md" color="btn2" text-color="app" dense no-caps @click="goToNotes">
|
||||
В примечаниях
|
||||
</q-btn>
|
||||
</div>
|
||||
|
||||
<q-btn class="q-px-md" color="btn2" text-color="app" dense no-caps @click="noteDialogVisible = false">
|
||||
OK
|
||||
</q-btn>
|
||||
</template>
|
||||
</Dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -51,6 +69,7 @@ import {loadCSS} from 'fg-loadcss';
|
||||
import _ from 'lodash';
|
||||
import he from 'he';
|
||||
|
||||
import Dialog from '../../share/Dialog.vue';
|
||||
import './TextPage.css';
|
||||
|
||||
import * as utils from '../../../share/utils';
|
||||
@@ -62,7 +81,19 @@ import {clickMap} from '../share/clickMap';
|
||||
|
||||
const minLayoutWidth = 100;
|
||||
|
||||
//обработчик кликов по примечаниям, см. DrawHelper
|
||||
//коряво, но иначе придется сильно усложнять рендеринг страниц (через Vue)
|
||||
window.onNoteClickLiberama = (noteId, orig) => {
|
||||
const textPage = window.textPageLiberama;
|
||||
if (textPage) {
|
||||
textPage.showNote(noteId, orig);
|
||||
}
|
||||
}
|
||||
|
||||
const componentOptions = {
|
||||
components: {
|
||||
Dialog
|
||||
},
|
||||
watch: {
|
||||
bookPos: function() {
|
||||
this.$emit('book-pos-changed', {bookPos: this.bookPos, bookPosSeen: this.bookPosSeen});
|
||||
@@ -90,6 +121,7 @@ class TextPage {
|
||||
_options = componentOptions;
|
||||
|
||||
showStatusBar = false;
|
||||
statusBarClickOpen = false;
|
||||
clickControl = true;
|
||||
|
||||
background = null;
|
||||
@@ -114,6 +146,10 @@ class TextPage {
|
||||
|
||||
meta = null;
|
||||
|
||||
noteDialogVisible = false;
|
||||
noteId = '';
|
||||
noteHtml = '';
|
||||
|
||||
created() {
|
||||
this.drawHelper = new DrawHelper();
|
||||
|
||||
@@ -153,6 +189,8 @@ class TextPage {
|
||||
await utils.sleep(200);
|
||||
this.$nextTick(this.onResize);
|
||||
});
|
||||
|
||||
window.textPageLiberama = this;
|
||||
}
|
||||
|
||||
mounted() {
|
||||
@@ -297,6 +335,8 @@ class TextPage {
|
||||
top += this.statusBarHeight*(this.statusBarTop ? 1 : 0);
|
||||
let page1 = this.$refs.scrollBox1.style;
|
||||
let page2 = this.$refs.scrollBox2.style;
|
||||
|
||||
page1.pointerEvents = page2.pointerEvents = (this.clickControl ? 'none' : 'auto');
|
||||
|
||||
page1.perspective = page2.perspective = '3072px';
|
||||
|
||||
@@ -913,6 +953,22 @@ class TextPage {
|
||||
}
|
||||
}
|
||||
|
||||
doPara(paraIndex) {
|
||||
const para = this.parsed.para[paraIndex];
|
||||
|
||||
if (para && this.pageLineCount > 0) {
|
||||
const lines = this.parsed.getLines(para.offset, this.pageLineCount);
|
||||
|
||||
if (lines.length >= this.pageLineCount) {
|
||||
this.currentAnimation = this.pageChangeAnimation;
|
||||
this.pageChangeDirectionDown = true;
|
||||
this.userBookPosChange = true;
|
||||
this.bookPos = lines[0].begin;
|
||||
} else
|
||||
this.doEnd();
|
||||
}
|
||||
}
|
||||
|
||||
doToolBarToggle(event) {
|
||||
this.$emit('do-action', {action: 'switchToolbar', event});
|
||||
}
|
||||
@@ -1209,6 +1265,36 @@ class TextPage {
|
||||
|
||||
event.clipboardData.setData('text/plain', filtered);
|
||||
}
|
||||
|
||||
showNote(noteId, orig) {
|
||||
const note = this.parsed.notes[noteId];
|
||||
if (note) {
|
||||
if (orig) {//show dialog
|
||||
this.noteId = noteId;
|
||||
const pad = (note.para.length > 1 ? 20 : 0);
|
||||
this.noteHtml = note.para.map(p => `<p style="margin: 0; padding-left: ${pad}px">${p}</p>`).join('');
|
||||
this.noteDialogVisible = true;
|
||||
} else {//go to orig
|
||||
this.goToOrigNote(noteId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
goToNotes() {
|
||||
const note = this.parsed.notes[this.noteId];
|
||||
if (note && note.noteParaIndex >= 0) {
|
||||
this.doPara(note.noteParaIndex);
|
||||
this.noteDialogVisible = false;
|
||||
}
|
||||
}
|
||||
|
||||
goToOrigNote(noteId) {
|
||||
const note = this.parsed.notes[noteId];
|
||||
if (note && note.linkParaIndex >= 0) {
|
||||
this.doPara(note.linkParaIndex);
|
||||
this.noteDialogVisible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default vueComponent(TextPage);
|
||||
@@ -1244,7 +1330,7 @@ export default vueComponent(TextPage);
|
||||
}
|
||||
|
||||
.events {
|
||||
z-index: 20;
|
||||
z-index: 9;
|
||||
background-color: rgba(0,0,0,0);
|
||||
}
|
||||
|
||||
|
||||
@@ -86,17 +86,23 @@ export default class BookParser {
|
||||
let binaryType = '';
|
||||
let dimPromises = [];
|
||||
this.coverPageId = '';
|
||||
this.images = [];
|
||||
let imageNum = 0;
|
||||
|
||||
//примечания
|
||||
this.notes = {};
|
||||
let inNote = false;
|
||||
let noteId = '';
|
||||
let inNotesBody = false;
|
||||
|
||||
//оглавление
|
||||
this.contents = [];
|
||||
this.images = [];
|
||||
let curTitle = {paraIndex: -1, title: '', subtitles: []};
|
||||
let curSubtitle = {paraIndex: -1, title: ''};
|
||||
let inTitle = false;
|
||||
let inSubtitle = false;
|
||||
let sectionLevel = 0;
|
||||
let bodyIndex = 0;
|
||||
let imageNum = 0;
|
||||
|
||||
let paraIndex = -1;
|
||||
let paraOffset = 0;
|
||||
@@ -289,7 +295,7 @@ export default class BookParser {
|
||||
if (attrs.href && attrs.href.value) {
|
||||
const href = attrs.href.value;
|
||||
const alt = (attrs.alt && attrs.alt.value ? attrs.alt.value : '');
|
||||
const {id, local} = this.imageHrefToId(href);
|
||||
const {id, local} = this.hrefToId(href);
|
||||
if (local) {//local
|
||||
imageNum++;
|
||||
|
||||
@@ -322,6 +328,23 @@ export default class BookParser {
|
||||
}
|
||||
}
|
||||
|
||||
if (tag == 'a') {
|
||||
let attrs = sax.getAttrsSync(tail);
|
||||
if (attrs.href && attrs.href.value && attrs.type && attrs.type.value === 'note') {//note
|
||||
const href = attrs.href.value;
|
||||
const {id, local} = this.hrefToId(href);
|
||||
|
||||
if (local) {
|
||||
inNote = true;
|
||||
growParagraph(`<note href="${id}" orig="1">`, 0);
|
||||
|
||||
if (!this.notes[id]) {
|
||||
this.notes[id] = {id, linkParaIndex: paraIndex};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (path == '/fictionbook/description/title-info/author') {
|
||||
if (!fb2.author)
|
||||
fb2.author = [];
|
||||
@@ -350,6 +373,11 @@ export default class BookParser {
|
||||
|
||||
if (path.indexOf('/fictionbook/body') == 0) {
|
||||
if (tag == 'body') {
|
||||
let attrs = sax.getAttrsSync(tail);
|
||||
if (attrs.name && attrs.name.value === 'notes') {//notes
|
||||
inNotesBody = true;
|
||||
}
|
||||
|
||||
if (isFirstBody && fb2.annotation) {
|
||||
const ann = fb2.annotation.split('<p>').filter(v => v).map(v => utils.removeHtmlTags(v));
|
||||
ann.forEach(a => {
|
||||
@@ -389,6 +417,23 @@ export default class BookParser {
|
||||
newParagraph();
|
||||
isFirstSection = false;
|
||||
sectionLevel++;
|
||||
|
||||
if (inNotesBody) {
|
||||
let attrs = sax.getAttrsSync(tail);
|
||||
if (attrs.id && attrs.id.value) {//notes
|
||||
const id = attrs.id.value;
|
||||
let note = this.notes[id];
|
||||
if (!note) {
|
||||
note = {id};
|
||||
this.notes[id] = note;
|
||||
}
|
||||
|
||||
note.noteParaIndex = paraIndex;
|
||||
note.para = [];
|
||||
noteId = id;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (tag == 'emphasis' || tag == 'strong' || tag == 'sup' || tag == 'sub') {
|
||||
@@ -401,6 +446,14 @@ export default class BookParser {
|
||||
if (tag == 'p') {
|
||||
inPara = true;
|
||||
isFirstTitlePara = false;
|
||||
|
||||
if (inNotesBody && noteId) {
|
||||
if (!inTitle) {
|
||||
this.notes[noteId].para.push('');
|
||||
} else {
|
||||
growParagraph(`<note href="${noteId}">`, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -440,11 +493,20 @@ export default class BookParser {
|
||||
const onEndNode = (elemName) => {// eslint-disable-line no-unused-vars
|
||||
tag = elemName;
|
||||
|
||||
if (tag == 'a' && inNote) {
|
||||
growParagraph('</note>', 0);
|
||||
inNote = false;
|
||||
}
|
||||
|
||||
if (tag == 'binary') {
|
||||
binaryId = '';
|
||||
}
|
||||
|
||||
if (path.indexOf('/fictionbook/body') == 0) {
|
||||
if (tag == 'body') {
|
||||
inNotesBody = false;
|
||||
}
|
||||
|
||||
if (tag == 'title') {
|
||||
isFirstTitlePara = false;
|
||||
bold = false;
|
||||
@@ -462,6 +524,10 @@ export default class BookParser {
|
||||
|
||||
if (tag == 'p') {
|
||||
inPara = false;
|
||||
|
||||
if (inTitle && inNotesBody && noteId) {
|
||||
growParagraph('</note>', 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (tag == 'subtitle') {
|
||||
@@ -570,6 +636,12 @@ export default class BookParser {
|
||||
growParagraph(`${tOpen}${text}${tClose}`, text.length, text);
|
||||
else
|
||||
growParagraph(' ', 1);
|
||||
|
||||
if (!inTitle && inPara && inNotesBody && noteId) {
|
||||
const p = this.notes[noteId].para;
|
||||
if (p.length)
|
||||
p[p.length - 1] = p[p.length - 1] + text;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -602,7 +674,7 @@ export default class BookParser {
|
||||
return {fb2};
|
||||
}
|
||||
|
||||
imageHrefToId(id) {
|
||||
hrefToId(id) {
|
||||
let local = false;
|
||||
if (id[0] == '#') {
|
||||
id = id.substr(1);
|
||||
@@ -635,7 +707,7 @@ export default class BookParser {
|
||||
|
||||
splitToStyle(s) {
|
||||
let result = [];/*array of {
|
||||
style: {bold: Boolean, italic: Boolean, sup: Boolean, sub: Boolean, center: Boolean, space: Number},
|
||||
style: {bold: Boolean, italic: Boolean, sup: Boolean, sub: Boolean, center: Boolean, space: Number, note: Object},
|
||||
image: {local: Boolean, inline: Boolean, id: String},
|
||||
text: String,
|
||||
}*/
|
||||
@@ -686,7 +758,7 @@ export default class BookParser {
|
||||
case 'image': {
|
||||
let attrs = sax.getAttrsSync(tail);
|
||||
if (attrs.href && attrs.href.value) {
|
||||
image = this.imageHrefToId(attrs.href.value);
|
||||
image = this.hrefToId(attrs.href.value);
|
||||
image.inline = false;
|
||||
image.num = (attrs.num && attrs.num.value ? attrs.num.value : 0);
|
||||
}
|
||||
@@ -695,7 +767,7 @@ export default class BookParser {
|
||||
case 'image-inline': {
|
||||
let attrs = sax.getAttrsSync(tail);
|
||||
if (attrs.href && attrs.href.value) {
|
||||
const img = this.imageHrefToId(attrs.href.value);
|
||||
const img = this.hrefToId(attrs.href.value);
|
||||
img.inline = true;
|
||||
img.num = (attrs.num && attrs.num.value ? attrs.num.value : 0);
|
||||
result.push({
|
||||
@@ -706,6 +778,13 @@ export default class BookParser {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'note': {
|
||||
let attrs = sax.getAttrsSync(tail);
|
||||
if (attrs.href && attrs.href.value) {
|
||||
style.note = {id: attrs.href.value, orig: attrs.orig?.value};
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -734,6 +813,9 @@ export default class BookParser {
|
||||
break;
|
||||
case 'image-inline':
|
||||
break;
|
||||
case 'note':
|
||||
style.note = false;
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,18 @@
|
||||
export const versionHistory = [
|
||||
{
|
||||
version: '1.2.1',
|
||||
releaseDate: '2024-07-28',
|
||||
showUntil: '2024-08-04',
|
||||
content:
|
||||
`
|
||||
<ul>
|
||||
<li>добавлено отображение примечаний на месте, по клику на сноске (#50)</li>
|
||||
<li>исправление багов</li>
|
||||
</ul>
|
||||
|
||||
`
|
||||
},
|
||||
|
||||
{
|
||||
version: '1.2.0',
|
||||
releaseDate: '2024-03-25',
|
||||
@@ -7,7 +21,7 @@ export const versionHistory = [
|
||||
`
|
||||
<ul>
|
||||
<li>в списке загруженных, книга в архив (из архива) переносится теперь со всей группой своих версий</li>
|
||||
<li>добавлена возможность задавать в конфиге любую ссылку для кнопки "Сетевая библиотека", параматр networkLibraryLink (#47)</li>
|
||||
<li>добавлена возможность задавать в конфиге любую ссылку для кнопки "Сетевая библиотека", параметр networkLibraryLink (#47)</li>
|
||||
</ul>
|
||||
|
||||
`
|
||||
|
||||
16
package-lock.json
generated
16
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "liberama",
|
||||
"version": "1.1.3",
|
||||
"version": "1.2.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "liberama",
|
||||
"version": "1.1.3",
|
||||
"version": "1.2.0",
|
||||
"hasInstallScript": true,
|
||||
"license": "CC0-1.0",
|
||||
"dependencies": {
|
||||
@@ -3364,9 +3364,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001566",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001566.tgz",
|
||||
"integrity": "sha512-ggIhCsTxmITBAMmK8yZjEhCO5/47jKXPu6Dha/wuCS4JePVL+3uiDEBuhu2aIoT+bqTOR8L76Ip1ARL9xYsEJA==",
|
||||
"version": "1.0.30001643",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001643.tgz",
|
||||
"integrity": "sha512-ERgWGNleEilSrHM6iUz/zJNSQTP8Mr21wDWpdgvRwcTXGAq6jMtOUPP4dqFPTdKqZ2wKTdtB+uucZ3MRpAUSmg==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -13709,9 +13709,9 @@
|
||||
}
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001566",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001566.tgz",
|
||||
"integrity": "sha512-ggIhCsTxmITBAMmK8yZjEhCO5/47jKXPu6Dha/wuCS4JePVL+3uiDEBuhu2aIoT+bqTOR8L76Ip1ARL9xYsEJA==",
|
||||
"version": "1.0.30001643",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001643.tgz",
|
||||
"integrity": "sha512-ERgWGNleEilSrHM6iUz/zJNSQTP8Mr21wDWpdgvRwcTXGAq6jMtOUPP4dqFPTdKqZ2wKTdtB+uucZ3MRpAUSmg==",
|
||||
"dev": true
|
||||
},
|
||||
"chalk": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "liberama",
|
||||
"version": "1.2.0",
|
||||
"version": "1.2.1",
|
||||
"author": "Book Pauk <bookpauk@gmail.com>",
|
||||
"license": "CC0-1.0",
|
||||
"repository": "bookpauk/liberama",
|
||||
|
||||
@@ -56,6 +56,9 @@ module.exports = {
|
||||
ip: '0.0.0.0',
|
||||
port: '33443',
|
||||
accessToken: '',
|
||||
shciForHost: {
|
||||
'samlib.ru': 300000
|
||||
},
|
||||
}*/
|
||||
],
|
||||
|
||||
|
||||
@@ -27,8 +27,8 @@ class BUCServer {
|
||||
|
||||
this.cleanQueryInterval = 300*dayMs;//интервал очистки устаревших
|
||||
this.oldQueryInterval = 14*dayMs;//интервал устаревания запроса на обновление
|
||||
this.checkingInterval = 5*hourMs;//интервал проверки обновления одного и того же файла
|
||||
this.sameHostCheckInterval = 1000;//интервал проверки файла на том же сайте, не менее
|
||||
this.checkingInterval = 1*dayMs;//интервал проверки обновления одного и того же файла
|
||||
this.sameHostCheckInterval = 10*1000;//интервал проверки файла на том же сайте, не менее
|
||||
} else {
|
||||
this.maxCheckQueueLength = 10;//максимальная длина checkQueue
|
||||
this.fillCheckQueuePeriod = 10*1000;//период пополнения очереди
|
||||
@@ -51,6 +51,7 @@ class BUCServer {
|
||||
|
||||
this.checkQueue = [];
|
||||
this.hostChecking = {};
|
||||
this.shciForHost = this.config.shciForHost || {};//sameHostCheckInterval for host
|
||||
|
||||
this.main(); //no await
|
||||
|
||||
@@ -262,7 +263,7 @@ class BUCServer {
|
||||
let unchanged = true;
|
||||
let hash = '';
|
||||
|
||||
const headers = await this.down.head(row.id);
|
||||
const headers = await this.down.head(row.id, {timeout: 10*1000});
|
||||
|
||||
const etag = headers['etag'] || '';
|
||||
const modTime = headers['last-modified'] || '';
|
||||
@@ -276,7 +277,7 @@ class BUCServer {
|
||||
&& (!size || !row.size || (size !== row.size))
|
||||
) {
|
||||
|
||||
const downdata = await this.down.load(row.id);
|
||||
const downdata = await this.down.load(row.id, {timeout: 10*1000});
|
||||
|
||||
size = downdata.length;
|
||||
hash = await utils.getBufHash(downdata, 'sha256', 'hex');
|
||||
@@ -316,7 +317,12 @@ class BUCServer {
|
||||
log(LM_ERR, `error ${row.id} > ${e.stack ? e.stack : e.message}`);
|
||||
} finally {
|
||||
(async() => {
|
||||
await utils.sleep(this.sameHostCheckInterval);
|
||||
let sameHostCheckInterval = this.shciForHost[url.hostname] || this.sameHostCheckInterval;
|
||||
sameHostCheckInterval = Math.round((Math.random() - 0.5)*(sameHostCheckInterval*0.2) + sameHostCheckInterval);
|
||||
|
||||
log(`delay ${sameHostCheckInterval}ms for host '${url.hostname}'`);
|
||||
await utils.sleep(sameHostCheckInterval);
|
||||
|
||||
this.hostChecking[url.hostname] = false;
|
||||
})();
|
||||
}
|
||||
@@ -327,7 +333,7 @@ class BUCServer {
|
||||
log(LM_ERR, e.stack);
|
||||
}
|
||||
|
||||
await utils.sleep(10);
|
||||
await utils.sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ const https = require('https');
|
||||
const axios = require('axios');
|
||||
const utils = require('./utils');
|
||||
|
||||
const userAgent = 'Mozilla/5.0 (X11; HasCodingOs 1.0; Linux x64) AppleWebKit/637.36 (KHTML, like Gecko) Chrome/70.0.3112.101 Safari/637.36 HasBrowser/5.0';
|
||||
const userAgent = 'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/113.0';
|
||||
|
||||
class FileDownloader {
|
||||
constructor(limitDownloadSize = 0) {
|
||||
@@ -16,7 +16,6 @@ class FileDownloader {
|
||||
headers: {
|
||||
'accept-encoding': 'gzip, compress, deflate',
|
||||
'user-agent': userAgent,
|
||||
timeout: 300*1000,
|
||||
},
|
||||
httpsAgent: new https.Agent({
|
||||
rejectUnauthorized: false // решение проблемы 'unable to verify the first certificate' для некоторых сайтов с валидным сертификатом
|
||||
@@ -26,6 +25,9 @@ class FileDownloader {
|
||||
if (opts)
|
||||
options = Object.assign({}, opts, options);
|
||||
|
||||
if (!options.timeout)
|
||||
options.timeout = 300*1000;//5 min
|
||||
|
||||
try {
|
||||
const res = await axios.get(url, options);
|
||||
|
||||
@@ -77,8 +79,8 @@ class FileDownloader {
|
||||
const options = {
|
||||
headers: {
|
||||
'user-agent': userAgent,
|
||||
timeout: 10*1000,
|
||||
},
|
||||
timeout: 10*1000,
|
||||
};
|
||||
|
||||
const res = await axios.head(url, options);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
require('tls').DEFAULT_MIN_VERSION = 'TLSv1';
|
||||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
||||
|
||||
const fs = require('fs-extra');
|
||||
const express = require('express');
|
||||
|
||||
Reference in New Issue
Block a user