mirror of
https://github.com/koreader/koreader
synced 2024-11-10 01:10:34 +00:00
79c13bee0c
* Added a safe pure-Lua SAX JSON parser (via LunaJSON). * Updated RapidJSON. * Also implemented a calibre-specific SAX parser in Lua-RapidJSON, and use it by default instead of the full RapidJSON one. * Raised the file-size threshold to switch between the fast & safe parsers to 50MB. * Added an UI option to switch between the three parsers.
469 lines
18 KiB
Lua
469 lines
18 KiB
Lua
--[[
|
|
This plugin implements KOReader integration with *some* calibre features:
|
|
|
|
- metadata search
|
|
- wireless transfers
|
|
|
|
This module handles the UI part of the plugin.
|
|
--]]
|
|
|
|
local BD = require("ui/bidi")
|
|
local CalibreExtensions = require("extensions")
|
|
local CalibreSearch = require("search")
|
|
local CalibreWireless = require("wireless")
|
|
local Dispatcher = require("dispatcher")
|
|
local InfoMessage = require("ui/widget/infomessage")
|
|
local LuaSettings = require("luasettings")
|
|
local UIManager = require("ui/uimanager")
|
|
local WidgetContainer = require("ui/widget/container/widgetcontainer")
|
|
local _ = require("gettext")
|
|
local C_ = _.pgettext
|
|
local T = require("ffi/util").template
|
|
|
|
local Calibre = WidgetContainer:extend{
|
|
name = "calibre",
|
|
is_doc_only = false,
|
|
}
|
|
|
|
function Calibre:onCalibreSearch()
|
|
CalibreSearch:ShowSearch()
|
|
return true
|
|
end
|
|
|
|
function Calibre:onCalibreBrowseBy(field)
|
|
CalibreSearch.search_value = ""
|
|
CalibreSearch:find(field)
|
|
return true
|
|
end
|
|
|
|
function Calibre:onNetworkDisconnected()
|
|
self:closeWirelessConnection()
|
|
end
|
|
|
|
function Calibre:onSuspend()
|
|
self:closeWirelessConnection()
|
|
end
|
|
|
|
function Calibre:onClose()
|
|
self:closeWirelessConnection()
|
|
end
|
|
|
|
function Calibre:onStartWirelessConnection()
|
|
self:startWirelessConnection()
|
|
end
|
|
|
|
function Calibre:onCloseWirelessConnection()
|
|
self:closeWirelessConnection()
|
|
end
|
|
|
|
function Calibre:startWirelessConnection()
|
|
if not CalibreWireless.calibre_socket then
|
|
CalibreWireless:connect()
|
|
end
|
|
end
|
|
|
|
function Calibre:closeWirelessConnection()
|
|
if CalibreWireless.calibre_socket then
|
|
CalibreWireless:disconnect()
|
|
end
|
|
end
|
|
|
|
function Calibre:onDispatcherRegisterActions()
|
|
Dispatcher:registerAction("calibre_search", { category="none", event="CalibreSearch", title=_("Calibre metadata search"), general=true,})
|
|
Dispatcher:registerAction("calibre_browse_tags", { category="none", event="CalibreBrowseBy", arg="tags", title=_("Browse all calibre tags"), general=true,})
|
|
Dispatcher:registerAction("calibre_browse_series", { category="none", event="CalibreBrowseBy", arg="series", title=_("Browse all calibre series"), general=true,})
|
|
Dispatcher:registerAction("calibre_browse_authors", { category="none", event="CalibreBrowseBy", arg="authors", title=_("Browse all calibre authors"), general=true,})
|
|
Dispatcher:registerAction("calibre_browse_titles", { category="none", event="CalibreBrowseBy", arg="title", title=_("Browse all calibre titles"), general=true, separator=true,})
|
|
Dispatcher:registerAction("calibre_start_connection", { category="none", event="StartWirelessConnection", title=_("Calibre wireless connect"), general=true,})
|
|
Dispatcher:registerAction("calibre_close_connection", { category="none", event="CloseWirelessConnection", title=_("Calibre wireless disconnect"), general=true,})
|
|
end
|
|
|
|
function Calibre:init()
|
|
CalibreWireless:init()
|
|
self:onDispatcherRegisterActions()
|
|
self.ui.menu:registerToMainMenu(self)
|
|
end
|
|
|
|
function Calibre:addToMainMenu(menu_items)
|
|
menu_items.calibre = {
|
|
-- its name is "calibre", but all our top menu items are uppercase.
|
|
text = _("Calibre"),
|
|
sub_item_table = {
|
|
{
|
|
text_func = function()
|
|
if CalibreWireless.calibre_socket then
|
|
return _("Disconnect")
|
|
else
|
|
return _("Connect")
|
|
end
|
|
end,
|
|
separator = true,
|
|
enabled_func = function()
|
|
return G_reader_settings:nilOrTrue("calibre_wireless")
|
|
end,
|
|
callback = function()
|
|
if not CalibreWireless.calibre_socket then
|
|
CalibreWireless:connect()
|
|
else
|
|
CalibreWireless:disconnect()
|
|
end
|
|
end,
|
|
},
|
|
{ text = _("Search settings"),
|
|
keep_menu_open = true,
|
|
sub_item_table = self:getSearchMenuTable(),
|
|
},
|
|
{
|
|
text = _("Wireless settings"),
|
|
keep_menu_open = true,
|
|
sub_item_table = self:getWirelessMenuTable(),
|
|
},
|
|
{
|
|
text = _("JSON parser"),
|
|
keep_menu_open = true,
|
|
sub_item_table = self:getParserMenuTable(),
|
|
},
|
|
}
|
|
}
|
|
-- insert the metadata search
|
|
if G_reader_settings:isTrue("calibre_search_from_reader") or not self.ui.view then
|
|
menu_items.find_book_in_calibre_catalog = {
|
|
text = _("Calibre metadata search"),
|
|
callback = function()
|
|
CalibreSearch:ShowSearch()
|
|
end
|
|
}
|
|
end
|
|
end
|
|
|
|
-- search options available from UI
|
|
function Calibre:getSearchMenuTable()
|
|
return {
|
|
{
|
|
text = _("Manage libraries"),
|
|
separator = true,
|
|
keep_menu_open = true,
|
|
sub_item_table_func = function()
|
|
local result = {}
|
|
-- append previous scanned dirs to the list.
|
|
local cache = LuaSettings:open(CalibreSearch.cache_libs.path)
|
|
for path, _ in pairs(cache.data) do
|
|
table.insert(result, {
|
|
text = path,
|
|
keep_menu_open = true,
|
|
checked_func = function()
|
|
return cache:isTrue(path)
|
|
end,
|
|
callback = function()
|
|
cache:toggle(path)
|
|
cache:flush()
|
|
CalibreSearch:invalidateCache()
|
|
end,
|
|
})
|
|
end
|
|
-- if there's no result then no libraries are stored
|
|
if #result == 0 then
|
|
table.insert(result, {
|
|
text = _("No calibre libraries"),
|
|
enabled = false
|
|
})
|
|
end
|
|
table.insert(result, 1, {
|
|
text = _("Rescan disk for calibre libraries"),
|
|
separator = true,
|
|
callback = function()
|
|
CalibreSearch:prompt()
|
|
end,
|
|
})
|
|
return result
|
|
end,
|
|
},
|
|
{
|
|
text = _("Enable searches in the reader"),
|
|
checked_func = function()
|
|
return G_reader_settings:isTrue("calibre_search_from_reader")
|
|
end,
|
|
callback = function()
|
|
G_reader_settings:toggle("calibre_search_from_reader")
|
|
UIManager:show(InfoMessage:new{
|
|
text = _("This will take effect on next restart."),
|
|
})
|
|
end,
|
|
},
|
|
{
|
|
text = _("Store metadata in cache"),
|
|
checked_func = function()
|
|
return G_reader_settings:nilOrTrue("calibre_search_cache_metadata")
|
|
end,
|
|
callback = function()
|
|
G_reader_settings:flipNilOrTrue("calibre_search_cache_metadata")
|
|
end,
|
|
},
|
|
{
|
|
text = _("Case sensitive search"),
|
|
checked_func = function()
|
|
return not G_reader_settings:nilOrTrue("calibre_search_case_insensitive")
|
|
end,
|
|
callback = function()
|
|
G_reader_settings:flipNilOrTrue("calibre_search_case_insensitive")
|
|
end,
|
|
},
|
|
{
|
|
text = _("Search by title"),
|
|
checked_func = function()
|
|
return G_reader_settings:nilOrTrue("calibre_search_find_by_title")
|
|
end,
|
|
callback = function()
|
|
G_reader_settings:flipNilOrTrue("calibre_search_find_by_title")
|
|
end,
|
|
},
|
|
{
|
|
text = _("Search by authors"),
|
|
checked_func = function()
|
|
return G_reader_settings:nilOrTrue("calibre_search_find_by_authors")
|
|
end,
|
|
callback = function()
|
|
G_reader_settings:flipNilOrTrue("calibre_search_find_by_authors")
|
|
end,
|
|
},
|
|
{
|
|
text = _("Search by series"),
|
|
checked_func = function()
|
|
return G_reader_settings:isTrue("calibre_search_find_by_series")
|
|
end,
|
|
callback = function()
|
|
G_reader_settings:toggle("calibre_search_find_by_series")
|
|
end,
|
|
},
|
|
{
|
|
text = _("Search by tag"),
|
|
checked_func = function()
|
|
return G_reader_settings:isTrue("calibre_search_find_by_tag")
|
|
end,
|
|
callback = function()
|
|
G_reader_settings:toggle("calibre_search_find_by_tag")
|
|
end,
|
|
},
|
|
{
|
|
text = _("Search by path"),
|
|
checked_func = function()
|
|
return G_reader_settings:isTrue("calibre_search_find_by_path")
|
|
end,
|
|
callback = function()
|
|
G_reader_settings:toggle("calibre_search_find_by_path")
|
|
end,
|
|
},
|
|
}
|
|
end
|
|
|
|
-- wireless options available from UI
|
|
function Calibre:getWirelessMenuTable()
|
|
local function isEnabled()
|
|
local enabled = G_reader_settings:nilOrTrue("calibre_wireless")
|
|
return enabled and not CalibreWireless.calibre_socket
|
|
end
|
|
|
|
local t = {
|
|
{
|
|
text = _("Enable wireless client"),
|
|
separator = true,
|
|
enabled_func = function()
|
|
return not CalibreWireless.calibre_socket
|
|
end,
|
|
checked_func = function()
|
|
return G_reader_settings:nilOrTrue("calibre_wireless")
|
|
end,
|
|
callback = function()
|
|
G_reader_settings:flipNilOrTrue("calibre_wireless")
|
|
end,
|
|
},
|
|
{
|
|
text = _("Set password"),
|
|
enabled_func = isEnabled,
|
|
callback = function()
|
|
CalibreWireless:setPassword()
|
|
end,
|
|
},
|
|
{
|
|
text = _("Set inbox folder"),
|
|
enabled_func = isEnabled,
|
|
callback = function()
|
|
CalibreWireless: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)"), BD.ltr(address))
|
|
end,
|
|
enabled_func = isEnabled,
|
|
sub_item_table = {
|
|
{
|
|
text = C_("Configuration type", "Automatic"),
|
|
checked_func = function()
|
|
return G_reader_settings:hasNot("calibre_wireless_url")
|
|
end,
|
|
callback = function()
|
|
G_reader_settings:delSetting("calibre_wireless_url")
|
|
end,
|
|
},
|
|
{
|
|
text = C_("Configuration type", "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"),
|
|
id = "close",
|
|
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,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
if not CalibreExtensions:isCustom() then
|
|
table.insert(t, 2, {
|
|
text = _("File formats"),
|
|
enabled_func = isEnabled,
|
|
sub_item_table_func = function()
|
|
local submenu = {
|
|
{
|
|
text = _("About formats"),
|
|
keep_menu_open = true,
|
|
separator = true,
|
|
callback = function()
|
|
UIManager:show(InfoMessage:new{
|
|
text = string.format("%s: %s \n\n%s",
|
|
_("Supported file formats"),
|
|
CalibreExtensions:getInfo(),
|
|
_("Unsupported formats will be converted by calibre to the first format of the list."))
|
|
})
|
|
end,
|
|
}
|
|
}
|
|
|
|
for i, v in ipairs(CalibreExtensions.outputs) do
|
|
table.insert(submenu, {})
|
|
submenu[i+1].text = v
|
|
submenu[i+1].checked_func = function()
|
|
if v == CalibreExtensions.default_output then
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
submenu[i+1].callback = function()
|
|
if type(v) == "string" and v ~= CalibreExtensions.default_output then
|
|
CalibreExtensions.default_output = v
|
|
G_reader_settings:saveSetting("calibre_wireless_default_format", CalibreExtensions.default_output)
|
|
end
|
|
end
|
|
end
|
|
return submenu
|
|
end,
|
|
})
|
|
end
|
|
return t
|
|
end
|
|
|
|
function Calibre:getParserMenuTable()
|
|
return {
|
|
{
|
|
text = _("Automatic"),
|
|
help_text = _("The program will decide based on the size of the JSON file. Recommended"),
|
|
checked_func = function()
|
|
return G_reader_settings:hasNot("calibre_json_parser")
|
|
end,
|
|
callback = function()
|
|
G_reader_settings:delSetting("calibre_json_parser")
|
|
end,
|
|
},
|
|
{
|
|
text = _("Fast"),
|
|
help_text = _("Faster parsing, but may not take too kindly to malformed input files"),
|
|
checked_func = function()
|
|
return G_reader_settings:readSetting("calibre_json_parser") == "fast"
|
|
end,
|
|
callback = function()
|
|
G_reader_settings:saveSetting("calibre_json_parser", "fast")
|
|
end,
|
|
},
|
|
{
|
|
text = _("Safe"),
|
|
help_text = _("Slower, but safer. Useful if you're experiencing problems with the other modes"),
|
|
checked_func = function()
|
|
return G_reader_settings:readSetting("calibre_json_parser") == "safe"
|
|
end,
|
|
callback = function()
|
|
G_reader_settings:saveSetting("calibre_json_parser", "safe")
|
|
end,
|
|
},
|
|
{
|
|
text = _("Legacy"),
|
|
help_text = _("Fast, but requires more RAM, only recommended on modest library sizes (or beefier devices)"),
|
|
checked_func = function()
|
|
return G_reader_settings:readSetting("calibre_json_parser") == "legacy"
|
|
end,
|
|
callback = function()
|
|
G_reader_settings:saveSetting("calibre_json_parser", "legacy")
|
|
end,
|
|
},
|
|
}
|
|
end
|
|
|
|
|
|
return Calibre
|