diff --git a/plugins/calibrecompanion.koplugin/main.lua b/plugins/calibrecompanion.koplugin/main.lua index 028e75d31..e6ff41dcd 100644 --- a/plugins/calibrecompanion.koplugin/main.lua +++ b/plugins/calibrecompanion.koplugin/main.lua @@ -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