|
|
|
@ -2,10 +2,11 @@ local InputContainer = require("ui/widget/container/inputcontainer")
|
|
|
|
|
local InfoMessage = require("ui/widget/infomessage")
|
|
|
|
|
local UIManager = require("ui/uimanager")
|
|
|
|
|
local JSON = require("json")
|
|
|
|
|
local DEBUG = require("dbg")
|
|
|
|
|
local _ = require("gettext")
|
|
|
|
|
local NetworkMgr = require("ui/network/manager")
|
|
|
|
|
local logger = require("logger")
|
|
|
|
|
local util = require("frontend/util")
|
|
|
|
|
local T = require("ffi/util").template
|
|
|
|
|
|
|
|
|
|
require("ffi/zeromq_h")
|
|
|
|
|
|
|
|
|
@ -80,6 +81,19 @@ function CalibreCompanion:find_calibre_server()
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function CalibreCompanion:checkCalibreServer(host, port)
|
|
|
|
|
local socket = require("socket")
|
|
|
|
|
local tcp = socket.tcp()
|
|
|
|
|
tcp:settimeout(5)
|
|
|
|
|
local client = tcp:connect(host, port)
|
|
|
|
|
-- In case of error, the method returns nil followed by a string describing the error. In case of success, the method returns 1.
|
|
|
|
|
if client then
|
|
|
|
|
tcp:close()
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function CalibreCompanion:addToMainMenu(menu_items)
|
|
|
|
|
menu_items.calibre_wireless_connection = {
|
|
|
|
|
text = _("calibre wireless connection"),
|
|
|
|
@ -105,6 +119,86 @@ function CalibreCompanion:addToMainMenu(menu_items)
|
|
|
|
|
callback = function()
|
|
|
|
|
CalibreCompanion:setInboxDir()
|
|
|
|
|
end
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
text_func = function()
|
|
|
|
|
local address = "automatic"
|
|
|
|
|
if G_reader_settings:has("calibre_wireless_url") then
|
|
|
|
|
address = G_reader_settings:readSetting("calibre_wireless_url")
|
|
|
|
|
address = string.format("%s:%s", address["address"], address["port"])
|
|
|
|
|
end
|
|
|
|
|
return T(_("Server address (%1)"), address)
|
|
|
|
|
end,
|
|
|
|
|
sub_item_table = {
|
|
|
|
|
{
|
|
|
|
|
text = _("Automatic"),
|
|
|
|
|
checked_func = function()
|
|
|
|
|
return G_reader_settings:hasNot("calibre_wireless_url")
|
|
|
|
|
end,
|
|
|
|
|
callback = function()
|
|
|
|
|
G_reader_settings:delSetting("calibre_wireless_url")
|
|
|
|
|
end,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
text = _("Manual"),
|
|
|
|
|
checked_func = function()
|
|
|
|
|
return G_reader_settings:has("calibre_wireless_url")
|
|
|
|
|
end,
|
|
|
|
|
callback = function(touchmenu_instance)
|
|
|
|
|
local MultiInputDialog = require("ui/widget/multiinputdialog")
|
|
|
|
|
local url_dialog
|
|
|
|
|
local calibre_url = G_reader_settings:readSetting("calibre_wireless_url")
|
|
|
|
|
local calibre_url_address, calibre_url_port
|
|
|
|
|
if calibre_url then
|
|
|
|
|
calibre_url_address = calibre_url["address"]
|
|
|
|
|
calibre_url_port = calibre_url["port"]
|
|
|
|
|
end
|
|
|
|
|
url_dialog = MultiInputDialog:new{
|
|
|
|
|
title = _("Set custom calibre address"),
|
|
|
|
|
fields = {
|
|
|
|
|
{
|
|
|
|
|
text = calibre_url_address,
|
|
|
|
|
input_type = "string",
|
|
|
|
|
hint = _("IP Address"),
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
text = calibre_url_port,
|
|
|
|
|
input_type = "number",
|
|
|
|
|
hint = _("Port"),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
buttons = {
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
text = _("Cancel"),
|
|
|
|
|
callback = function()
|
|
|
|
|
UIManager:close(url_dialog)
|
|
|
|
|
end,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
text = _("OK"),
|
|
|
|
|
callback = function()
|
|
|
|
|
local fields = url_dialog:getFields()
|
|
|
|
|
if fields[1] ~= "" then
|
|
|
|
|
local port = tonumber(fields[2])
|
|
|
|
|
if not port or port < 1 or port > 65355 then
|
|
|
|
|
--default port
|
|
|
|
|
port = 9090
|
|
|
|
|
end
|
|
|
|
|
G_reader_settings:saveSetting("calibre_wireless_url", {address = fields[1], port = port })
|
|
|
|
|
end
|
|
|
|
|
UIManager:close(url_dialog)
|
|
|
|
|
if touchmenu_instance then touchmenu_instance:updateItems() end
|
|
|
|
|
end,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
UIManager:show(url_dialog)
|
|
|
|
|
url_dialog:onShowKeyboard()
|
|
|
|
|
end,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -118,16 +212,22 @@ function CalibreCompanion:initCalibreMQ(host, port)
|
|
|
|
|
port = port,
|
|
|
|
|
receiveCallback = function(data)
|
|
|
|
|
self:onReceiveJSON(data)
|
|
|
|
|
if not self.connect_message then
|
|
|
|
|
UIManager:show(InfoMessage:new{
|
|
|
|
|
text = T(_("Connected to calibre server at %1:%2"), host, port),
|
|
|
|
|
})
|
|
|
|
|
self.connect_message = true
|
|
|
|
|
if self.failed_connect_callback then
|
|
|
|
|
--don't disconnect if we connect in 10 seconds
|
|
|
|
|
UIManager:unschedule(self.failed_connect_callback)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end,
|
|
|
|
|
}
|
|
|
|
|
self.calibre_socket:start()
|
|
|
|
|
self.calibre_messagequeue = UIManager:insertZMQ(self.calibre_socket)
|
|
|
|
|
end
|
|
|
|
|
DEBUG("connected to calibre", host, port)
|
|
|
|
|
UIManager:show(InfoMessage:new{
|
|
|
|
|
text = _("Connected to calibre server at ") .. host .. ":" .. port,
|
|
|
|
|
timeout = 1,
|
|
|
|
|
})
|
|
|
|
|
logger.info("connected to calibre", host, port)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- will callback initCalibreMQ if inbox is confirmed to be set
|
|
|
|
@ -135,7 +235,7 @@ function CalibreCompanion:setInboxDir(host, port)
|
|
|
|
|
local calibre_device = self
|
|
|
|
|
require("ui/downloadmgr"):new{
|
|
|
|
|
onConfirm = function(inbox)
|
|
|
|
|
DEBUG("set inbox directory", inbox)
|
|
|
|
|
logger.info("set inbox directory", inbox)
|
|
|
|
|
G_reader_settings:saveSetting("inbox_dir", inbox)
|
|
|
|
|
if host and port then
|
|
|
|
|
calibre_device:initCalibreMQ(host, port)
|
|
|
|
@ -145,7 +245,26 @@ function CalibreCompanion:setInboxDir(host, port)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function CalibreCompanion:connect()
|
|
|
|
|
local host, port = self:find_calibre_server()
|
|
|
|
|
self.connect_message = false
|
|
|
|
|
local host, port
|
|
|
|
|
if G_reader_settings:hasNot("calibre_wireless_url") then
|
|
|
|
|
host, port = self:find_calibre_server()
|
|
|
|
|
else
|
|
|
|
|
local calibre_url = G_reader_settings:readSetting("calibre_wireless_url")
|
|
|
|
|
host, port = calibre_url["address"], calibre_url["port"]
|
|
|
|
|
if not self:checkCalibreServer(host, port) then
|
|
|
|
|
host = nil
|
|
|
|
|
else
|
|
|
|
|
self.failed_connect_callback = function()
|
|
|
|
|
UIManager:show(InfoMessage:new{
|
|
|
|
|
text = _("Cannot connect to calibre server."),
|
|
|
|
|
})
|
|
|
|
|
self:disconnect()
|
|
|
|
|
end
|
|
|
|
|
-- wait 10 seconds to connect to calibre
|
|
|
|
|
UIManager:scheduleIn(10, self.failed_connect_callback)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
if host and port then
|
|
|
|
|
local inbox_dir = G_reader_settings:readSetting("inbox_dir")
|
|
|
|
|
if inbox_dir then
|
|
|
|
@ -156,7 +275,7 @@ function CalibreCompanion:connect()
|
|
|
|
|
elseif not NetworkMgr:isConnected() then
|
|
|
|
|
NetworkMgr:promptWifiOn()
|
|
|
|
|
else
|
|
|
|
|
DEBUG("cannot connect to calibre server")
|
|
|
|
|
logger.info("cannot connect to calibre server")
|
|
|
|
|
UIManager:show(InfoMessage:new{
|
|
|
|
|
text = _("Cannot connect to calibre server."),
|
|
|
|
|
})
|
|
|
|
@ -165,7 +284,8 @@ function CalibreCompanion:connect()
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function CalibreCompanion:disconnect()
|
|
|
|
|
DEBUG("disconnect from calibre")
|
|
|
|
|
logger.info("disconnect from calibre")
|
|
|
|
|
self.connect_message = false
|
|
|
|
|
self.calibre_socket:stop()
|
|
|
|
|
UIManager:removeZMQ(self.calibre_messagequeue)
|
|
|
|
|
self.calibre_socket = nil
|
|
|
|
@ -174,30 +294,30 @@ end
|
|
|
|
|
|
|
|
|
|
function CalibreCompanion:onReceiveJSON(data)
|
|
|
|
|
self.buffer = (self.buffer or "") .. (data or "")
|
|
|
|
|
--DEBUG("data buffer", self.buffer)
|
|
|
|
|
--logger.info("data buffer", self.buffer)
|
|
|
|
|
-- messages from calibre stream socket are encoded in JSON strings like this
|
|
|
|
|
-- 34[0, {"key0":value, "key1": value}]
|
|
|
|
|
-- the JSON string has a leading length string field followed by the actual
|
|
|
|
|
-- JSON data in which the first element is always the operator code which can
|
|
|
|
|
-- be looked up in the opnames dictionary
|
|
|
|
|
while self.buffer ~= nil do
|
|
|
|
|
--DEBUG("buffer", self.buffer)
|
|
|
|
|
--logger.info("buffer", self.buffer)
|
|
|
|
|
local index = self.buffer:find('%[') or 1
|
|
|
|
|
local size = tonumber(self.buffer:sub(1, index - 1))
|
|
|
|
|
local json_data
|
|
|
|
|
if size and #self.buffer >= index - 1 + size then
|
|
|
|
|
json_data = self.buffer:sub(index, index - 1 + size)
|
|
|
|
|
--DEBUG("json_data", json_data)
|
|
|
|
|
--logger.info("json_data", json_data)
|
|
|
|
|
-- reset buffer to nil if all buffer is copied out to json data
|
|
|
|
|
self.buffer = self.buffer:sub(index + size)
|
|
|
|
|
--DEBUG("new buffer", self.buffer)
|
|
|
|
|
--logger.info("new buffer", self.buffer)
|
|
|
|
|
-- data is not complete which means there are still missing data not received
|
|
|
|
|
else
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
local ok, json = pcall(JSON.decode, json_data)
|
|
|
|
|
if ok and json then
|
|
|
|
|
DEBUG("received json table", json)
|
|
|
|
|
logger.dbg("received json table", json)
|
|
|
|
|
local opcode = json[1]
|
|
|
|
|
local arg = json[2]
|
|
|
|
|
if self.opnames[opcode] == 'GET_INITIALIZATION_INFO' then
|
|
|
|
@ -220,7 +340,7 @@ function CalibreCompanion:onReceiveJSON(data)
|
|
|
|
|
self:noop(arg)
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
DEBUG("failed to decode json data", json_data)
|
|
|
|
|
logger.dbg("failed to decode json data", json_data)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
@ -234,7 +354,7 @@ function CalibreCompanion:sendJsonData(opname, data)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function CalibreCompanion:getInitInfo(arg)
|
|
|
|
|
DEBUG("GET_INITIALIZATION_INFO", arg)
|
|
|
|
|
logger.dbg("GET_INITIALIZATION_INFO", arg)
|
|
|
|
|
self.calibre_info = arg
|
|
|
|
|
local init_info = {
|
|
|
|
|
canUseCachedMetadata = true,
|
|
|
|
@ -269,7 +389,7 @@ function CalibreCompanion:getInitInfo(arg)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function CalibreCompanion:getDeviceInfo(arg)
|
|
|
|
|
DEBUG("GET_DEVICE_INFORMATION", arg)
|
|
|
|
|
logger.dbg("GET_DEVICE_INFORMATION", arg)
|
|
|
|
|
local device_info = {
|
|
|
|
|
device_info = {
|
|
|
|
|
device_store_uuid = G_reader_settings:readSetting("device_store_uuid"),
|
|
|
|
@ -282,14 +402,14 @@ function CalibreCompanion:getDeviceInfo(arg)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function CalibreCompanion:setCalibreInfo(arg)
|
|
|
|
|
DEBUG("SET_CALIBRE_DEVICE_INFO", arg)
|
|
|
|
|
logger.dbg("SET_CALIBRE_DEVICE_INFO", arg)
|
|
|
|
|
self.calibre_info = arg
|
|
|
|
|
G_reader_settings:saveSetting("device_store_uuid", arg.device_store_uuid)
|
|
|
|
|
self:sendJsonData('OK', {})
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function CalibreCompanion:getFreeSpace(arg)
|
|
|
|
|
DEBUG("FREE_SPACE", arg)
|
|
|
|
|
logger.dbg("FREE_SPACE", arg)
|
|
|
|
|
-- TODO: portable free space calculation?
|
|
|
|
|
-- assume we have 1GB of free space on device
|
|
|
|
|
local free_space = {
|
|
|
|
@ -299,13 +419,13 @@ function CalibreCompanion:getFreeSpace(arg)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function CalibreCompanion:setLibraryInfo(arg)
|
|
|
|
|
DEBUG("SET_LIBRARY_INFO", arg)
|
|
|
|
|
logger.dbg("SET_LIBRARY_INFO", arg)
|
|
|
|
|
self.library_info = arg
|
|
|
|
|
self:sendJsonData('OK', {})
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function CalibreCompanion:getBookCount(arg)
|
|
|
|
|
DEBUG("GET_BOOK_COUNT", arg)
|
|
|
|
|
logger.dbg("GET_BOOK_COUNT", arg)
|
|
|
|
|
local books = {
|
|
|
|
|
willStream = true,
|
|
|
|
|
willScan = true,
|
|
|
|
@ -315,21 +435,21 @@ function CalibreCompanion:getBookCount(arg)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function CalibreCompanion:noop(arg)
|
|
|
|
|
DEBUG("NOOP", arg)
|
|
|
|
|
logger.dbg("NOOP", arg)
|
|
|
|
|
if not arg.count then
|
|
|
|
|
self:sendJsonData('OK', {})
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function CalibreCompanion:sendBooklists(arg)
|
|
|
|
|
DEBUG("SEND_BOOKLISTS", arg)
|
|
|
|
|
logger.dbg("SEND_BOOKLISTS", arg)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function CalibreCompanion:sendBook(arg)
|
|
|
|
|
DEBUG("SEND_BOOK", arg)
|
|
|
|
|
logger.dbg("SEND_BOOK", arg)
|
|
|
|
|
local inbox_dir = G_reader_settings:readSetting("inbox_dir")
|
|
|
|
|
local filename = inbox_dir .. "/" .. arg.lpath
|
|
|
|
|
DEBUG("write to file", filename)
|
|
|
|
|
logger.dbg("write to file", filename)
|
|
|
|
|
util.makePath((util.splitFilePathName(filename)))
|
|
|
|
|
local outfile = io.open(filename, "wb")
|
|
|
|
|
local to_write_bytes = arg.length
|
|
|
|
@ -337,15 +457,15 @@ function CalibreCompanion:sendBook(arg)
|
|
|
|
|
local calibre_socket = self.calibre_socket
|
|
|
|
|
-- switching to raw data receiving mode
|
|
|
|
|
self.calibre_socket.receiveCallback = function(data)
|
|
|
|
|
--DEBUG("receive file data", #data)
|
|
|
|
|
--DEBUG("Memory usage KB:", collectgarbage("count"))
|
|
|
|
|
--logger.info("receive file data", #data)
|
|
|
|
|
--logger.info("Memory usage KB:", collectgarbage("count"))
|
|
|
|
|
local to_write_data = data:sub(1, to_write_bytes)
|
|
|
|
|
outfile:write(to_write_data)
|
|
|
|
|
to_write_bytes = to_write_bytes - #to_write_data
|
|
|
|
|
if to_write_bytes == 0 then
|
|
|
|
|
-- close file as all file data is received and written to local storage
|
|
|
|
|
outfile:close()
|
|
|
|
|
DEBUG("complete writing file", filename)
|
|
|
|
|
logger.info("complete writing file", filename)
|
|
|
|
|
UIManager:show(InfoMessage:new{
|
|
|
|
|
text = _("Received file:") .. filename,
|
|
|
|
|
timeout = 1,
|
|
|
|
@ -356,7 +476,7 @@ function CalibreCompanion:sendBook(arg)
|
|
|
|
|
end
|
|
|
|
|
-- if calibre sends multiple files there may be left JSON data
|
|
|
|
|
calibre_device.buffer = data:sub(#to_write_data + 1) or ""
|
|
|
|
|
DEBUG("device buffer", calibre_device.buffer)
|
|
|
|
|
logger.info("device buffer", calibre_device.buffer)
|
|
|
|
|
if calibre_device.buffer ~= "" then
|
|
|
|
|
UIManager:scheduleIn(0.1, function()
|
|
|
|
|
-- since data is already copied to buffer
|
|
|
|
|