e4bb9fc4b791cf166daff41bea8d49751f8ca208
4 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
a92fe0ce2e |
fix(koplugin): upload local book cover so synced books show a cover (#4374) (#4385)
uploadBook only attached a cover.png when one was already cached under readest_covers/<hash>.png from a prior cloud download. Books that originated locally in KOReader were never downloaded, so the cover step was silently skipped and they synced to Readest with no cover. Add extractLocalCover, which renders the book's embedded cover via coverbrowser's FileManagerBookInfo:getCoverImage(nil, file_path) and writes it as PNG. uploadBook now falls back to it when no cached cover exists, caching the result under covers_dir so the Library view reuses it like a downloaded cover. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
e0cb433550 |
fix(koplugin): harden cover-download subprocess against Adreno exit crash (#4169)
Browsing a folder in the Readest Library spawns a forked child per cloud cover via syncbooks.downloadCover. On Boox / Adreno devices the child crashed with a SIGSEGV (issue #4165): it terminated through the libc exit() path, and __cxa_finalize ran the destructor of the GL driver inherited from the parent, which segfaults on Adreno. Terminate the child with ffi.C._exit(0) instead. _exit() skips libc atexit handlers, so __cxa_finalize — and the Adreno destructor it runs — never execute. The body is also wrapped in pcall so a network error in http.request cannot unwind past that _exit call. This eliminates the child-side crash in the reported tombstone. The parent KOReader exiting is most likely a knock-on effect of the child tearing down GPU state shared across the fork, but that link is not provable from the log alone — so this intentionally does not auto-close #4165 until confirmed on an affected device. No unit test: the fork + network path isn't reproducible in the busted harness, consistent with the other network methods in this file. |
||
|
|
ab2def32dd |
fix(koplugin): stop sync from wiping cloud book fields + Library polish (#4166)
* fix(koplugin): pull before push so sync doesn't wipe cloud book fields, closes #4138 The Library sync pushed a touched book row before pulling, so a row still missing the cloud's uploaded_at / metadata / group_id (e.g. one created by lightScan, not yet merged from a cloud pull) was sent with those fields nil. The server's transformBookToDB explicit-nulls uploaded_at and metadata for any field absent from the wire payload, wiping the cloud copy — after which every device that pulled lost the book's upload state. syncBooks("both") now pulls first, then pushes, and takes a before_push callback. syncBooksLibrary passes touchOpenBook through it so the open book's updated_at bump lands after the pull has refreshed the local row, letting the push carry the preserved cloud fields. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(koplugin): hide Library books with neither an uploaded nor a local file The Library showed any row with cloud_present = 1, but a bare cloud *record* whose file was never uploaded (uploaded_at NULL) has no cover and can't be opened — showing it is meaningless. Tighten the visibility predicate to (uploaded_at IS NOT NULL OR local_present = 1) across listBooks, getGroups, listBookshelfGroups and listBooksInGroup. This mirrors Readest, which only adds a synced book to the library when uploadedAt is set and keeps locally-imported books that carry a downloadedAt. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(koplugin): close the Library widget when opening a book Opening a book from the Library called ReaderUI:showReader without closing the Library Menu, so it stayed in the UIManager widget stack with M._menu still set. A later background M.refresh() — a cloud-sync or cover-download completion — then repainted that ghost Library over the reader, making it flash on screen for a few seconds. Add M.close(); route the title-bar X, M.reopen() and both handleTap book-open paths through it. A wrapped onCloseWidget clears M._menu on every close path, so M.refresh() no-ops once the Menu is gone. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
ed8956e9ed |
feat(koplugin): Readest Library view in KOReader (#4056)
* feat(koplugin/library): data layer + busted harness + design doc - LibraryStore: per-user SQLite index merging cloud + local books by partial-md5 hash. listBooks/listBookshelfBooks/listBookshelfGroups, upsertBook with cloud_present/local_present OR-merge + _force/clear sentinels, parseSyncRow, getChangedBooks for tombstone push. - EXTS table mirroring web's document.ts. - busted harness with KOReader stubs (G_reader_settings, DataStorage, lua-ljsqlite3 against :memory:); spec_helper, exts_spec, smoke_spec, librarystore_spec covering schema, sort, group nesting, dedupe. - Library design doc + spec README. - pnpm test:lua wired through root + app package.json; lint-koplugin recurses into library/ + spec/. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(koplugin/library): cloud sync — push, pull, upload, delete, downloads - Spore methods: pullBooks (incremental /sync), getDownloadUrl, getUploadUrl, listFiles, deleteFile. - syncbooks.lua: pushBook + pushChangedBooks (advances watermark to max(updated_at, deleted_at)), syncBooks(opts, mode) for push/pull/both, downloadBook + downloadCover (sync socket.http with file sink; cover download via fork+poll with single-slot queue + visible-page filter + coalesced refresh), uploadBook (presigned-PUT flow + best-effort cover), deleteCloudFiles (list-then-delete-each, mirrors cloudService.deleteBook). - SyncAuth.withFreshToken wrapper resolves the ensureClient race; 401/403 unified across syncconfig + syncannotations. - Cloud + local book covers shared by partial-md5 hash; cover.png cached at <settings>/readest_covers/<hash>.png with sentinel for known 404s. - syncbooks_spec covers row-to-wire conversion + file_key shape. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(koplugin/library): local discovery — sidecar scanner + cover provider - localscanner.lightScan iterates ReadHistory entries, reads partial_md5_checksum from .sdr sidecars, and upserts local rows. Slow filesystem walks deferred to fullSidecarWalk (24h-gated). - coverprovider wraps BookInfoManager:getBookInfo for local books with graceful FakeCover fallback when coverbrowser is absent. - localscanner_spec + coverprovider_spec. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(koplugin/library): UI — widget, item, view menu, FileManager hooks - librarywidget: full-screen Menu mixed in with CoverMenu + Mosaic/List per zen_ui's group_view pattern. Title-bar tap → view menu, search via left icon, drill-in/back for grouping. Async cloud sync deferred via scheduleIn so the menu paints before HTTP fires. - libraryitem: BIM patch for cloud-only (readest-cloud://) and group (readest-group://) URIs; group-cover composer (2x2 mosaic for grid, same in list) with cache key derived from the actual first-N hashes for content-based invalidation; ListMenuItem update + paintTo patches for wider list-mode cover strip and cloud-up/down icon overlay. - libraryviewmenu: ButtonDialog with View/Group by/Sort by/Actions. Default Group by = Groups (parity with web), values authors/groups. - librarypaint: partial-page e-ink repaint shim adapted from zen_ui. - main.lua: Library menu entry, dispatcher actions (Open Library / Push / Pull as general; progress + annotations as reader-only), "Add to Readest" button in FileManager's long-press file dialog (dedupe by partial_md5; bumps updated_at when present, inserts a fresh local-only row otherwise; un-tombstones via _clear_fields). Push books on Library open when auto_sync is on, pull-only otherwise. - Long-press action sheet with Readest BookDetailView parity: Remove from Cloud & Device / Cloud Only / Device Only, Upload to Cloud, Download Book / Cover / All. - Cloud-down + cloud-up SVG icons (LiaCloudDownloadAltSolid / LiaCloudUploadAltSolid) painted in the right-side wpageinfo slot. - i18n catalog updated for new strings. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(koplugin/library): split libraryitem into focused modules libraryitem.lua had grown to 1018 lines mixing five unrelated concerns. Split along the natural seams: cloud_covers — readest-cloud:// URI scheme, on-disk <hash>.png cache, single-slot async download queue, visible-page filter group_covers — readest-group:// URI scheme, 2x2 mosaic composer with content-derived cache key (first-N hashes), cell layout table cloud_icons — bundled cloud-up/cloud-down SVG loader, IconWidget cache, paint-overlay positioning list_strip — list-mode group row builder (4-cover wider strip replacing ListMenu's square cover slot) bim_patch — BookInfoManager:getBookInfo router (cloud / group / local) + ListMenuItem update + paintTo patches; owns the _library_local_paths set and orig BIM reference libraryitem.lua is now 141 lines: just the entry-table constructors (entry_from_row, entry_from_group, entry_back) plus thin install / set_visible_hashes delegates. Each new module is 88-216 lines. No behavior change — same 113 specs pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(koplugin): co-locate dev tooling, ship-zip exclusions, CI job split - apps/readest.koplugin/scripts/build-koplugin.mjs: local sideloading build with the same exclusions the release workflow uses. - Move lint-koplugin + test-koplugin from apps/readest-app/scripts/ to apps/readest.koplugin/scripts/. All koplugin dev tooling now lives with the koplugin and is excluded from the published release zip. - Rename to .mjs so Node treats them as ESM without the reparse warning (the i18n CommonJS scripts stay .js). - Release workflow: zip -r exclusions for scripts/, docs/, spec/, .busted so dev artifacts don't ship to end users. - PR workflow: split build_web_app into build_web_app + test_web_app for parallelism. The test job installs luarocks + busted + lsqlite3complete and runs pnpm test:lua. test-koplugin.mjs now hard-fails (instead of soft-skipping) when CI=true and a tool is missing — a broken CI toolchain previously exited 0 silently. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |