When a book fails to open, a '!' button appears next to it in the list.
Clicking it prompts for the original file; the file is re-encrypted with
the current key and replaces the broken ciphertext on the server while
keeping all metadata, progress, highlights and bookmarks intact.
- Add POST /books/<pk>/replace-data/ endpoint (updates data only)
- Add _evictCachedBook() to clear IndexedDB cache for a single book
- Replace complex client-side re-encryption on password change with a
simple confirm() warning that books will need to be re-uploaded
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The encryption key is PBKDF2-derived from the login password. Changing
the password without migrating the key would make all books undecryptable
on the next login.
- Add POST /books/<pk>/rekey/ endpoint to replace a book's ciphertexts
- Password change form is now JS-driven: before submitting to the server,
derives the old key, verifies it matches localStorage, derives the new
key, re-encrypts all books (data + meta) and their highlights/bookmarks,
updates localStorage, then submits the Django form
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- HIGHLIGHTS_MAX_BYTES, BOOKMARKS_MAX_BYTES: books size limits now in settings
- PODCAST_INBOX_PAGE_SIZE: shared between podcasts/views.py and app.js via DIORA_CONFIG
- VOLUME_DEFAULT: radio stream player default volume
- ITUNES_TIMEOUT: unified iTunes API timeout (was 5s vs 6s inconsistency)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Create EBookProgress on upload so new books get a last_read timestamp
and sort to the top of the book list
- Update frontend file size check from 10 MB to 50 MB
- Fix backend error message to say 50 MB instead of 10 MB
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces scroll_fraction-only position tracking with element-based
anchors ("{index}:{innerFraction}"). Position is now stable across
font size changes and different screen sizes. A ResizeObserver
restores the anchor on viewport/orientation changes.
Falls back to scroll_fraction for books without a saved anchor.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend includes last_read from EBookProgress.updated_at.
Frontend sorts by last_read desc, unread books by uploaded_at desc.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>