Markdown export (#9076)

Enables users to export markdown locally with some configuration options to allow users to format the output to a certain extent.
pull/9139/head
Utsob Roy 2 years ago committed by GitHub
parent ae5b2d68ca
commit 6804b77251
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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.
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)

@ -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

@ -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
}
Loading…
Cancel
Save