mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
The "My Clipping" file that storing highlights and notes for Kindle native readers could also be parsed and exported. The parser is implemented in `evernote.koplugin/clip.lua`. Parsed highlights and notes in one book will be packed and rendered into html node with a slt2 template `note.tpl` that complies with evernote markup language(ENML). Finally the evernote client will create or update note entries and push them to Evernote cloud.
263 lines
7.1 KiB
Lua
263 lines
7.1 KiB
Lua
-- lfs
|
|
|
|
local MyClipping = {
|
|
my_clippings = "/mnt/us/documents/My Clippings.txt",
|
|
history_dir = "./history",
|
|
}
|
|
|
|
function MyClipping:new(o)
|
|
o = o or {}
|
|
setmetatable(o, self)
|
|
self.__index = self
|
|
return o
|
|
end
|
|
|
|
--[[
|
|
-- clippings: main table to store parsed highlights and notes entries
|
|
-- {
|
|
-- ["Title(Author Name)"] = {
|
|
-- {
|
|
-- {
|
|
-- ["page"] = 123,
|
|
-- ["time"] = 1398127554,
|
|
-- ["text"] = "Games of all sorts were played in homes and fields."
|
|
-- },
|
|
-- {
|
|
-- ["page"] = 156,
|
|
-- ["time"] = 1398128287,
|
|
-- ["text"] = "There Spenser settled down to gentleman farming.",
|
|
-- ["note"] = "This is a sample note.",
|
|
-- },
|
|
-- ["title"] = "Chapter I"
|
|
-- },
|
|
-- }
|
|
-- }
|
|
-- ]]
|
|
function MyClipping:parseMyClippings()
|
|
-- My Clippings format:
|
|
-- Title(Author Name)
|
|
-- Your Highlight on Page 123 | Added on Monday, April 21, 2014 10:08:07 PM
|
|
--
|
|
-- This is a sample highlight.
|
|
-- ==========
|
|
local file = io.open(self.my_clippings, "r")
|
|
local clippings = {}
|
|
if file then
|
|
local index = 1
|
|
local corrupted = false
|
|
local title, author, info, text
|
|
for line in file:lines() do
|
|
line = line:match("^%s*(.-)%s*$") or ""
|
|
if index == 1 then
|
|
title, author = self:getTitle(line)
|
|
clippings[title] = clippings[title] or {
|
|
title = title,
|
|
author = author,
|
|
}
|
|
elseif index == 2 then
|
|
info = self:getInfo(line)
|
|
elseif index == 3 then
|
|
-- should be a blank line, we skip this line
|
|
elseif index == 4 then
|
|
text = self:getText(line)
|
|
end
|
|
if line == "==========" then
|
|
if index == 5 then
|
|
-- entry ends normally
|
|
local clipping = {
|
|
page = info.page or info.location,
|
|
sort = info.sort,
|
|
time = info.time,
|
|
text = text,
|
|
}
|
|
-- we cannot extract chapter info so just insert clipping
|
|
-- to a place holder chapter
|
|
table.insert(clippings[title], { clipping })
|
|
end
|
|
index = 0
|
|
end
|
|
index = index + 1
|
|
end
|
|
end
|
|
|
|
return clippings
|
|
end
|
|
|
|
local extensions = {
|
|
[".pdf"] = true,
|
|
[".djvu"] = true,
|
|
[".epub"] = true,
|
|
[".fb2"] = true,
|
|
[".mobi"] = true,
|
|
[".txt"] = true,
|
|
[".html"] = true,
|
|
[".doc"] = true,
|
|
}
|
|
|
|
-- remove file extensions added by former Koreader
|
|
-- extract author name in "Title(Author)" format
|
|
-- extract author name in "Title - Author" format
|
|
function MyClipping:getTitle(line)
|
|
line = line:match("^%s*(.-)%s*$") or ""
|
|
if extensions[line:sub(-4):lower()] then
|
|
line = line:sub(1, -5)
|
|
elseif extensions[line:sub(-5):lower()] then
|
|
line = line:sub(1, -6)
|
|
end
|
|
local _, _, title, author = line:find("(.-)%s*%((.*)%)")
|
|
if not author then
|
|
_, _, title, author = line:find("(.-)%s*-%s*(.*)")
|
|
end
|
|
if not title then title = line end
|
|
return title:match("^%s*(.-)%s*$"), author
|
|
end
|
|
|
|
local keywords = {
|
|
["highlight"] = {
|
|
"Highlight",
|
|
"标注",
|
|
},
|
|
["note"] = {
|
|
"Note",
|
|
"笔记",
|
|
},
|
|
["bookmark"] = {
|
|
"Bookmark",
|
|
"书签",
|
|
},
|
|
}
|
|
|
|
local months = {
|
|
["Jan"] = 1,
|
|
["Feb"] = 2,
|
|
["Mar"] = 3,
|
|
["Apr"] = 4,
|
|
["May"] = 5,
|
|
["Jun"] = 6,
|
|
["Jul"] = 7,
|
|
["Aug"] = 8,
|
|
["Sep"] = 9,
|
|
["Oct"] = 10,
|
|
["Nov"] = 11,
|
|
["Dec"] = 12
|
|
}
|
|
|
|
local pms = {
|
|
["PM"] = 12,
|
|
["下午"] = 12,
|
|
}
|
|
|
|
function MyClipping:getTime(line)
|
|
if not line then return end
|
|
local _, _, year, month, day = line:find("(%d+)年(%d+)月(%d+)日")
|
|
if not year or not month or not day then
|
|
_, _, year, month, day = line:find("(%d%d%d%d)-(%d%d)-(%d%d)")
|
|
end
|
|
if not year or not month or not day then
|
|
for k, v in pairs(months) do
|
|
if line:find(k) then
|
|
month = v
|
|
_, _, day = line:find(" (%d%d),")
|
|
_, _, year = line:find(" (%d%d%d%d)")
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
local _, _, hour, minute, second = line:find("(%d+):(%d+):(%d+)")
|
|
if year and month and day and hour and minute and second then
|
|
for k, v in pairs(pms) do
|
|
if line:find(k) then hour = hour + v end
|
|
break
|
|
end
|
|
local time = os.time({
|
|
year = year, month = month, day = day,
|
|
hour = hour, min = minute, sec = second,
|
|
})
|
|
|
|
return time
|
|
end
|
|
end
|
|
|
|
function MyClipping:getInfo(line)
|
|
local info = {}
|
|
line = line or ""
|
|
local _, _, part1, part2 = line:find("(.+)%s*|%s*(.+)")
|
|
|
|
-- find entry type and location
|
|
for sort, words in pairs(keywords) do
|
|
for _, word in ipairs(words) do
|
|
if part1 and part1:find(word) then
|
|
info.sort = sort
|
|
info.location = part1:match("(%d+-?%d+)")
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
-- find entry created time
|
|
info.time = self:getTime(part2 or "")
|
|
|
|
return info
|
|
end
|
|
|
|
function MyClipping:getText(line)
|
|
line = line or ""
|
|
return line:match("^%s*(.-)%s*$") or ""
|
|
end
|
|
|
|
function MyClipping:parseHighlight(highlights, book)
|
|
for page, items in pairs(highlights) do
|
|
for _, item in ipairs(items) do
|
|
local clipping = {}
|
|
clipping.page = page
|
|
clipping.sort = "highlight"
|
|
clipping.time = self:getTime(item.datetime or "")
|
|
clipping.text = self:getText(item.text)
|
|
-- TODO: store chapter info when exporting highlights
|
|
if clipping.text and clipping.text ~= "" then
|
|
table.insert(book, { clipping })
|
|
end
|
|
end
|
|
end
|
|
table.sort(book, function(v1, v2) return v1[1].page < v2[1].page end)
|
|
end
|
|
|
|
function MyClipping:parseHistory()
|
|
local clippings = {}
|
|
for f in lfs.dir(self.history_dir) do
|
|
local path = self.history_dir.."/"..f
|
|
if lfs.attributes(path, "mode") == "file" and path:find(".+%.lua$") then
|
|
local ok, stored = pcall(dofile, path)
|
|
if ok and stored.highlight then
|
|
local _, _, docname = path:find("%[.*%](.*)%.lua$")
|
|
local title, author = self:getTitle(docname)
|
|
clippings[title] = {
|
|
title = title,
|
|
author = author,
|
|
}
|
|
self:parseHighlight(stored.highlight, clippings[title])
|
|
end
|
|
end
|
|
end
|
|
|
|
return clippings
|
|
end
|
|
|
|
function MyClipping:parseCurrentDoc(view)
|
|
local clippings = {}
|
|
local path = view.document.file
|
|
local _, _, docname = path:find(".*/(.*)")
|
|
local title, author = self:getTitle(docname)
|
|
clippings[title] = {
|
|
title = title,
|
|
author = author,
|
|
}
|
|
self:parseHighlight(view.highlight.saved, clippings[title])
|
|
|
|
return clippings
|
|
end
|
|
|
|
return MyClipping
|
|
|