Bücher: Titel in "Zuletzt gelesen"/Ordner-Kacheln unsichtbar (Button-Farbvererbung); Regal-Lookup jetzt auch für Altbestand (SW v38)
<button> erbt in den meisten Browsern nicht automatisch color von der Seite — .book-recent-item und .book-folder-tile hatten keine explizite Farbe gesetzt, wodurch der Titeltext (kein separates color wie beim gedimmten Autor-Span) unsichtbar auf dunklem Grund landete. Der Metadaten-Button funktionierte bisher nur für Bücher, die nach der letzten Änderung hochgeladen wurden (ISBN wurde nur dort extrahiert). Fehlt die ISBN im Meta-Blob, wird sie jetzt bei Bedarf direkt aus der bereits clientseitig entschlüsselbaren Buchdatei nachgeholt (gleiche OPF-Quelle wie beim Öffnen) und für künftige Abrufe mitgespeichert — kein erneuter Upload nötig. Nur EPUB, PDFs liefern über pdf.js keine verlässliche ISBN. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011j4LcoeddwFt6Rw8Wyaknj
This commit is contained in:
parent
3e301b6f61
commit
e6b3ee620e
3 changed files with 56 additions and 9 deletions
|
|
@ -1684,8 +1684,10 @@ body.dnd-mode .timer-display {
|
|||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
background: none;
|
||||
color: var(--fg);
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
font: inherit;
|
||||
}
|
||||
.book-recent-item-title {
|
||||
font-size: 13px;
|
||||
|
|
@ -1717,8 +1719,10 @@ body.dnd-mode .timer-display {
|
|||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
background: none;
|
||||
color: var(--fg);
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
font: inherit;
|
||||
}
|
||||
.book-folder-tile-icon {
|
||||
font-size: 22px;
|
||||
|
|
|
|||
|
|
@ -3618,6 +3618,35 @@ async function assignBookFolder(bookId) {
|
|||
}
|
||||
}
|
||||
|
||||
// Fallback for books uploaded before ISBN extraction existed (or repaired/re-encrypted
|
||||
// without it): pulls the already-accessible encrypted book bytes, decrypts and unzips just
|
||||
// far enough to read the OPF identifiers — same source openBook() itself parses — without
|
||||
// doing a full reader render. EPUB only; PDFs never carried a reliable ISBN via pdf.js.
|
||||
async function _extractIsbnFromBookFile(bookId) {
|
||||
try {
|
||||
const key = await getOrCreateEncKey();
|
||||
let data_ct, data_iv;
|
||||
const cached = await _getCachedBook(bookId);
|
||||
if (cached) {
|
||||
({data_ct, data_iv} = cached);
|
||||
} else {
|
||||
const res = await fetch(`/books/${bookId}/data/`);
|
||||
({data_ct, data_iv} = await res.json());
|
||||
}
|
||||
const plain = await decryptBytes(key, data_iv, data_ct);
|
||||
const zip = await JSZip.loadAsync(plain);
|
||||
const containerXml = await zip.file('META-INF/container.xml').async('text');
|
||||
const opfPath = new DOMParser()
|
||||
.parseFromString(containerXml, 'application/xml')
|
||||
.querySelector('rootfile')?.getAttribute('full-path');
|
||||
if (!opfPath) return '';
|
||||
const opfDoc = new DOMParser().parseFromString(await zip.file(opfPath).async('text'), 'application/xml');
|
||||
return _extractIsbnFromOpf(opfDoc);
|
||||
} catch (e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
// Manually triggered (never automatic) — looks up which library shelf/DDC category this
|
||||
// book falls under via the server-side DNB→Open Library proxy, purely as an organizational
|
||||
// hint (shown as a badge). See books/views.py:lookup_book_metadata for why this one call is
|
||||
|
|
@ -3625,20 +3654,34 @@ async function assignBookFolder(bookId) {
|
|||
async function lookupBookMetadata(bookId) {
|
||||
const book = _lastBookListData.find(b => b.id === bookId);
|
||||
if (!book) return;
|
||||
const isbn = (bookMetaCache[bookId] || {}).isbn || book.isbn || '';
|
||||
const cached = bookMetaCache[bookId] || {};
|
||||
let isbn = cached.isbn || book.isbn || '';
|
||||
const isPdf = (cached.type || book.type) === 'pdf';
|
||||
|
||||
if (!isbn) {
|
||||
await customAlert('Keine ISBN in den Metadaten dieses Buchs gefunden (nur bei neueren Uploads automatisch erkannt).');
|
||||
return;
|
||||
if (isPdf) {
|
||||
await customAlert('Keine ISBN gespeichert — bei PDFs wird sie nicht automatisch erkannt.');
|
||||
return;
|
||||
}
|
||||
// Older upload without a stored ISBN — extract it from the book file itself (may take
|
||||
// a moment for large books, since it downloads/decrypts the full file if not cached).
|
||||
isbn = await _extractIsbnFromBookFile(bookId);
|
||||
if (!isbn) {
|
||||
await customAlert('Keine ISBN in diesem Buch gefunden.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch(`/books/metadata-lookup/?isbn=${encodeURIComponent(isbn)}`);
|
||||
const data = await res.json();
|
||||
if (!data.label) {
|
||||
await customAlert('Keine Regal-Kategorie gefunden (weder bei der DNB noch bei Open Library).');
|
||||
return;
|
||||
}
|
||||
await _updateBookMeta(bookId, {shelfTag: data.label});
|
||||
// Persist the (possibly newly-found) ISBN either way, so a repeat lookup never needs
|
||||
// to re-download/decrypt the book file again.
|
||||
await _updateBookMeta(bookId, data.label ? {isbn, shelfTag: data.label} : {isbn});
|
||||
renderBookList(_lastBookListData);
|
||||
if (!data.label) {
|
||||
await customAlert('Keine Regal-Kategorie gefunden (weder bei der DNB noch bei Open Library). Die ISBN wurde für spätere Versuche gespeichert.');
|
||||
}
|
||||
} catch (e) {
|
||||
await customAlert('Metadaten-Abruf fehlgeschlagen: ' + e.message);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
* diora service worker — caches the app shell for offline use.
|
||||
*/
|
||||
|
||||
const CACHE = 'diora-v37';
|
||||
const CACHE = 'diora-v38';
|
||||
const PODCAST_CACHE = 'diora-podcast-v1';
|
||||
const SHELL = [
|
||||
'/static/css/app.css',
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue