feat: ratings/review/reading-time sync bridge (handoff item 4 Batch B)
Three new What-to-sync toggles — Ratings, Book review, Reading time —
plus a manual 'Sync rating, review & time now' menu action.
Ratings mirror summary.rating (0-5) against the server's 1-10 (exact
doubling over, nearest-star back, 0 clears). Reviews mirror
summary.note as raw markdown (the review box shows it literally, the
same accepted tradeoff as notes). Reading time pushes the statistics
plugin's cumulative book total (getStatsBookStatus) — the server
watermark-converts only the delta, so repeated pushes are harmless.
KOReader's summary carries no timestamps, so sync runs on
change-detection: the plugin remembers the last values it pushed per
book (settings.meta_sync_state); a local diff means koreader changed
it (push), a server diff with an unchanged local copy means the server
moved (pull applies, writing straight into doc_settings + flush).
Pull on book open (2s behind the progress pull), push on book close
inside the existing goOnlineToRun window, and both in the manual
action (push first so a koreader edit survives the same gesture).
Server endpoints: /api/sync/koreader/{rating,review,stats}.
This commit is contained in:
1 parent
b466af09b0
commit
001628f2a2
2 files changed
+327
No files matched your search
@@ -155,6 +155,64 @@ function BookhoardAPI:getMetadata(uuid)
|
||||
return self:_request("GET", "/api/sync/koreader/metadata/" .. uuid, nil, PROGRESS_TIMEOUTS)
|
||||
end
|
||||
|
||||
-- ----- Ratings / review / reading-time bridge (handoff item 4 Batch B) -----
|
||||
|
||||
-- GET ref: only uuid/sha256 travel as query params (both URL-safe); the
|
||||
-- fallback title/authors resolution only exists on the push side, where the
|
||||
-- body can carry them. An unlinked book has no server rating to pull anyway.
|
||||
local function book_ref_query(ref)
|
||||
local q = {}
|
||||
if ref.uuid and ref.uuid ~= "" then
|
||||
table.insert(q, "uuid=" .. ref.uuid)
|
||||
end
|
||||
if ref.sha256 and ref.sha256 ~= "" then
|
||||
table.insert(q, "sha256=" .. ref.sha256)
|
||||
end
|
||||
return table.concat(q, "&")
|
||||
end
|
||||
|
||||
local function book_ref_body(ref)
|
||||
local authors = ref.authors or {}
|
||||
json_util.InitArray(authors)
|
||||
return {
|
||||
uuid = ref.uuid,
|
||||
sha256 = ref.sha256,
|
||||
title = ref.title,
|
||||
authors = authors,
|
||||
}
|
||||
end
|
||||
|
||||
-- Server rating is 1-10; koreader's summary.rating is 0-5 (0 = unrated).
|
||||
function BookhoardAPI:getRating(ref)
|
||||
return self:_request("GET", "/api/sync/koreader/rating?" .. book_ref_query(ref), nil, PROGRESS_TIMEOUTS)
|
||||
end
|
||||
|
||||
function BookhoardAPI:setRating(ref, rating)
|
||||
local body = book_ref_body(ref)
|
||||
body.rating = rating
|
||||
return self:_request("POST", "/api/sync/koreader/rating", body, PROGRESS_TIMEOUTS)
|
||||
end
|
||||
|
||||
-- Review bodies are RAW MARKDOWN both ways (koreader's review box shows it
|
||||
-- literally — the same accepted tradeoff as notes).
|
||||
function BookhoardAPI:getReview(ref)
|
||||
return self:_request("GET", "/api/sync/koreader/review?" .. book_ref_query(ref), nil, PROGRESS_TIMEOUTS)
|
||||
end
|
||||
|
||||
function BookhoardAPI:setReview(ref, review)
|
||||
local body = book_ref_body(ref)
|
||||
body.review = review
|
||||
return self:_request("POST", "/api/sync/koreader/review", body, PROGRESS_TIMEOUTS)
|
||||
end
|
||||
|
||||
-- Cumulative total reading seconds for the book; the server converts
|
||||
-- watermark-deltas into reading_history sessions (device-attributed).
|
||||
function BookhoardAPI:pushStats(ref, total_seconds)
|
||||
local body = book_ref_body(ref)
|
||||
body.total_seconds = math.floor(total_seconds)
|
||||
return self:_request("POST", "/api/sync/koreader/stats", body, PROGRESS_TIMEOUTS)
|
||||
end
|
||||
|
||||
|
||||
|
||||
return BookhoardAPI
|
||||
Reference in new issue
Block a user