diff --git a/plugins/exporter.koplugin/base.lua b/plugins/exporter.koplugin/base.lua
index 31e37a122..546b6ff81 100644
--- a/plugins/exporter.koplugin/base.lua
+++ b/plugins/exporter.koplugin/base.lua
@@ -24,6 +24,13 @@ function BaseExporter:_init()
self.is_remote = self.is_remote or false
self.version = self.version or "1.0.0"
self:loadSettings()
+ if type(self.init_callback) == "function" then
+ local changed, settings = self:init_callback(self.settings)
+ if changed then
+ self.settings = settings
+ self:saveSettings()
+ end
+ end
return self
end
diff --git a/plugins/exporter.koplugin/main.lua b/plugins/exporter.koplugin/main.lua
index 94b08b583..ea83e2de3 100644
--- a/plugins/exporter.koplugin/main.lua
+++ b/plugins/exporter.koplugin/main.lua
@@ -100,6 +100,7 @@ local Exporter = InputContainer:new {
html = require("target/html"),
joplin = require("target/joplin"),
json = require("target/json"),
+ markdown = require("target/markdown"),
readwise = require("target/readwise"),
text = require("target/text"),
},
diff --git a/plugins/exporter.koplugin/target/joplin.lua b/plugins/exporter.koplugin/target/joplin.lua
index 5e086e747..ac7692e47 100644
--- a/plugins/exporter.koplugin/target/joplin.lua
+++ b/plugins/exporter.koplugin/target/joplin.lua
@@ -1,4 +1,3 @@
-local BD = require("ui/bidi")
local InfoMessage = require("ui/widget/infomessage")
local InputDialog = require("ui/widget/inputdialog")
local UIManager = require("ui/uimanager")
@@ -6,6 +5,7 @@ local http = require("socket.http")
local json = require("json")
local logger = require("logger")
local ltn12 = require("ltn12")
+local md = require("template/md")
local socketutil = require("socketutil")
local T = require("ffi/util").template
local _ = require("gettext")
@@ -15,6 +15,7 @@ local JoplinExporter = require("base"):new {
name = "joplin",
is_remote = true,
notebook_name = _("KOReader Notes"),
+ version = "1.1.0",
}
local function makeRequest(url, method, request_body)
@@ -64,24 +65,6 @@ local function ping(ip, port)
end
end
-local function prepareNote(booknotes)
- local note = ""
- for _, clipping in ipairs(booknotes) do
- local entry = clipping[1]
- if entry.chapter then
- note = note .. "\n\t*" .. entry.chapter .. "*\n\n * * *"
- end
-
- note = note .. os.date("%Y-%m-%d %H:%M:%S \n", entry.time)
- note = note .. entry.text
- if entry.note then
- note = note .. "\n---\n" .. entry.note
- end
- note = note .. "\n * * *\n"
- end
- return note
-end
-
-- If successful returns id of found note.
function JoplinExporter:findNoteByTitle(title, notebook_id)
local url_base = string.format("http://%s:%s/notes?token=%s&fields=id,title,parent_id&page=",
@@ -148,7 +131,7 @@ function JoplinExporter:notebookExist(title)
end
for i, notebook in ipairs(response.items) do
- if notebook.title == title then return true end
+ if notebook.title == title then return notebook.id end
end
return false
end
@@ -305,16 +288,10 @@ function JoplinExporter:getMenuTable()
keep_menu_open = true,
callback = function()
UIManager:show(InfoMessage:new {
- text = T(_([[You can enter your auth token on your computer by saving an empty token. Then quit KOReader, edit the exporter.joplin_token field in %1/settings.reader.lua after creating a backup, and restart KOReader once you're done.
-
-To export to Joplin, you must forward the IP and port used by this plugin to the localhost:port on which Joplin is listening. This can be done with socat or a similar program. For example:
-
-For Windows: netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=41185 connectaddress=localhost connectport=41184
+ text = T(_([[For Joplin setup instructions, see %1
-For Linux: $socat tcp-listen:41185,reuseaddr,fork tcp:localhost:41184
-
-For more information, please visit https://github.com/koreader/koreader/wiki/Highlight-export.]])
- , BD.dirpath("example"))
+Markdown formatting can be configured in:
+Export highlights > Choose formats and services > Markdown.]]), "https://github.com/koreader/koreader/wiki/Joplin")
})
end
}
@@ -329,7 +306,7 @@ function JoplinExporter:export(t)
logger.warn("Cannot reach Joplin server")
return false
end
-
+ local existing_notebook = self:notebookExist(self.notebook_name)
if not self:notebookExist(self.notebook_name) then
local notebook = self:createNotebook(self.notebook_name)
if notebook then
@@ -341,12 +318,19 @@ function JoplinExporter:export(t)
logger.warn("Joplin: unable to create new notebook")
return false
end
+ else
+ if not self.settings.notebook_guid then
+ self.settings.notebook_guid = existing_notebook
+ self:saveSettings()
+ end
end
-
+ local plugin_settings = G_reader_settings:readSetting("exporter") or {}
+ local markdown_settings = plugin_settings.markdown
local notebook_id = self.settings.notebook_guid
for _, booknotes in pairs(t) do
- local note = prepareNote(booknotes)
+ local note = md.prepareBookContent(booknotes, markdown_settings.formatting_options, markdown_settings.highlight_formatting)
local note_id = self:findNoteByTitle(booknotes.title, notebook_id)
+
local response
if note_id then
response = self:updateNote(note, note_id)
diff --git a/plugins/exporter.koplugin/target/markdown.lua b/plugins/exporter.koplugin/target/markdown.lua
new file mode 100644
index 000000000..d81a5f5d2
--- /dev/null
+++ b/plugins/exporter.koplugin/target/markdown.lua
@@ -0,0 +1,135 @@
+local UIManager = require("ui/uimanager")
+local md = require("template/md")
+local _ = require("gettext")
+local T = require("ffi/util").template
+
+-- markdown exporter
+local MarkdownExporter = require("base"):new {
+ name = "markdown",
+ extension = "md",
+ init_callback = function(self, settings)
+ local changed = false
+ if not settings.formatting_options or settings.highlight_formatting == nil then
+ settings.formatting_options = settings.formatting_options or {
+ lighten = "italic",
+ underscore = "underline_markdownit",
+ strikeout = "strikethrough",
+ invert = "bold",
+ }
+ settings.highlight_formatting = settings.highlight_formatting or true
+ changed = true
+ end
+ return changed, settings
+ end,
+}
+
+local formatter_buttons = {
+ { _("None"), "none" },
+ { _("Bold"), "bold" },
+ { _("Bold italic"), "bold_italic" },
+ { _("Italic"), "italic" },
+ { _("Strikethrough"), "strikethrough" },
+ { _("Underline (Markdownit style, with ++)"), "underline_markdownit" },
+ { _("Underline (with tags)"), "underline_u_tag" },
+}
+
+function MarkdownExporter:editFormatStyle(drawer_style, label, touchmenu_instance)
+ local radio_buttons = {}
+ for _idx, v in ipairs(formatter_buttons) do
+ table.insert(radio_buttons, {
+ {
+ text = v[1],
+ checked = self.settings.formatting_options[drawer_style] == v[2],
+ provider = v[2],
+ },
+ })
+ end
+ UIManager:show(require("ui/widget/radiobuttonwidget"):new {
+ title_text = T(_("Formatting style for %1"), label),
+ width_factor = 0.8,
+ radio_buttons = radio_buttons,
+ callback = function(radio)
+ self.settings.formatting_options[drawer_style] = radio.provider
+ touchmenu_instance:updateItems()
+ end,
+ })
+end
+
+function MarkdownExporter:onInit()
+ local changed = false
+ if self.settings.formatting_options == nil then
+ self.settings.formatting_options = {
+ lighten = "italic",
+ underscore = "underline_markdownit",
+ strikeout = "strikethrough",
+ invert = "bold",
+ }
+ changed = true
+ end
+ if self.settings.highlight_formatting == nil then
+ self.settings.highlight_formatting = true
+ changed = true
+ end
+ if changed then
+ self:saveSettings()
+ end
+end
+
+local highlight_style = {
+ { _("Lighten"), "lighten" },
+ { _("Underline"), "underscore" },
+ { _("Strikeout"), "strikeout" },
+ { _("Invert"), "invert" },
+}
+
+function MarkdownExporter:getMenuTable()
+ local menu = {
+ text = _("Markdown"),
+ checked_func = function() return self:isEnabled() end,
+ sub_item_table = {
+ {
+ text = _("Export to Markdown"),
+ checked_func = function() return self:isEnabled() end,
+ callback = function() self:toggleEnabled() end,
+ },
+ {
+ text = _("Format highlights based on style"),
+ checked_func = function() return self.settings.highlight_formatting end,
+ callback = function() self.settings.highlight_formatting = not self.settings.highlight_formatting end,
+ },
+ }
+ }
+
+ for _idx, entry in ipairs(highlight_style) do
+ table.insert(menu.sub_item_table, {
+ text_func = function()
+ return entry[1] .. ": " .. md.formatters[self.settings.formatting_options[entry[2]]].label
+ end,
+ enabled_func = function()
+ return self.settings.highlight_formatting
+ end,
+ keep_menu_open = true,
+ callback = function(touchmenu_instance)
+ self:editFormatStyle(entry[2], entry[1], touchmenu_instance)
+ end,
+ })
+ end
+ return menu
+end
+
+function MarkdownExporter:export(t)
+ local path = self:getFilePath(t)
+ local file = io.open(path, "w")
+ if not file then return false end
+ for idx, book in ipairs(t) do
+ file:write(md.prepareBookContent(book, self.settings.formatting_options, self.settings.highlight_formatting))
+ if idx < #t then
+ file:write("\n")
+ end
+ end
+ file:write("\n\n_Generated at: " .. self:getTimeStamp() .. "_")
+ file:close()
+ return true
+end
+
+return MarkdownExporter
diff --git a/plugins/exporter.koplugin/template/md.lua b/plugins/exporter.koplugin/template/md.lua
new file mode 100644
index 000000000..b11f67b59
--- /dev/null
+++ b/plugins/exporter.koplugin/template/md.lua
@@ -0,0 +1,62 @@
+local _ = require("gettext")
+
+local formatters = {
+ none = {
+ formatter = "%s",
+ label = _("None")
+ },
+ bold = {
+ formatter = "**%s**",
+ label = _("Bold")
+ },
+ italic = {
+ formatter = "*%s*",
+ label = _("Italic")
+ },
+ bold_italic = {
+ formatter = "**_%s_**",
+ label = _("Bold italic")
+ },
+ underline_markdownit = {
+ formatter = "++%s++",
+ label = _("Underline (Markdownit style, with ++)")
+ },
+ underline_u_tag = {
+ formatter = "%s",
+ label = _("Underline (with tags)")
+ },
+ strikethrough = {
+ formatter = "~~%s~~",
+ label = _("Strikethrough")
+ },
+}
+
+local function prepareBookContent(book, formatting_options, highlight_formatting)
+ local content = ""
+ local current_chapter = nil
+ content = content .. "# " .. book.title .. "\n"
+ content = content .. "##### " .. book.author:gsub("\n", ", ") .. "\n\n"
+ for _, note in ipairs(book) do
+ local entry = note[1]
+ if entry.chapter ~= current_chapter then
+ current_chapter = entry.chapter
+ content = content .. "## " .. current_chapter .. "\n"
+ end
+ content = content .. "### Page " .. entry.page .. " @ " .. os.date("%d %B %Y %I:%M %p", entry.time) .. "\n"
+ if highlight_formatting then
+ content = content .. string.format(formatters[formatting_options[entry.drawer]].formatter, entry.text) .."\n"
+ else
+ content = content .. entry.text .. "\n"
+ end
+ if entry.note then
+ content = content .. "\n---\n" .. entry.note .. "\n"
+ end
+ content = content .. "\n"
+ end
+ return content
+end
+
+return {
+ prepareBookContent = prepareBookContent,
+ formatters = formatters
+}