Files
bookhoard.koplugin/main.lua
T
john-okeefe 89bdcc50f3 Auto-add OPDS catalog to KOReader after device registration
Writes directly to KOReader's opds.lua settings so the user
doesn't have to type the long URL. Also updates the manual
menu item to auto-configure instead of showing the URL.
2026-05-29 22:42:26 -04:00

1008 lines
33 KiB
Lua

local ConfirmBox = require("ui/widget/confirmbox")
local DataStorage = require("datastorage")
local Device = require("device")
local Event = require("ui/event")
local InfoMessage = require("ui/widget/infomessage")
local InputDialog = require("ui/widget/inputdialog")
local LuaSettings = require("luasettings")
local Math = require("optmath")
local NetworkMgr = require("ui/network/manager")
local SpinWidget = require("ui/widget/spinwidget")
local UIManager = require("ui/uimanager")
local WidgetContainer = require("ui/widget/container/widgetcontainer")
local json = require("json")
local logger = require("logger")
local sha2 = require("ffi/sha2")
local time = require("ui/time")
local util = require("util")
local T = require("ffi/util").template
local _ = require("gettext")
local BookhoardAPI = require("BookhoardAPI")
local SYNC_STRATEGY = {
PROMPT = 1,
SILENT = 2,
DISABLE = 3,
}
local SYNC_MODE = {
IMMEDIATE = "immediate",
CHECKPOINT = "checkpoint",
}
local API_CALL_DEBOUNCE_DELAY = time.s(25)
local PERIODIC_PUSH_DELAY = 10
local sha256hex = sha2.sha256hex or sha2.sha256
local Bookhoard = WidgetContainer:extend({
name = "bookhoard",
is_doc_only = false,
title = _("Bookhoard Server"),
settings_key = "bookhoard",
push_timestamp = nil,
pull_timestamp = nil,
page_update_counter = nil,
last_page = nil,
periodic_push_task = nil,
periodic_push_scheduled = nil,
registration_poll_scheduled = nil,
settings = nil,
})
Bookhoard.default_settings = {
server_url = nil,
auth_token = nil,
device_id = nil,
auto_sync = false,
pages_before_update = 50,
sync_forward = SYNC_STRATEGY.PROMPT,
sync_backward = SYNC_STRATEGY.DISABLE,
sync_progress = true,
sync_bookmarks = true,
sync_highlights = true,
sync_notes = true,
sync_mode = SYNC_MODE.IMMEDIATE,
sync_endpoints = nil,
}
function Bookhoard:init()
self.push_timestamp = 0
self.pull_timestamp = 0
self.page_update_counter = 0
self.last_page = -1
self.periodic_push_scheduled = false
self.registration_poll_scheduled = false
self.periodic_push_task = function()
self.periodic_push_scheduled = false
self.page_update_counter = 0
self:updateProgress(false, false)
end
self.settings = G_reader_settings:readSetting(self.settings_key, self.default_settings)
if self.settings.auto_sync
and Device:hasSeamlessWifiToggle()
and G_reader_settings:readSetting("wifi_enable_action") ~= "turn_on" then
self.settings.auto_sync = false
logger.warn("Bookhoard: auto-sync disabled because wifi_enable_action is not turn_on")
end
self.ui.menu:registerToMainMenu(self)
end
function Bookhoard:onReaderReady()
if self.settings.auto_sync then
UIManager:nextTick(function()
self:getProgress(true, false)
end)
end
self:registerEvents()
self.last_page = self.ui:getCurrentPage()
end
function Bookhoard:registerEvents()
if self.settings.auto_sync then
self.onCloseDocument = self._onCloseDocument
self.onPageUpdate = self._onPageUpdate
self.onResume = self._onResume
self.onSuspend = self._onSuspend
self.onNetworkConnected = self._onNetworkConnected
self.onNetworkDisconnecting = self._onNetworkDisconnecting
else
self.onCloseDocument = nil
self.onPageUpdate = nil
self.onResume = nil
self.onSuspend = nil
self.onNetworkConnected = nil
self.onNetworkDisconnecting = nil
end
end
function Bookhoard:getAPI()
return BookhoardAPI:new({
server_url = self.settings.server_url,
auth_token = self.settings.auth_token,
})
end
function Bookhoard:isConfigured()
return self.settings.server_url and self.settings.auth_token
end
function Bookhoard:getSyncPeriod()
if not self.settings.auto_sync then
return _("Not available")
end
local period = self.settings.pages_before_update
if period and period > 0 then
return period
end
return _("Never")
end
function Bookhoard:addToMainMenu(menu_items)
menu_items.bookhoard_sync = {
text = _("Bookhoard sync"),
sorting_hint = "tools",
sub_item_table = self:buildMainMenu(),
}
end
function Bookhoard:buildMainMenu()
local items = {}
table.insert(items, {
text = _("Server URL"),
keep_menu_open = true,
tap_input_func = function()
return {
title = _("Bookhoard server URL"),
input = self.settings.server_url or "http://",
callback = function(input)
self.settings.server_url = input ~= "" and input or nil
G_reader_settings:saveSetting(self.settings_key, self.settings)
end,
}
end,
})
if self:isConfigured() then
table.insert(items, {
text = _("Device info"),
keep_menu_open = true,
callback = function()
UIManager:show(InfoMessage:new{
text = T(_("Device ID: %1\nServer: %2"),
self.settings.device_id or _("unknown"),
self.settings.server_url),
})
end,
})
table.insert(items, {
text = _("Disconnect"),
keep_menu_open = true,
callback = function()
UIManager:show(ConfirmBox:new{
text = _("Disconnect from Bookhoard server?"),
ok_text = _("Disconnect"),
ok_callback = function()
self.settings.auth_token = nil
self.settings.device_id = nil
self.settings.sync_endpoints = nil
self.settings.auto_sync = false
G_reader_settings:saveSetting(self.settings_key, self.settings)
self:registerEvents()
UIManager:askForRestart()
end,
})
end,
separator = true,
})
else
table.insert(items, {
text = _("Register device"),
keep_menu_open = true,
callback = function()
self:startRegistration()
end,
separator = true,
})
end
table.insert(items, {
text = _("Automatically keep documents in sync"),
checked_func = function() return self.settings.auto_sync end,
help_text = _([[This may lead to prompts about toggling WiFi on document close and suspend/resume, depending on your device's connectivity.]]),
callback = function()
self:toggleAutoSync()
end,
})
table.insert(items, {
text_func = function()
return T(_("Periodically sync every # pages (%1)"), self:getSyncPeriod())
end,
enabled_func = function() return self.settings.auto_sync end,
keep_menu_open = true,
callback = function(touchmenu_instance)
local spin = SpinWidget:new{
text = _([[Number of page turns between progress updates. Set to 0 to disable.]]),
value = self.settings.pages_before_update or 0,
value_min = 0,
value_max = 999,
value_step = 1,
value_hold_step = 10,
ok_text = _("Set"),
title_text = _("Pages before update"),
default_value = 50,
callback = function(spin)
self.settings.pages_before_update = spin.value > 0 and spin.value or nil
G_reader_settings:saveSetting(self.settings_key, self.settings)
if touchmenu_instance then touchmenu_instance:updateItems() end
end,
}
UIManager:show(spin)
end,
})
table.insert(items, {
text_func = function()
return T(_("Sync mode (%1)"),
self.settings.sync_mode == SYNC_MODE.IMMEDIATE and _("immediate") or _("checkpoint"))
end,
sub_item_table = {
{
text = _("Immediate"),
checked_func = function()
return self.settings.sync_mode == SYNC_MODE.IMMEDIATE
end,
callback = function()
self.settings.sync_mode = SYNC_MODE.IMMEDIATE
G_reader_settings:saveSetting(self.settings_key, self.settings)
end,
},
{
text = _("Checkpoint"),
checked_func = function()
return self.settings.sync_mode == SYNC_MODE.CHECKPOINT
end,
callback = function()
self.settings.sync_mode = SYNC_MODE.CHECKPOINT
G_reader_settings:saveSetting(self.settings_key, self.settings)
end,
},
},
separator = true,
})
table.insert(items, {
text = _("Sync behavior"),
sub_item_table = {
{
text_func = function()
return T(_("Sync to a newer state (%1)"),
self:getStrategyName(self.settings.sync_forward))
end,
sub_item_table = {
{
text = _("Silently"),
checked_func = function()
return self.settings.sync_forward == SYNC_STRATEGY.SILENT
end,
callback = function()
self.settings.sync_forward = SYNC_STRATEGY.SILENT
G_reader_settings:saveSetting(self.settings_key, self.settings)
end,
},
{
text = _("Prompt"),
checked_func = function()
return self.settings.sync_forward == SYNC_STRATEGY.PROMPT
end,
callback = function()
self.settings.sync_forward = SYNC_STRATEGY.PROMPT
G_reader_settings:saveSetting(self.settings_key, self.settings)
end,
},
{
text = _("Never"),
checked_func = function()
return self.settings.sync_forward == SYNC_STRATEGY.DISABLE
end,
callback = function()
self.settings.sync_forward = SYNC_STRATEGY.DISABLE
G_reader_settings:saveSetting(self.settings_key, self.settings)
end,
},
},
},
{
text_func = function()
return T(_("Sync to an older state (%1)"),
self:getStrategyName(self.settings.sync_backward))
end,
sub_item_table = {
{
text = _("Silently"),
checked_func = function()
return self.settings.sync_backward == SYNC_STRATEGY.SILENT
end,
callback = function()
self.settings.sync_backward = SYNC_STRATEGY.SILENT
G_reader_settings:saveSetting(self.settings_key, self.settings)
end,
},
{
text = _("Prompt"),
checked_func = function()
return self.settings.sync_backward == SYNC_STRATEGY.PROMPT
end,
callback = function()
self.settings.sync_backward = SYNC_STRATEGY.PROMPT
G_reader_settings:saveSetting(self.settings_key, self.settings)
end,
},
{
text = _("Never"),
checked_func = function()
return self.settings.sync_backward == SYNC_STRATEGY.DISABLE
end,
callback = function()
self.settings.sync_backward = SYNC_STRATEGY.DISABLE
G_reader_settings:saveSetting(self.settings_key, self.settings)
end,
},
},
},
},
separator = true,
})
table.insert(items, {
text = _("What to sync"),
sub_item_table = {
{
text = _("Reading progress"),
checked_func = function() return self.settings.sync_progress end,
callback = function()
self.settings.sync_progress = not self.settings.sync_progress
G_reader_settings:saveSetting(self.settings_key, self.settings)
end,
},
{
text = _("Bookmarks"),
checked_func = function() return self.settings.sync_bookmarks end,
callback = function()
self.settings.sync_bookmarks = not self.settings.sync_bookmarks
G_reader_settings:saveSetting(self.settings_key, self.settings)
end,
},
{
text = _("Highlights"),
checked_func = function() return self.settings.sync_highlights end,
callback = function()
self.settings.sync_highlights = not self.settings.sync_highlights
G_reader_settings:saveSetting(self.settings_key, self.settings)
end,
},
{
text = _("Notes"),
checked_func = function() return self.settings.sync_notes end,
callback = function()
self.settings.sync_notes = not self.settings.sync_notes
G_reader_settings:saveSetting(self.settings_key, self.settings)
end,
},
},
separator = true,
})
table.insert(items, {
text = _("Sync now"),
enabled_func = function() return self:isConfigured() end,
callback = function()
self:updateProgress(true, true)
self:getProgress(true, true)
end,
})
table.insert(items, {
text = _("Push progress from this device"),
enabled_func = function() return self:isConfigured() end,
callback = function()
self:updateProgress(true, true)
end,
})
table.insert(items, {
text = _("Pull progress from server"),
enabled_func = function() return self:isConfigured() end,
callback = function()
self:getProgress(true, true)
end,
separator = true,
})
table.insert(items, {
text = _("Setup OPDS catalog"),
keep_menu_open = true,
enabled_func = function() return self:isConfigured() end,
callback = function()
if self:setupOPDS() then
UIManager:show(InfoMessage:new{
text = _("Bookhoard OPDS catalog added! Find it in Home → OPDS Catalog."),
timeout = 3,
})
else
UIManager:show(InfoMessage:new{
text = _("Please configure and register your device first."),
timeout = 3,
})
end
end,
})
return items
end
function Bookhoard:getStrategyName(strategy)
if strategy == SYNC_STRATEGY.PROMPT then
return _("Prompt")
elseif strategy == SYNC_STRATEGY.SILENT then
return _("Auto")
else
return _("Disable")
end
end
function Bookhoard:toggleAutoSync()
if not self.settings.auto_sync
and Device:hasSeamlessWifiToggle()
and G_reader_settings:readSetting("wifi_enable_action") ~= "turn_on" then
UIManager:show(InfoMessage:new{
text = _("Set 'Action when Wi-Fi is off' to 'turn on' in Network settings to enable auto sync."),
})
return
end
self.settings.auto_sync = not self.settings.auto_sync
self:registerEvents()
G_reader_settings:saveSetting(self.settings_key, self.settings)
if self.settings.auto_sync and self.ui.doc_settings then
self:getProgress(true, true)
end
end
function Bookhoard:startRegistration()
if not self.settings.server_url or self.settings.server_url == "" then
UIManager:show(InfoMessage:new{
text = _("Please set your server URL first."),
timeout = 3,
})
return
end
if NetworkMgr:willRerunWhenOnline(function() self:startRegistration() end) then
return
end
local device_name = Device.model or "KOReader Device"
local device_identifier = Device:info() or device_name
UIManager:show(InfoMessage:new{
text = _("Registering device…"),
timeout = 1,
})
UIManager:scheduleIn(0.5, function()
local api = BookhoardAPI:new({ server_url = self.settings.server_url })
local ok, result = api:registerDevice(device_name, device_identifier)
if not ok then
UIManager:show(InfoMessage:new{
text = T(_("Registration failed: %1"),
result and result.error or _("unknown error")),
})
return
end
self.registration_id = result.registration_id
self.waiting_dialog = InfoMessage:new{
text = T(_("Device registered on server.\n\nOpen your Bookhoard web UI and go to:\n%1/devices\n\nApprove this device in the \"Pending Device Registrations\" section.\n\nWaiting for approval…"), self.settings.server_url),
}
UIManager:show(self.waiting_dialog)
self:startRegistrationPoll()
end)
end
function Bookhoard:startRegistrationPoll()
if self.registration_poll_scheduled then return end
self.registration_poll_scheduled = true
local function poll()
self.registration_poll_scheduled = false
if not self.registration_id then return end
local api = BookhoardAPI:new({ server_url = self.settings.server_url })
local ok, result = api:checkRegistrationStatus(self.registration_id)
if not ok then
if result and result.status == 410 then
self.registration_id = nil
UIManager:show(InfoMessage:new{
text = _("Registration expired. Please try again."),
})
return
end
self.registration_poll_scheduled = true
UIManager:scheduleIn(3, poll)
return
end
if result.status == "approved" then
self.registration_id = nil
self.settings.auth_token = result.auth_token
self.settings.device_id = result.device_id and tostring(result.device_id) or nil
self.settings.sync_endpoints = result.sync_endpoints
G_reader_settings:saveSetting(self.settings_key, self.settings)
self:registerEvents()
self:setupOPDS()
if self.waiting_dialog then
UIManager:close(self.waiting_dialog)
self.waiting_dialog = nil
end
UIManager:show(InfoMessage:new{
text = _("Device registered successfully!"),
timeout = 3,
})
elseif result.status == "pending" then
self.registration_poll_scheduled = true
UIManager:scheduleIn(3, poll)
else
self.registration_id = nil
UIManager:show(InfoMessage:new{
text = T(_("Registration %1"), result.status or _("failed")),
})
end
end
UIManager:scheduleIn(3, poll)
end
function Bookhoard:setupOPDS()
if not self.settings.server_url or not self.settings.device_id then
return false
end
local opds_url = self.settings.server_url
.. "/opds/devices/" .. self.settings.device_id .. "/catalog"
local opds_settings_file = DataStorage:getSettingsDir() .. "/opds.lua"
local opds_settings = LuaSettings:open(opds_settings_file)
local servers = opds_settings:readSetting("servers", {})
for _, server in ipairs(servers) do
if server.url == opds_url then
return true
end
end
table.insert(servers, {
title = "Bookhoard",
url = opds_url,
})
opds_settings:saveSetting("servers", servers)
opds_settings:flush()
return true
end
function Bookhoard:getLastPercent()
if self.ui.document.info.has_pages then
return Math.roundPercent(self.ui.paging:getLastPercent())
else
return Math.roundPercent(self.ui.rolling:getLastPercent())
end
end
function Bookhoard:getLastProgress()
if self.ui.document.info.has_pages then
return self.ui.paging:getLastProgress()
else
return self.ui.rolling:getLastProgress()
end
end
function Bookhoard:getFileSHA256()
local cached = self.ui.doc_settings:readSetting("bookhoard_sha256")
if cached then return cached end
local file = io.open(self.ui.document.file, "rb")
if not file then return nil end
local data = file:read("*a")
file:close()
local hash = sha256hex(data)
if hash then
self.ui.doc_settings:saveSetting("bookhoard_sha256", hash)
end
return hash
end
function Bookhoard:getBookhoardUUID()
if not self.ui.doc_settings then return nil end
return self.ui.doc_settings:readSetting("bookhoard_uuid")
end
function Bookhoard:collectBookData()
local props = self.ui.doc_props
local file_path = self.ui.document.file
local title = props.display_title or ""
local authors = ""
if props.authors then
authors = props.authors
end
local file_sha256 = self:getFileSHA256()
local book_uuid = self:getBookhoardUUID()
local percentage = self:getLastPercent()
local progress = self:getLastProgress()
local page = self.ui:getCurrentPage()
local total_pages = self.ui.document:getPageCount()
local chapter = ""
if self.ui.toc and self.ui.toc.getTocTitleOfCurrentPage then
chapter = self.ui.toc:getTocTitleOfCurrentPage() or ""
end
local book_data = {
uuid = book_uuid,
sha256 = file_sha256,
title = title,
authors = authors,
percentage = percentage,
chapter = chapter,
epubcfi = progress,
page = page,
total_pages = total_pages,
file_path = file_path,
device_info = {
koreader_version = require("version"):getCurrentRevision(),
device_model = Device.model,
},
}
return book_data
end
function Bookhoard:collectAnnotations()
local bookmarks = {}
local highlights = {}
local notes = {}
if not self.ui.bookmark then
return bookmarks, highlights, notes
end
local all_bookmarks = self.ui.bookmark.bookmarks
if not all_bookmarks then
return bookmarks, highlights, notes
end
local file_sha256 = self:getFileSHA256()
local total_pages = self.ui.document:getPageCount()
for _, bm in ipairs(all_bookmarks) do
local page_num = bm.page
if type(page_num) == "string" and self.ui.document.info.has_pages == false then
page_num = self.ui.document:getPageFromXPointer(page_num)
end
page_num = tonumber(page_num) or 0
local percentage = total_pages > 0 and (page_num / total_pages) or 0
local chapter = bm.chapter or ""
local entry = {
chapter = chapter,
datetime = bm.datetime or "",
notes = bm.notes or "",
pos0 = bm.pos0 or "",
pos1 = bm.pos1 or "",
page = tostring(bm.page or ""),
text = bm.text or "",
percentage = Math.roundPercent(percentage),
book_sha256 = file_sha256,
}
local has_text = bm.text and bm.text ~= ""
local has_notes = bm.notes and bm.notes ~= ""
if has_text and has_notes then
entry.type = "note"
table.insert(notes, entry)
elseif has_text then
entry.type = "highlight"
if bm.color then
entry.color = bm.color
end
table.insert(highlights, entry)
else
entry.type = "bookmark"
table.insert(bookmarks, entry)
end
end
return bookmarks, highlights, notes
end
function Bookhoard:syncToProgress(progress, percentage)
logger.dbg("Bookhoard: sync to progress", progress, percentage)
if self.ui.document.info.has_pages then
self.ui:handleEvent(Event:new("GotoPage", tonumber(progress)))
else
self.ui:handleEvent(Event:new("GotoXPointer", progress))
end
end
function Bookhoard:updateProgress(ensure_networking, interactive)
if not self:isConfigured() then
if interactive then
UIManager:show(InfoMessage:new{
text = _("Please configure and register your device first."),
timeout = 3,
})
end
return
end
if not self.settings.sync_progress then return end
local now = UIManager:getElapsedTimeSinceBoot()
if not interactive and now - self.push_timestamp <= API_CALL_DEBOUNCE_DELAY then
return
end
if ensure_networking
and NetworkMgr:willRerunWhenOnline(function() self:updateProgress(ensure_networking, interactive) end) then
return
end
UIManager:scheduleIn(0.5, function()
self:_doUpdateProgress(interactive)
end)
self.push_timestamp = now
end
function Bookhoard:_doUpdateProgress(interactive)
local book_data = self:collectBookData()
if not book_data then return end
if self.settings.sync_bookmarks or self.settings.sync_highlights or self.settings.sync_notes then
local bm, hl, nt = self:collectAnnotations()
book_data.bookmarks = self.settings.sync_bookmarks and bm or {}
book_data.highlights = self.settings.sync_highlights and hl or {}
book_data.notes = self.settings.sync_notes and nt or {}
else
book_data.bookmarks = {}
book_data.highlights = {}
book_data.notes = {}
end
local api = self:getAPI()
local ok, result = api:syncProgress(book_data, self.settings.sync_mode)
UIManager:nextTick(function()
if ok then
logger.dbg("Bookhoard: progress pushed successfully")
if result and result.device_updated and result.device_updated.uuid then
self.ui.doc_settings:saveSetting("bookhoard_uuid", result.device_updated.uuid)
self.ui.doc_settings:flush()
end
if interactive then
UIManager:show(InfoMessage:new{
text = _("Progress has been pushed."),
timeout = 3,
})
end
else
logger.warn("Bookhoard: failed to push progress")
if interactive then
UIManager:show(InfoMessage:new{
text = _("Failed to push progress. Check your network connection."),
timeout = 3,
})
end
end
end)
end
function Bookhoard:getProgress(ensure_networking, interactive)
if not self:isConfigured() then
if interactive then
UIManager:show(InfoMessage:new{
text = _("Please configure and register your device first."),
timeout = 3,
})
end
return
end
if not self.settings.sync_progress then 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
return
end
local book_uuid = self:getBookhoardUUID()
if not book_uuid then
if interactive then
UIManager:show(InfoMessage:new{
text = _("No Bookhoard UUID for this document. Push progress first."),
timeout = 3,
})
end
return
end
UIManager:scheduleIn(0.5, function()
self:_doGetProgress(interactive)
end)
self.pull_timestamp = now
end
function Bookhoard:_doGetProgress(interactive)
local book_uuid = self:getBookhoardUUID()
if not book_uuid then return end
local api = self:getAPI()
local ok, result = api:getMetadata(book_uuid)
UIManager:nextTick(function()
if not ok or not result then
if interactive then
UIManager:show(InfoMessage:new{
text = _("Failed to pull progress."),
timeout = 3,
})
end
return
end
if not result.progress then
if interactive then
UIManager:show(InfoMessage:new{
text = _("No progress found for this document."),
timeout = 3,
})
end
return
end
local progress = result.progress
local percentage = self:getLastPercent()
local server_percentage = progress.percentage or 0
if percentage == server_percentage then
if interactive then
UIManager:show(InfoMessage:new{
text = _("Progress is already synchronized."),
timeout = 3,
})
end
return
end
local self_older = server_percentage > percentage
if self_older then
if self.settings.sync_forward == SYNC_STRATEGY.SILENT then
self:syncToProgress(progress.epubcfi or progress.page, server_percentage)
self:_showSyncedMessage()
elseif self.settings.sync_forward == SYNC_STRATEGY.PROMPT then
UIManager:show(ConfirmBox:new{
text = T(_("Sync to newer location %1%% from server?"),
Math.round(server_percentage * 100)),
ok_callback = function()
self:syncToProgress(progress.epubcfi or progress.page, server_percentage)
end,
})
end
else
if self.settings.sync_backward == SYNC_STRATEGY.SILENT then
self:syncToProgress(progress.epubcfi or progress.page, server_percentage)
self:_showSyncedMessage()
elseif self.settings.sync_backward == SYNC_STRATEGY.PROMPT then
UIManager:show(ConfirmBox:new{
text = T(_("Sync to previous location %1%% from server?"),
Math.round(server_percentage * 100)),
ok_callback = function()
self:syncToProgress(progress.epubcfi or progress.page, server_percentage)
end,
})
end
end
end)
end
function Bookhoard:_showSyncedMessage()
UIManager:show(InfoMessage:new{
text = _("Progress has been synchronized."),
timeout = 3,
})
end
function Bookhoard:_onCloseDocument()
self.onResume = nil
self.onSuspend = nil
NetworkMgr:goOnlineToRun(function()
self:updateProgress(false, false)
end)
end
function Bookhoard:schedulePeriodicPush()
UIManager:unschedule(self.periodic_push_task)
UIManager:scheduleIn(PERIODIC_PUSH_DELAY, self.periodic_push_task)
self.periodic_push_scheduled = true
end
function Bookhoard:_onPageUpdate(page)
if page == nil then return end
if self.last_page ~= page then
self.last_page = page
self.page_update_counter = self.page_update_counter + 1
if self.periodic_push_scheduled
or (self.settings.pages_before_update
and self.page_update_counter >= self.settings.pages_before_update) then
self:schedulePeriodicPush()
end
end
end
function Bookhoard:_onResume()
if Device:hasWifiRestore() and NetworkMgr.wifi_was_on
and G_reader_settings:isTrue("auto_restore_wifi") then
return
end
UIManager:scheduleIn(1, function()
self:getProgress(true, false)
end)
end
function Bookhoard:_onSuspend()
self:updateProgress(true, false)
end
function Bookhoard:_onNetworkConnected()
UIManager:scheduleIn(0.5, function()
self:getProgress(false, false)
end)
end
function Bookhoard:_onNetworkDisconnecting()
self:updateProgress(false, false)
end
function Bookhoard:onCloseWidget()
UIManager:unschedule(self.periodic_push_task)
self.periodic_push_task = nil
end
return Bookhoard