mirror of https://github.com/koreader/koreader
Exporter: add XMNote export (#11087)
parent
7870e83410
commit
dea94026f1
@ -0,0 +1,166 @@
|
|||||||
|
local InputDialog = require("ui/widget/inputdialog")
|
||||||
|
local UIManager = require("ui/uimanager")
|
||||||
|
local InfoMessage = require("ui/widget/infomessage")
|
||||||
|
local BD = require("ui/bidi")
|
||||||
|
local DataStorage = require("datastorage")
|
||||||
|
local http = require("socket.http")
|
||||||
|
local json = require("json")
|
||||||
|
local logger = require("logger")
|
||||||
|
local ltn12 = require("ltn12")
|
||||||
|
local socket = require("socket")
|
||||||
|
local socketutil = require("socketutil")
|
||||||
|
local util = require("ffi/util")
|
||||||
|
local T = util.template
|
||||||
|
local _ = require("gettext")
|
||||||
|
|
||||||
|
-- xmnote exporter
|
||||||
|
local XMNoteExporter = require("base"):new {
|
||||||
|
name = "xmnote",
|
||||||
|
is_remote = true,
|
||||||
|
server_port = 8080
|
||||||
|
}
|
||||||
|
|
||||||
|
function XMNoteExporter:getMenuTable()
|
||||||
|
return {
|
||||||
|
text = _("XMNote"),
|
||||||
|
checked_func = function() return self:isEnabled() end,
|
||||||
|
sub_item_table = {
|
||||||
|
{
|
||||||
|
text = _("Set XMNote IP"),
|
||||||
|
keep_menu_open = true,
|
||||||
|
callback = function()
|
||||||
|
local url_dialog
|
||||||
|
url_dialog = InputDialog:new {
|
||||||
|
title = _("Set XMNote IP"),
|
||||||
|
input = self.settings.ip,
|
||||||
|
buttons = {
|
||||||
|
{
|
||||||
|
{
|
||||||
|
text = _("Cancel"),
|
||||||
|
callback = function()
|
||||||
|
UIManager:close(url_dialog)
|
||||||
|
end
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text = _("Set IP"),
|
||||||
|
callback = function()
|
||||||
|
local ip = url_dialog:getInputText()
|
||||||
|
self.settings.ip = ip
|
||||||
|
self:saveSettings()
|
||||||
|
UIManager:close(url_dialog)
|
||||||
|
end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UIManager:show(url_dialog)
|
||||||
|
url_dialog:onShowKeyboard()
|
||||||
|
end
|
||||||
|
} ,
|
||||||
|
{
|
||||||
|
text = _("Export to XMNote"),
|
||||||
|
checked_func = function() return self:isEnabled() end,
|
||||||
|
callback = function() self:toggleEnabled() end,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text = _("Help"),
|
||||||
|
keep_menu_open = true,
|
||||||
|
callback = function()
|
||||||
|
UIManager:show(InfoMessage:new {
|
||||||
|
text = T(_([[Before starting the export process, please make sure that your mobile and KOReader are connected to the same local network. Open XMNote and go to "My" - "Import Highlights" - "Import via API". At the bottom of the interface, you will find the IP address of your mobile device. Enter this IP address into KOReader to complete the configuration.]])
|
||||||
|
, BD.dirpath(DataStorage:getDataDir()))
|
||||||
|
})
|
||||||
|
end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function XMNoteExporter:createRequestBody(booknotes)
|
||||||
|
local book = {
|
||||||
|
title = booknotes.title or "",
|
||||||
|
type = 1,
|
||||||
|
locationUnit = 1,
|
||||||
|
}
|
||||||
|
local entries = {}
|
||||||
|
for _, chapter in ipairs(booknotes) do
|
||||||
|
for _, clipping in ipairs(chapter) do
|
||||||
|
local entry = {
|
||||||
|
text = clipping.text,
|
||||||
|
note = clipping.note or "",
|
||||||
|
chapter = clipping.chapter,
|
||||||
|
time = clipping.time
|
||||||
|
}
|
||||||
|
local page = tonumber(clipping.page)
|
||||||
|
if page ~= nil then
|
||||||
|
entry.page = page
|
||||||
|
end
|
||||||
|
table.insert(entries, entry)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
book.entries = entries
|
||||||
|
return book
|
||||||
|
end
|
||||||
|
|
||||||
|
function XMNoteExporter:makeRequest(endpoint, method, request_body)
|
||||||
|
local sink = {}
|
||||||
|
local request_body_json = json.encode(request_body)
|
||||||
|
local source = ltn12.source.string(request_body_json)
|
||||||
|
socketutil:set_timeout(socketutil.LARGE_BLOCK_TIMEOUT, socketutil.LARGE_TOTAL_TIMEOUT)
|
||||||
|
local url = "http://".. self.settings.ip .. ":" .. self.server_port .. endpoint
|
||||||
|
local request = {
|
||||||
|
url = url,
|
||||||
|
method = method,
|
||||||
|
sink = ltn12.sink.table(sink),
|
||||||
|
source = source,
|
||||||
|
headers = {
|
||||||
|
["Content-Length"] = #request_body_json,
|
||||||
|
["Content-Type"] = "application/json"
|
||||||
|
},
|
||||||
|
}
|
||||||
|
local code, headers, status = socket.skip(1, http.request(request))
|
||||||
|
socketutil:reset_timeout()
|
||||||
|
|
||||||
|
if code ~= 200 then
|
||||||
|
logger.warn("XMNoteClient: HTTP response code <> 200. Response status: ", status)
|
||||||
|
logger.dbg("Response headers:", headers)
|
||||||
|
return nil, status
|
||||||
|
end
|
||||||
|
|
||||||
|
local response = json.decode(sink[1])
|
||||||
|
local api_code = response["code"]
|
||||||
|
if api_code ~= nil and api_code ~= 200 then
|
||||||
|
logger.warn("XMNoteClient: response code <> 200. message: ", response["message"])
|
||||||
|
logger.dbg("Response headers:", headers)
|
||||||
|
return nil, status
|
||||||
|
end
|
||||||
|
return response
|
||||||
|
end
|
||||||
|
|
||||||
|
function XMNoteExporter:createHighlights(booknotes)
|
||||||
|
local body = self:createRequestBody(booknotes)
|
||||||
|
local result, err = self:makeRequest("/send", "POST", body)
|
||||||
|
if not result then
|
||||||
|
logger.warn("error creating highlights", err)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
logger.dbg("createHighlights result", result)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function XMNoteExporter:isReadyToExport()
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function XMNoteExporter:export(t)
|
||||||
|
for _, booknotes in ipairs(t) do
|
||||||
|
local ok = self:createHighlights(booknotes)
|
||||||
|
if not ok then return false end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
return XMNoteExporter
|
||||||
|
|
Loading…
Reference in New Issue