Basic fixes to calibre-sync (#3558)

* Properly create intermediate directories when receiving books from Calibre.

This fixes an issue where you can't receive books except into directories that
already exist on the Kobo, which, in particular, causes problems when your
configuration in Calibre is something like "put books in $Author/$Title.epub"
and you haven't previously synced any books by that author.

* Wake up periodically to process ZMQs if any are registered.

This fixes an issue where if there are any timed events (such as the suspend
timer) in the queue, ZMQ events may not get processed until the timed event
fires, which is a problem when (for example) the suspend timer goes off in
an hour and you have something trying to send a book to the kobo over wifi
*right now*.

With this change, the event loop will wake up every 50ms to check for ZMQ
events and process them if necessary. If there are no ZMQs registered (which
is typical), it uses the original behaviour -- so this won't affect battery
life under normal usage.
pull/3572/head
Ben Kelly 6 years ago committed by Frans de Jonge
parent 79517eb166
commit f9ac8b138b

@ -21,6 +21,9 @@ local UIManager = {
G_reader_settings:readSetting("full_refresh_count") or DRCOUNTMAX, G_reader_settings:readSetting("full_refresh_count") or DRCOUNTMAX,
refresh_count = 0, refresh_count = 0,
-- How long to wait between ZMQ wakeups: 50ms.
ZMQ_TIMEOUT = 50 * 1000,
event_handlers = nil, event_handlers = nil,
_running = true, _running = true,
@ -660,6 +663,27 @@ function UIManager:resetInputTimeout()
self.INPUT_TIMEOUT = nil self.INPUT_TIMEOUT = nil
end end
function UIManager:handleInputEvent(input_event)
if input_event.handler ~= "onInputError" then
self.event_hook:execute("InputEvent", input_event)
end
local handler = self.event_handlers[input_event]
if handler then
handler(input_event)
else
self.event_handlers["__default__"](input_event)
end
end
-- Process all pending events on all registered ZMQs.
function UIManager:processZMQs()
for _, zeromq in ipairs(self._zeromqs) do
for input_event in zeromq.waitEvent,zeromq do
self:handleInputEvent(input_event)
end
end
end
function UIManager:handleInput() function UIManager:handleInput()
local wait_until, now local wait_until, now
-- run this in a loop, so that paints can trigger events -- run this in a loop, so that paints can trigger events
@ -683,42 +707,32 @@ function UIManager:handleInput()
self:_repaint() self:_repaint()
until not self._task_queue_dirty until not self._task_queue_dirty
-- wait for next event -- run ZMQs if any
-- note that we will skip that if we have tasks that are ready to run self:processZMQs()
local input_event = nil
if not wait_until then -- Figure out how long to wait.
if #self._zeromqs > 0 then -- Default to INPUT_TIMEOUT (which may be nil, i.e. block until an event happens).
-- pending message queue, wait 100ms for input local wait_us = self.INPUT_TIMEOUT
input_event = Input:waitEvent(1000*100)
if not input_event or input_event.handler == "onInputError" then -- If there's a timed event pending, that puts an upper bound on how long to wait.
for _, zeromq in ipairs(self._zeromqs) do if wait_until then
input_event = zeromq:waitEvent() wait_us = math.min(
if input_event then break end wait_us or math.huge,
end (wait_until[1] - now[1]) * MILLION
end + (wait_until[2] - now[2]))
else end
-- no pending task, wait without timeout
input_event = Input:waitEvent(self.INPUT_TIMEOUT) -- If we have any ZMQs registered, ZMQ_TIMEOUT is another upper bound.
end if #self._zeromqs > 0 then
elseif wait_until[1] > now[1] wait_us = math.min(wait_us or math.huge, self.ZMQ_TIMEOUT)
or wait_until[1] == now[1] and wait_until[2] > now[2] then
-- wait until next task is pending
local wait_us = (wait_until[1] - now[1]) * MILLION
+ (wait_until[2] - now[2])
input_event = Input:waitEvent(wait_us)
end end
-- wait for next event
local input_event = Input:waitEvent(wait_us)
-- delegate input_event to handler -- delegate input_event to handler
if input_event then if input_event then
if input_event.handler ~= "onInputError" then self:handleInputEvent(input_event)
self.event_hook:execute("InputEvent", input_event)
end
local handler = self.event_handlers[input_event]
if handler then
handler(input_event)
else
self.event_handlers["__default__"](input_event)
end
end end
if self.looper then if self.looper then

@ -304,6 +304,32 @@ function util.isEmptyDir(path)
return true return true
end end
--- Checks if the given path exists. Doesn't care if it's a file or directory.
---- @string path
---- @treturn bool
function util.pathExists(path)
local lfs = require("libs/libkoreader-lfs")
return lfs.attributes(path, "mode") ~= nil
end
--- As `mkdir -p`.
--- Unlike lfs.mkdir(), does not error if the directory already exists, and
--- creates intermediate directories as needed.
---- @string path the directory to create
---- @treturn bool true on success; nil, err_message on error
function util.makePath(path)
path = path:gsub("/+$", "")
if util.pathExists(path) then return true end
local success, err = util.makePath((util.splitFilePathName(path)))
if not success then
return nil, err.." (creating "..path..")"
end
local lfs = require("libs/libkoreader-lfs")
return lfs.mkdir(path)
end
--- Replaces characters that are invalid filenames. --- Replaces characters that are invalid filenames.
-- --
-- Replaces the characters <code>\/:*?"<>|</code> with an <code>_</code>. -- Replaces the characters <code>\/:*?"<>|</code> with an <code>_</code>.
@ -326,7 +352,9 @@ function util.replaceSlashChar(str)
end end
end end
--- Splits a file into its path and name --- Splits a file into its directory path and file name.
--- If the given path has a trailing /, returns the entire path as the directory
--- path and "" as the file name.
---- @string file ---- @string file
---- @treturn string path, filename ---- @treturn string path, filename
function util.splitFilePathName(file) function util.splitFilePathName(file)

@ -5,6 +5,7 @@ local JSON = require("json")
local DEBUG = require("dbg") local DEBUG = require("dbg")
local _ = require("gettext") local _ = require("gettext")
local NetworkMgr = require("ui/network/manager") local NetworkMgr = require("ui/network/manager")
local util = require("frontend/util")
require("ffi/zeromq_h") require("ffi/zeromq_h")
@ -320,6 +321,7 @@ function CalibreCompanion:sendBook(arg)
local inbox_dir = G_reader_settings:readSetting("inbox_dir") local inbox_dir = G_reader_settings:readSetting("inbox_dir")
local filename = inbox_dir .. "/" .. arg.lpath local filename = inbox_dir .. "/" .. arg.lpath
DEBUG("write to file", filename) DEBUG("write to file", filename)
util.makePath((util.splitFilePathName(filename)))
local outfile = io.open(filename, "wb") local outfile = io.open(filename, "wb")
local to_write_bytes = arg.length local to_write_bytes = arg.length
local calibre_device = self local calibre_device = self

Loading…
Cancel
Save