mirror of
https://github.com/koreader/koreader.git
synced 2025-12-13 20:36:53 +01:00
Markdown export (#9076)
Enables users to export markdown locally with some configuration options to allow users to format the output to a certain extent.
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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"),
|
||||
},
|
||||
|
||||
@@ -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.
|
||||
text = T(_([[For Joplin setup instructions, see %1
|
||||
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
135
plugins/exporter.koplugin/target/markdown.lua
Normal file
135
plugins/exporter.koplugin/target/markdown.lua
Normal file
@@ -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 <u></u> 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
|
||||
62
plugins/exporter.koplugin/template/md.lua
Normal file
62
plugins/exporter.koplugin/template/md.lua
Normal file
@@ -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 = "<u>%s</u>",
|
||||
label = _("Underline (with <u></u> 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
|
||||
}
|
||||
Reference in New Issue
Block a user