Compare commits
2
Commits
4ea3966e37
...
656505eda7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
656505eda7 | ||
|
|
9467bdb2bd |
@@ -126,6 +126,13 @@ function BookhoardAPI:syncProgress(book_data, sync_mode)
|
||||
}, PROGRESS_TIMEOUTS)
|
||||
end
|
||||
|
||||
-- Read-only SHA-256 → book UUID lookup. Used to link a freshly downloaded
|
||||
-- book WITHOUT pushing progress (a push would transmit the device's
|
||||
-- first-page position and clobber/conflict with real server progress).
|
||||
function BookhoardAPI:resolveBook(sha256)
|
||||
return self:_request("GET", "/api/sync/koreader/resolve?sha256=" .. sha256, nil, PROGRESS_TIMEOUTS)
|
||||
end
|
||||
|
||||
function BookhoardAPI:getMetadata(uuid)
|
||||
return self:_request("GET", "/api/sync/koreader/metadata/" .. uuid, nil, PROGRESS_TIMEOUTS)
|
||||
end
|
||||
|
||||
@@ -121,17 +121,13 @@ end
|
||||
function Bookhoard:onReaderReady()
|
||||
if self.settings.auto_sync then
|
||||
UIManager:nextTick(function()
|
||||
-- On the first open of a freshly downloaded book there is no cached
|
||||
-- bookhoard UUID yet (it is only learned from a successful push
|
||||
-- response). Pulling now would just fail with "Push progress first".
|
||||
-- Instead, push once to bootstrap identity: the server resolves the
|
||||
-- book by SHA-256 (format-aware) and returns the UUID, which we then
|
||||
-- cache. After that, push and pull both work without ordering.
|
||||
if not self:getBookhoardUUID() then
|
||||
self:updateProgress(true, false)
|
||||
else
|
||||
-- Pull-first on every open. A freshly downloaded book has no cached
|
||||
-- bookhoard UUID yet; getProgress resolves it by SHA-256 with a
|
||||
-- read-only lookup (no progress push). The previous push-to-
|
||||
-- bootstrap strategy transmitted the device's first-page position
|
||||
-- to the server on books already mid-read elsewhere, creating a
|
||||
-- sync conflict before the first pull could ever happen.
|
||||
self:getProgress(true, false)
|
||||
end
|
||||
end)
|
||||
end
|
||||
self:registerEvents()
|
||||
@@ -994,7 +990,15 @@ function Bookhoard:updateProgress(ensure_networking, interactive)
|
||||
return
|
||||
end
|
||||
|
||||
if not self.settings.sync_progress then return end
|
||||
if not self.settings.sync_progress then
|
||||
if interactive then
|
||||
UIManager:show(InfoMessage:new{
|
||||
text = _("Reading progress sync is disabled (see What to sync)."),
|
||||
timeout = 3,
|
||||
})
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if not self.ui.document then
|
||||
if interactive then
|
||||
@@ -1011,10 +1015,20 @@ function Bookhoard:updateProgress(ensure_networking, interactive)
|
||||
return
|
||||
end
|
||||
|
||||
if ensure_networking
|
||||
and NetworkMgr:willRerunWhenOnline(function() self:updateProgress(ensure_networking, interactive) end) then
|
||||
if ensure_networking then
|
||||
-- Same silent-drop case as getProgress: connected but no internet
|
||||
-- means willRerunWhenOnline neither reruns nor shows any UI.
|
||||
if interactive and NetworkMgr:isConnected() and not NetworkMgr:isOnline() then
|
||||
UIManager:show(InfoMessage:new{
|
||||
text = _("No internet connection. Progress was not pushed."),
|
||||
timeout = 3,
|
||||
})
|
||||
return
|
||||
end
|
||||
if NetworkMgr:willRerunWhenOnline(function() self:updateProgress(ensure_networking, interactive) end) then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
UIManager:scheduleIn(0.5, function()
|
||||
self:_doUpdateProgress(interactive)
|
||||
@@ -1082,30 +1096,53 @@ function Bookhoard:getProgress(ensure_networking, interactive)
|
||||
return
|
||||
end
|
||||
|
||||
if not self.settings.sync_progress then return end
|
||||
if not self.settings.sync_progress then
|
||||
if interactive then
|
||||
UIManager:show(InfoMessage:new{
|
||||
text = _("Reading progress sync is disabled (see What to sync)."),
|
||||
timeout = 3,
|
||||
})
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local now = UIManager:getElapsedTimeSinceBoot()
|
||||
if not interactive and now - self.pull_timestamp <= API_CALL_DEBOUNCE_DELAY then
|
||||
return
|
||||
end
|
||||
|
||||
if ensure_networking
|
||||
and NetworkMgr:willRerunWhenOnline(function() self:getProgress(ensure_networking, interactive) end) then
|
||||
if ensure_networking then
|
||||
-- willRerunWhenOnline silently drops the action (no rerun, no UI)
|
||||
-- when the device is connected at the IP level but has no internet
|
||||
-- access: it only guarantees isConnected, and in that state the
|
||||
-- framework re-connects without ever invoking the callback. Catch
|
||||
-- that state up front so an interactive pull never no-ops silently.
|
||||
if interactive and NetworkMgr:isConnected() and not NetworkMgr:isOnline() then
|
||||
UIManager:show(InfoMessage:new{
|
||||
text = _("No internet connection. Progress was not pulled."),
|
||||
timeout = 3,
|
||||
})
|
||||
return
|
||||
end
|
||||
if NetworkMgr:willRerunWhenOnline(function() self:getProgress(ensure_networking, interactive) end) then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local book_uuid = self:getBookhoardUUID()
|
||||
if not book_uuid then
|
||||
-- No cached UUID yet (first open of a freshly downloaded book). Bootstrap
|
||||
-- it by pushing once: the server resolves the book by SHA-256 and returns
|
||||
-- the UUID, which the push handler caches. Then retry the pull.
|
||||
-- No cached UUID yet (first open of a freshly downloaded book). Link
|
||||
-- the book with a read-only SHA-256 lookup, then pull. Progress is
|
||||
-- never pushed here: the device sits on the first page of a book the
|
||||
-- server may hold mid-read, and pushing that would overwrite/server-
|
||||
-- conflict the real progress before the pull could deliver it.
|
||||
if interactive then
|
||||
UIManager:show(InfoMessage:new{
|
||||
text = _("Linking this book to Bookhoard first…"),
|
||||
timeout = 3,
|
||||
})
|
||||
end
|
||||
self:_bootstrapUUIDThenPull(interactive)
|
||||
self:_linkBookThenPull(interactive)
|
||||
return
|
||||
end
|
||||
|
||||
@@ -1116,9 +1153,70 @@ function Bookhoard:getProgress(ensure_networking, interactive)
|
||||
self.pull_timestamp = now
|
||||
end
|
||||
|
||||
-- Establish the bookhoard UUID for the current document by pushing once, then
|
||||
-- (once cached) perform the originally-requested pull. Used when a pull is
|
||||
-- requested before any push has run on a newly downloaded book.
|
||||
-- Link the current document to Bookhoard WITHOUT pushing progress, then run
|
||||
-- the pull. The UUID is learned via a read-only SHA-256 resolve; only when
|
||||
-- the server does not know the hash at all (sideloaded book) do we fall back
|
||||
-- to the legacy push-bootstrap, and only for background (non-interactive)
|
||||
-- flows — a user-initiated pull must never push this device's position.
|
||||
function Bookhoard:_linkBookThenPull(interactive)
|
||||
local file_sha256 = self:getFileSHA256()
|
||||
if not file_sha256 then
|
||||
if interactive then
|
||||
UIManager:show(InfoMessage:new{
|
||||
text = _("Could not read this file to link it with Bookhoard."),
|
||||
timeout = 3,
|
||||
})
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
UIManager:scheduleIn(0.5, function()
|
||||
local api = self:getAPI()
|
||||
local ok, result = api:resolveBook(file_sha256)
|
||||
|
||||
UIManager:nextTick(function()
|
||||
if not self.ui.doc_settings then return end
|
||||
|
||||
if ok and type(result) == "table" and result.book_uuid then
|
||||
self.ui.doc_settings:saveSetting("bookhoard_uuid", result.book_uuid)
|
||||
self.ui.doc_settings:flush()
|
||||
logger.dbg("Bookhoard: linked book by SHA-256 to", result.book_uuid)
|
||||
self:_doGetProgress(interactive)
|
||||
return
|
||||
end
|
||||
|
||||
if result and result.status == 404 then
|
||||
-- Genuinely unknown to the server (sideloaded book). The
|
||||
-- legacy push-bootstrap only ever linked such books via fuzzy
|
||||
-- title/author matching; keep that as a background attempt,
|
||||
-- but tell interactive users to push explicitly.
|
||||
if interactive then
|
||||
UIManager:show(InfoMessage:new{
|
||||
text = _("This book is not in your Bookhoard library yet. Push progress to link it."),
|
||||
timeout = 4,
|
||||
})
|
||||
else
|
||||
self:_bootstrapUUIDThenPull(interactive)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
logger.warn("Bookhoard: failed to resolve book by SHA-256")
|
||||
if interactive then
|
||||
UIManager:show(InfoMessage:new{
|
||||
text = _("Could not reach Bookhoard. Check your network connection and try again."),
|
||||
timeout = 4,
|
||||
})
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
-- Legacy fallback: establish the bookhoard UUID for the current document by
|
||||
-- pushing once, then (once cached) perform the originally-requested pull.
|
||||
-- Only reached from background flows when the SHA-256 resolve reported the
|
||||
-- book as unknown to the server; the push mainly serves fuzzy title/author
|
||||
-- linking and device alias registration for sideloaded books.
|
||||
function Bookhoard:_bootstrapUUIDThenPull(interactive)
|
||||
self:updateProgress(false, false)
|
||||
UIManager:scheduleIn(2, function()
|
||||
@@ -1144,7 +1242,7 @@ function Bookhoard:_doGetProgress(interactive)
|
||||
if not ok or not result then
|
||||
if interactive then
|
||||
UIManager:show(InfoMessage:new{
|
||||
text = _("Failed to pull progress."),
|
||||
text = _("Failed to pull progress. Check your network connection."),
|
||||
timeout = 3,
|
||||
})
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user