diora-web/static/js/app/57-cloud-inbox.js

80 lines
3.3 KiB
JavaScript
Raw Normal View History

'use strict';
// ---------------------------------------------------------------------------
// "Eingang" (docs/library.md): a cloud source marked so brings its new books into the
// library by itself. When the library loads, the server says which files of such sources
// no device has taken yet (and keeps them for this device ten minutes); each is fetched
// through the server, encrypted here like any import, uploaded, and reported done. A book
// already in the library (same title and author) is left out. The folder stays as it is.
// Only where the book key is there; not in diora Reader (e-readers: battery, weak Wi-Fi).
// ---------------------------------------------------------------------------
const CLOUD_INBOX_EVERY_MS = 5 * 60 * 1000;
let _cloudInboxBusy = false;
let _cloudInboxLast = 0;
function _cloudInboxKeyReady() {
// getOrCreateEncKey() would make a new key when there is none: never import without the real one.
try { return !!localStorage.getItem(`diora_enc_key_${window.USER_ID || 0}`); } catch (e) { return false; }
}
async function cloudInboxCheck(force) {
if (typeof IS_AUTHENTICATED === 'undefined' || !IS_AUTHENTICATED) return;
if (document.documentElement.classList.contains('nui-reader-app')) return;
if (_cloudInboxBusy || !_cloudInboxKeyReady()) return;
if (!force && Date.now() - _cloudInboxLast < CLOUD_INBOX_EVERY_MS) return;
_cloudInboxBusy = true;
_cloudInboxLast = Date.now();
const statusEl = $('book-upload-status');
const taken = [];
try {
const r = await fetch('/books/cloud/inbox/', {cache: 'no-store'});
if (!r.ok) return;
const files = (await r.json()).files || [];
for (let i = 0; i < files.length; i++) {
const f = files[i];
if (statusEl) statusEl.textContent = `Eingang „${f.label}“: ${i + 1} von ${files.length} – ${f.name} …`;
let result = 'failed';
try {
if (f.size > DIORA_CONFIG.ebookMaxBytes) {
result = 'too_big';
} else {
const res = await fetch(`/books/cloud/${f.source}/fetch/?path=${encodeURIComponent(f.path)}`);
if (res.ok) {
const out = await _importEbookBuffer(await res.arrayBuffer(), f.name, null, {reload: false, skipKnown: true});
result = out === 'dup' ? 'known' : out ? 'imported' : 'failed';
}
}
} catch (e) {
result = 'failed';
}
if (result === 'imported') taken.push(f);
await fetch(`/books/cloud/${f.source}/done/`, {
method: 'POST', headers: {'Content-Type': 'application/json'},
body: JSON.stringify({path: f.path, result}),
}).catch(() => {});
}
} catch (e) {
// Offline or the source unreachable: next time.
} finally {
_cloudInboxBusy = false;
}
if (statusEl) {
const labels = [...new Set(taken.map(f => f.label))].map(l => `„${l}“`).join(', ');
statusEl.textContent = taken.length
? `${taken.length === 1 ? 'Ein neues Buch' : taken.length + ' neue Bücher'} aus ${labels} übernommen.` : '';
}
if (taken.length) loadBookList();
}
// After the list has loaded (and the key is known), quietly look into the inboxes.
(function () {
const orig = window.loadBookList;
if (typeof orig !== 'function') return;
window.loadBookList = async function () {
const out = await orig.apply(this, arguments);
setTimeout(() => cloudInboxCheck(false), 1500);
return out;
};
})();