Docsettings: add centralized sdr storage (#10074)

Added an option to choose a new location to save document settings, highlights and bookmarks 
(koreader/docsettings folder).
reviewable/pr10135/r1
hius07 1 year ago committed by GitHub
parent c5d606a7f4
commit d1081fa982
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -42,6 +42,9 @@ function DataStorage:getSettingsDir()
return self:getDataDir() .. "/settings"
end
function DataStorage:getDocSettingsDir()
return self:getDataDir() .. "/docsettings"
end
function DataStorage:getFullDataDir()
if full_data_dir then return full_data_dir end
@ -58,8 +61,8 @@ end
local function initDataDir()
local sub_data_dirs = {
"cache", "clipboard",
"data", "data/dict", "data/tessdata",
"history", "ota", "plugins",
"data", "data/dict", "data/tessdata", "docsettings",
"history", "ota", "patches", "plugins",
"screenshots", "settings", "styletweaks",
}
for _, dir in ipairs(sub_data_dirs) do

@ -15,6 +15,7 @@ local util = require("util")
local DocSettings = LuaSettings:extend{}
local HISTORY_DIR = DataStorage:getHistoryDir()
local DOCSETTINGS_DIR = DataStorage:getDocSettingsDir()
local function buildCandidates(list)
local candidates = {}
@ -64,120 +65,125 @@ local function buildCandidates(list)
end
--- Returns path to sidecar directory (`filename.sdr`).
--
-- Sidecar directory is the file without _last_ suffix.
-- @string doc_path path to the document (e.g., `/foo/bar.pdf`)
-- @treturn string path to the sidecar directory (e.g., `/foo/bar.sdr`)
function DocSettings:getSidecarDir(doc_path)
if doc_path == nil or doc_path == '' then return '' end
local file_without_suffix = doc_path:match("(.*)%.")
if file_without_suffix then
return file_without_suffix..".sdr"
function DocSettings:getSidecarDir(doc_path, force_location)
if doc_path == nil or doc_path == "" then return "" end
local path = doc_path:match("(.*)%.") or doc_path -- file path without the last suffix
local location = force_location or G_reader_settings:readSetting("document_metadata_folder", "doc")
if location == "dir" then
path = DOCSETTINGS_DIR..path
end
return doc_path..".sdr"
return path..".sdr"
end
--- Returns path to `metadata.lua` file.
-- @string doc_path path to the document (e.g., `/foo/bar.pdf`)
-- @treturn string path to `/foo/bar.sdr/metadata.lua` file
function DocSettings:getSidecarFile(doc_path)
if doc_path == nil or doc_path == '' then return '' end
function DocSettings:getSidecarFile(doc_path, force_location)
if doc_path == nil or doc_path == "" then return "" end
-- If the file does not have a suffix or we are working on a directory, we
-- should ignore the suffix part in metadata file path.
local suffix = doc_path:match(".*%.(.+)")
if suffix == nil then
suffix = ''
end
return self:getSidecarDir(doc_path) .. "/metadata." .. suffix .. ".lua"
local suffix = doc_path:match(".*%.(.+)") or ""
return self:getSidecarDir(doc_path, force_location) .. "/metadata." .. suffix .. ".lua"
end
--- Returns `true` if there is a `metadata.lua` file.
-- @string doc_path path to the document (e.g., `/foo/bar.pdf`)
-- @treturn bool
function DocSettings:hasSidecarFile(doc_path)
return lfs.attributes(self:getSidecarFile(doc_path), "mode") == "file"
return lfs.attributes(self:getSidecarFile(doc_path, "doc"), "mode") == "file"
or lfs.attributes(self:getSidecarFile(doc_path, "dir"), "mode") == "file"
or lfs.attributes(self:getHistoryPath(doc_path), "mode") == "file"
end
function DocSettings:getLastSaveTime(doc_path) -- for readhistory
return lfs.attributes(self:getSidecarFile(doc_path, "doc"), "modification")
or lfs.attributes(self:getSidecarFile(doc_path, "dir"), "modification")
end
function DocSettings:getHistoryPath(fullpath)
return HISTORY_DIR .. "/[" .. fullpath:gsub("(.*/)([^/]+)", "%1] %2"):gsub("/", "#") .. ".lua"
function DocSettings:getHistoryPath(doc_path)
if doc_path == nil or doc_path == "" then return "" end
return HISTORY_DIR .. "/[" .. doc_path:gsub("(.*/)([^/]+)", "%1] %2"):gsub("/", "#") .. ".lua"
end
function DocSettings:getPathFromHistory(hist_name)
if hist_name == nil or hist_name == '' then return '' end
if hist_name:sub(-4) ~= ".lua" then return '' end -- ignore .lua.old backups
if hist_name == nil or hist_name == "" then return "" end
if hist_name:sub(-4) ~= ".lua" then return "" end -- ignore .lua.old backups
-- 1. select everything included in brackets
local s = string.match(hist_name,"%b[]")
if s == nil or s == '' then return '' end
if s == nil or s == "" then return "" end
-- 2. crop the bracket-sign from both sides
-- 3. and finally replace decorative signs '#' to dir-char '/'
return string.gsub(string.sub(s, 2, -3), "#", "/")
end
function DocSettings:getNameFromHistory(hist_name)
if hist_name == nil or hist_name == '' then return '' end
if hist_name:sub(-4) ~= ".lua" then return '' end -- ignore .lua.old backups
if hist_name == nil or hist_name == "" then return "" end
if hist_name:sub(-4) ~= ".lua" then return "" end -- ignore .lua.old backups
local s = string.match(hist_name, "%b[]")
if s == nil or s == '' then return '' end
if s == nil or s == "" then return "" end
-- at first, search for path length
-- and return the rest of string without 4 last characters (".lua")
return string.sub(hist_name, string.len(s)+2, -5)
end
function DocSettings:getLastSaveTime(doc_path)
local attr = lfs.attributes(self:getSidecarFile(doc_path))
if attr and attr.mode == "file" then
return attr.modification
end
end
function DocSettings:ensureSidecar(sidecar)
if lfs.attributes(sidecar, "mode") ~= "directory" then
lfs.mkdir(sidecar)
function DocSettings:getFileFromHistory(hist_name)
local path = self:getPathFromHistory(hist_name)
if path ~= "" then
local name = self:getNameFromHistory(hist_name)
if name ~= "" then
return ffiutil.joinPath(path, name)
end
end
end
--- Opens a document's individual settings (font, margin, dictionary, etc.)
-- @string docfile path to the document (e.g., `/foo/bar.pdf`)
-- @string doc_path path to the document (e.g., `/foo/bar.pdf`)
-- @treturn DocSettings object
function DocSettings:open(docfile)
--- @todo (zijiehe): Remove history_path, use only sidecar.
function DocSettings:open(doc_path)
-- NOTE: Beware, our new instance is new, but self is still DocSettings!
local new = DocSettings:extend{}
new.history_file = new:getHistoryPath(docfile)
local sidecar = new:getSidecarDir(docfile)
new.sidecar = sidecar
DocSettings:ensureSidecar(sidecar)
-- If there is a file which has a same name as the sidecar directory,
-- or the file system is read-only, we should not waste time to read it.
if lfs.attributes(sidecar, "mode") == "directory" then
-- New sidecar file name is metadata.{file last suffix}.lua.
-- So we can handle two files with only different suffixes.
new.sidecar_file = new:getSidecarFile(docfile)
new.legacy_sidecar_file = sidecar.."/"..
ffiutil.basename(docfile)..".lua"
new.doc_sidecar_dir = new:getSidecarDir(doc_path, "doc")
new.doc_sidecar_file = new:getSidecarFile(doc_path, "doc")
local doc_sidecar_file, legacy_sidecar_file
if lfs.attributes(new.doc_sidecar_dir, "mode") == "directory" then
doc_sidecar_file = new.doc_sidecar_file
legacy_sidecar_file = new.doc_sidecar_dir.."/"..ffiutil.basename(doc_path)..".lua"
end
new.dir_sidecar_dir = new:getSidecarDir(doc_path, "dir")
new.dir_sidecar_file = new:getSidecarFile(doc_path, "dir")
local dir_sidecar_file
if lfs.attributes(new.dir_sidecar_dir, "mode") == "directory" then
dir_sidecar_file = new.dir_sidecar_file
end
local history_file = new:getHistoryPath(doc_path)
-- Candidates list, in order of priority:
local candidates_list = {
-- New sidecar file
new.sidecar_file or "",
-- Backup file of new sidecar file
new.sidecar_file and (new.sidecar_file .. ".old") or "",
-- New sidecar file in doc folder
doc_sidecar_file or "",
-- Backup file of new sidecar file in doc folder
doc_sidecar_file and (doc_sidecar_file..".old") or "",
-- Legacy sidecar file
new.legacy_sidecar_file or "",
legacy_sidecar_file or "",
-- New sidecar file in docsettings folder
dir_sidecar_file or "",
-- Backup file of new sidecar file in docsettings folder
dir_sidecar_file and (dir_sidecar_file..".old") or "",
-- Legacy history folder
new.history_file,
history_file,
-- Backup file in legacy history folder
new.history_file .. ".old",
history_file..".old",
-- Legacy kpdfview setting
docfile..".kpdfview.lua",
doc_path..".kpdfview.lua",
}
-- We get back an array of tables for *existing* candidates, sorted MRU first (insertion order breaks ties).
local candidates = buildCandidates(candidates_list)
local ok, stored, filepath
local ok, stored
for _, t in ipairs(candidates) do
local candidate_path = t.path
-- Ignore empty files
@ -186,7 +192,6 @@ function DocSettings:open(docfile)
-- Ignore empty tables
if ok and next(stored) ~= nil then
logger.dbg("DocSettings: data is read from", candidate_path)
filepath = candidate_path
break
end
end
@ -196,7 +201,6 @@ function DocSettings:open(docfile)
if ok and stored then
new.data = stored
new.candidates = candidates
new.filepath = filepath
else
new.data = {}
end
@ -205,40 +209,33 @@ function DocSettings:open(docfile)
end
--- Serializes settings and writes them to `metadata.lua`.
function DocSettings:flush()
-- write serialized version of the data table into one of
-- i) sidecar directory in the same directory of the document or
-- ii) history directory in root directory of KOReader
if not self.history_file and not self.sidecar_file then
return
end
function DocSettings:flush(data)
-- Depending on the settings, doc_settings are saved to the book folder or
-- to koreader/docsettings folder. The latter is also a fallback for read-only book storage.
local serials = G_reader_settings:readSetting("document_metadata_folder", "doc") == "doc"
and { {self.doc_sidecar_dir, self.doc_sidecar_file},
{self.dir_sidecar_dir, self.dir_sidecar_file}, }
or { {self.dir_sidecar_dir, self.dir_sidecar_file}, }
-- If we can write to sidecar_file, we do not need to write to history_file anymore.
local serials = {}
if self.sidecar_file then
table.insert(serials, self.sidecar_file)
end
if self.history_file then
table.insert(serials, self.history_file)
end
self:ensureSidecar(self.sidecar)
local s_out = dump(self.data)
for _, f in ipairs(serials) do
local s_out = dump(data or self.data, nil, true)
for _, s in ipairs(serials) do
util.makePath(s[1])
local sidecar_file = s[2]
local directory_updated = false
if lfs.attributes(f, "mode") == "file" then
if lfs.attributes(sidecar_file, "mode") == "file" then
-- As an additional safety measure (to the ffiutil.fsync* calls used below),
-- we only backup the file to .old when it has not been modified in the last 60 seconds.
-- This should ensure in the case the fsync calls are not supported
-- that the OS may have itself sync'ed that file content in the meantime.
local mtime = lfs.attributes(f, "modification")
local mtime = lfs.attributes(sidecar_file, "modification")
if mtime < os.time() - 60 then
logger.dbg("DocSettings: Renamed", f, "to", f .. ".old")
os.rename(f, f .. ".old")
logger.dbg("DocSettings: Renamed", sidecar_file, "to", sidecar_file .. ".old")
os.rename(sidecar_file, sidecar_file .. ".old")
directory_updated = true -- fsync directory content too below
end
end
logger.dbg("DocSettings: Writing to", f)
local f_out = io.open(f, "w")
logger.dbg("DocSettings: Writing to", sidecar_file)
local f_out = io.open(sidecar_file, "w")
if f_out ~= nil then
f_out:write("-- we can read Lua syntax here!\nreturn ")
f_out:write(s_out)
@ -246,77 +243,67 @@ function DocSettings:flush()
ffiutil.fsyncOpenedFile(f_out) -- force flush to the storage device
f_out:close()
if self.candidates ~= nil
and G_reader_settings:nilOrFalse("preserve_legacy_docsetting") then
for _, t in ipairs(self.candidates) do
local candidate_path = t.path
if candidate_path ~= f and candidate_path ~= f .. ".old" then
logger.dbg("DocSettings: Removed legacy file", candidate_path)
os.remove(candidate_path)
-- We should not remove sidecar folder, as it may
-- contain Kindle history files.
end
end
end
if directory_updated then
-- Ensure the file renaming is flushed to storage device
ffiutil.fsyncDirectory(f)
ffiutil.fsyncDirectory(sidecar_file)
end
self:purge(false, sidecar_file) -- remove old candidates and empty sidecar folders
break
end
end
end
function DocSettings:getFilePath()
return self.filepath
end
--- Purges (removes) sidecar directory.
function DocSettings:purge(full)
function DocSettings:purge(full, sidecar_to_keep)
-- Remove any of the old ones we may consider as candidates in DocSettings:open()
if self.history_file then
os.remove(self.history_file)
os.remove(self.history_file .. ".old")
if self.candidates then
for _, t in ipairs(self.candidates) do
local candidate_path = t.path
if lfs.attributes(candidate_path, "mode") == "file" then
if (not sidecar_to_keep)
or (candidate_path ~= sidecar_to_keep and candidate_path ~= sidecar_to_keep..".old") then
os.remove(candidate_path)
logger.dbg("DocSettings: purged:", candidate_path)
end
end
end
end
if self.legacy_sidecar_file then
os.remove(self.legacy_sidecar_file)
local function purgeDir(dir, full_purge)
if lfs.attributes(dir, "mode") == "directory" then
if full_purge then
-- Asked to remove all the content of this .sdr directory, whether it's ours or not
ffiutil.purgeDir(dir)
else
-- If the sidecar folder ends up empty, os.remove() can delete it.
-- Otherwise, the following statement has no effect.
os.remove(dir)
end
end
end
if lfs.attributes(self.sidecar, "mode") == "directory" then
if full then
-- Asked to remove all the content of this .sdr directory, whether it's ours or not
ffiutil.purgeDir(self.sidecar)
purgeDir(self.doc_sidecar_dir, full)
purgeDir(self.dir_sidecar_dir, full)
end
--- Updates sidecar info for file rename/copy/move/delete operations.
function DocSettings:update(doc_path, new_doc_path, copy)
if self:hasSidecarFile(doc_path) then
local doc_settings = DocSettings:open(doc_path)
if new_doc_path then
local new_doc_settings = DocSettings:open(new_doc_path)
new_doc_settings:flush(doc_settings.data) -- with current "Book metadata folder" setting
else
-- Only remove the files we know we may have created with our usual names.
for f in lfs.dir(self.sidecar) do
local fullpath = self.sidecar.."/"..f
local to_remove = false
if lfs.attributes(fullpath, "mode") == "file" then
-- Currently, we only create a single file in there,
-- named metadata.suffix.lua (ie. metadata.epub.lua),
-- with possibly backups named metadata.epub.lua.old and
-- metadata.epub.lua.old_dom20180528,
-- so all sharing the same base: self.sidecar_file
if util.stringStartsWith(fullpath, self.sidecar_file) then
to_remove = true
end
end
if to_remove then
os.remove(fullpath)
logger.dbg("DocSettings: purged:", fullpath)
end
local cache_file_path = doc_settings:readSetting("cache_file_path")
if cache_file_path then
os.remove(cache_file_path)
end
-- If the sidecar folder ends up empty, os.remove() can delete it.
-- Otherwise, the following statement has no effect.
os.remove(self.sidecar)
end
if not copy then
doc_settings:purge()
end
end
-- We should have meet the candidate we used and remove it above.
-- But in case we didn't, remove it.
if self.filepath and lfs.attributes(self.filepath, "mode") == "file" then
os.remove(self.filepath)
end
self.data = {}
end
return DocSettings

@ -146,17 +146,13 @@ function ReadHistory:_readLegacyHistory()
local history_updated
local history_dir = DataStorage:getHistoryDir()
for f in lfs.dir(history_dir) do
local path = joinPath(history_dir, f)
if lfs.attributes(path, "mode") == "file" then
path = DocSettings:getPathFromHistory(f)
if path ~= nil and path ~= "" then
local file = DocSettings:getNameFromHistory(f)
if file ~= nil and file ~= "" then
local item_path = joinPath(path, file)
local item_time = lfs.attributes(joinPath(history_dir, f), "modification")
if self:addItem(item_path, item_time, true) then
history_updated = true
end
local legacy_history_file = joinPath(history_dir, f)
if lfs.attributes(legacy_history_file, "mode") == "file" then
local item_path = DocSettings:getFileFromHistory(f)
if item_path then
local item_time = lfs.attributes(legacy_history_file, "modification")
if self:addItem(item_path, item_time, true) then
history_updated = true
end
end
end

@ -526,6 +526,34 @@ common_settings.document = {
-- submenus are filled by menu_order
}
local metadata_folder_str = {
["doc"] = _("book folder"),
["dir"] = "koreader/docsettings",
}
local function genMetadataFolderMenuItem(value)
return {
text = metadata_folder_str[value],
checked_func = function()
return G_reader_settings:readSetting("document_metadata_folder") == value
end,
callback = function()
G_reader_settings:saveSetting("document_metadata_folder", value)
end,
}
end
common_settings.document_metadata_folder = {
text_func = function()
local value = G_reader_settings:readSetting("document_metadata_folder", "doc")
return T(_("Book metadata folder: %1"), metadata_folder_str[value])
end,
sub_item_table = {
genMetadataFolderMenuItem("doc"),
genMetadataFolderMenuItem("dir"),
},
}
common_settings.document_auto_save = {
text_func = function()
local interval = G_reader_settings:readSetting("auto_save_settings_interval_minutes")
@ -552,6 +580,7 @@ common_settings.document_auto_save = {
end,
} or nil,
},
separator = true,
}
common_settings.document_save = {

@ -36,6 +36,7 @@ local order = {
-- end common settings
},
document = {
"document_metadata_folder",
"document_auto_save",
"document_save",
"document_end_action",

@ -79,6 +79,7 @@ local order = {
"status_bar",
},
document = {
"document_metadata_folder",
"document_auto_save",
"document_save",
"document_end_action",

@ -1,16 +1,16 @@
local DataStorage = require("datastorage")
local DocumentRegistry = require("document/documentregistry")
local DocSettings = require("docsettings")
local ReadHistory = require("readhistory")
local ffiutil = require("ffi/util")
local lfs = require("libs/libkoreader-lfs")
local logger = require("logger")
local md5 = require("ffi/sha2").md5
local util = require("util")
local _ = require("gettext")
local T = require("ffi/util").template
local T = ffiutil.template
local MyClipping = {
my_clippings = "/mnt/us/documents/My Clippings.txt",
history_dir = "./history",
}
function MyClipping:new(o)
@ -333,18 +333,22 @@ end
function MyClipping:parseHistory()
local clippings = {}
for f in lfs.dir(self.history_dir) do
self:parseHistoryFile(clippings,
self.history_dir .. "/" .. f,
DocSettings:getPathFromHistory(f) .. "/" ..
DocSettings:getNameFromHistory(f))
local history_dir = DataStorage:getHistoryDir()
for f in lfs.dir(history_dir) do
local legacy_history_file = ffiutil.joinPath(history_dir, f)
if lfs.attributes(legacy_history_file, "mode") == "file" then
local doc_file = DocSettings:getFileFromHistory(f)
if doc_file then
self:parseHistoryFile(clippings, legacy_history_file, doc_file)
end
end
end
for _, item in ipairs(ReadHistory.hist) do
self:parseHistoryFile(clippings,
DocSettings:getSidecarFile(item.file),
item.file)
for _, item in ipairs(require("readhistory").hist) do
if not item.dim then
self:parseHistoryFile(clippings, DocSettings:getSidecarFile(item.file, "doc"), item.file)
self:parseHistoryFile(clippings, DocSettings:getSidecarFile(item.file, "dir"), item.file)
end
end
return clippings
end

@ -1,20 +1,38 @@
describe("docsettings module", function()
local docsettings, lfs, util
local DataStorage, docsettings, docsettings_dir, ffiutil, lfs
setup(function()
require("commonrequire")
DataStorage = require("datastorage")
docsettings = require("docsettings")
ffiutil = require("ffi/util")
lfs = require("libs/libkoreader-lfs")
util = require("ffi/util")
docsettings_dir = DataStorage:getDocSettingsDir()
end)
it("should generate sidecar folder path in book folder (by default)", function()
G_reader_settings:delSetting("document_metadata_folder")
assert.Equals("../../foo.sdr", docsettings:getSidecarDir("../../foo.pdf"))
assert.Equals("/foo/bar.sdr", docsettings:getSidecarDir("/foo/bar.pdf"))
assert.Equals("baz.sdr", docsettings:getSidecarDir("baz.pdf"))
end)
it("should generate sidecar directory path", function()
it("should generate sidecar folder path in book folder", function()
G_reader_settings:saveSetting("document_metadata_folder", "doc")
assert.Equals("../../foo.sdr", docsettings:getSidecarDir("../../foo.pdf"))
assert.Equals("/foo/bar.sdr", docsettings:getSidecarDir("/foo/bar.pdf"))
assert.Equals("baz.sdr", docsettings:getSidecarDir("baz.pdf"))
end)
it("should generate sidecar metadata file", function()
it("should generate sidecar folder path in docsettings folder", function()
G_reader_settings:saveSetting("document_metadata_folder", "dir")
assert.Equals(docsettings_dir.."/foo/bar.sdr", docsettings:getSidecarDir("/foo/bar.pdf"))
assert.Equals(docsettings_dir.."baz.sdr", docsettings:getSidecarDir("baz.pdf"))
end)
it("should generate sidecar metadata file (book folder)", function()
G_reader_settings:saveSetting("document_metadata_folder", "doc")
assert.Equals("../../foo.sdr/metadata.pdf.lua",
docsettings:getSidecarFile("../../foo.pdf"))
assert.Equals("/foo/bar.sdr/metadata.pdf.lua",
@ -23,7 +41,16 @@ describe("docsettings module", function()
docsettings:getSidecarFile("baz.epub"))
end)
it("should generate sidecar metadata file (docsettings folder)", function()
G_reader_settings:saveSetting("document_metadata_folder", "dir")
assert.Equals(docsettings_dir.."/foo/bar.sdr/metadata.pdf.lua",
docsettings:getSidecarFile("/foo/bar.pdf"))
assert.Equals(docsettings_dir.."baz.sdr/metadata.epub.lua",
docsettings:getSidecarFile("baz.epub"))
end)
it("should read legacy history file", function()
G_reader_settings:delSetting("document_metadata_folder")
local file = "file.pdf"
local d = docsettings:open(file)
d:saveSetting("a", "b")
@ -32,15 +59,15 @@ describe("docsettings module", function()
-- Now the sidecar file should be written.
local legacy_files = {
d.history_file,
d.sidecar .. "/file.pdf.lua",
docsettings:getHistoryPath(file),
d.doc_sidecar_dir .. "/file.pdf.lua",
"file.pdf.kpdfview.lua",
}
for _, f in pairs(legacy_files) do
assert.False(os.rename(d.sidecar_file, f) == nil)
for _, f in ipairs(legacy_files) do
assert.False(os.rename(d.doc_sidecar_file, f) == nil)
d = docsettings:open(file)
assert.True(os.remove(d.sidecar_file) == nil)
assert.True(os.remove(d.doc_sidecar_file) == nil)
-- Legacy history files should not be removed before flush has been
-- called.
assert.Equals(lfs.attributes(f, "mode"), "file")
@ -53,7 +80,7 @@ describe("docsettings module", function()
assert.True(os.remove(f) == nil)
end
assert.False(os.remove(d.sidecar_file) == nil)
assert.False(os.remove(d.doc_sidecar_file) == nil)
d:purge()
end)
@ -62,20 +89,20 @@ describe("docsettings module", function()
local d = docsettings:open(file)
local legacy_files = {
d.history_file,
d.sidecar .. "/file.pdf.lua",
docsettings:getHistoryPath(file),
d.doc_sidecar_dir .. "/file.pdf.lua",
"file.pdf.kpdfview.lua",
}
-- docsettings:flush will remove legacy files.
for i, v in pairs(legacy_files) do
for i, v in ipairs(legacy_files) do
d:saveSetting("a", i)
d:flush()
assert.False(os.rename(d.sidecar_file, v.."1") == nil)
assert.False(os.rename(d.doc_sidecar_file, v.."1") == nil)
end
d:close()
for _, v in pairs(legacy_files) do
for _, v in ipairs(legacy_files) do
assert.False(os.rename(v.."1", v) == nil)
end
@ -87,45 +114,46 @@ describe("docsettings module", function()
it("should build correct legacy history path", function()
local file = "/a/b/c--d/c.txt"
local history_path = util.basename(docsettings:getHistoryPath(file))
local history_path = ffiutil.basename(docsettings:getHistoryPath(file))
local path_from_history = docsettings:getPathFromHistory(history_path)
local name_from_history = docsettings:getNameFromHistory(history_path)
assert.is.same(file, path_from_history .. "/" .. name_from_history)
end)
it("should reserve last good file", function()
G_reader_settings:delSetting("document_metadata_folder")
local file = "file.pdf"
local d = docsettings:open(file)
d:saveSetting("a", "a")
d:flush()
-- metadata.pdf.lua should be generated.
assert.Equals("file", lfs.attributes(d.sidecar_file, "mode"))
assert.Equals("file", lfs.attributes(d.doc_sidecar_file, "mode"))
d:flush()
-- metadata.pdf.lua.old should not yet be generated.
assert.are.not_equal("file", lfs.attributes(d.sidecar_file .. ".old", "mode"))
assert.are.not_equal("file", lfs.attributes(d.doc_sidecar_file .. ".old", "mode"))
-- make metadata.pdf.lua older to bypass 60s age needed for .old rotation
local minutes_ago = os.time() - 120
lfs.touch(d.sidecar_file, minutes_ago)
lfs.touch(d.doc_sidecar_file, minutes_ago)
d:close()
-- metadata.pdf.lua and metadata.pdf.lua.old should be generated.
assert.Equals("file", lfs.attributes(d.sidecar_file, "mode"))
assert.Equals("file", lfs.attributes(d.sidecar_file .. ".old", "mode"))
assert.Equals("file", lfs.attributes(d.doc_sidecar_file, "mode"))
assert.Equals("file", lfs.attributes(d.doc_sidecar_file .. ".old", "mode"))
-- write some garbage to sidecar-file.
local f_out = io.open(d.sidecar_file, "w")
local f_out = io.open(d.doc_sidecar_file, "w")
f_out:write("bla bla bla")
f_out:close()
d = docsettings:open(file)
-- metadata.pdf.lua should be removed.
assert.are.not_equal("file", lfs.attributes(d.sidecar_file, "mode"))
assert.Equals("file", lfs.attributes(d.sidecar_file .. ".old", "mode"))
assert.are.not_equal("file", lfs.attributes(d.doc_sidecar_file, "mode"))
assert.Equals("file", lfs.attributes(d.doc_sidecar_file .. ".old", "mode"))
assert.Equals("a", d:readSetting("a"))
d:saveSetting("a", "b")
d:close()
-- metadata.pdf.lua should be generated.
assert.Equals("file", lfs.attributes(d.sidecar_file, "mode"))
assert.Equals("file", lfs.attributes(d.sidecar_file .. ".old", "mode"))
assert.Equals("file", lfs.attributes(d.doc_sidecar_file, "mode"))
assert.Equals("file", lfs.attributes(d.doc_sidecar_file .. ".old", "mode"))
-- The contents in sidecar_file and sidecar_file.old are different.
-- a:b v.s. a:a
@ -133,87 +161,89 @@ describe("docsettings module", function()
-- The content should come from sidecar_file.
assert.Equals("b", d:readSetting("a"))
-- write some garbage to sidecar-file.
f_out = io.open(d.sidecar_file, "w")
f_out = io.open(d.doc_sidecar_file, "w")
f_out:write("bla bla bla")
f_out:close()
-- do not flush the result, open docsettings again.
d = docsettings:open(file)
-- metadata.pdf.lua should be removed.
assert.are.not_equal("file", lfs.attributes(d.sidecar_file, "mode"))
assert.Equals("file", lfs.attributes(d.sidecar_file .. ".old", "mode"))
assert.are.not_equal("file", lfs.attributes(d.doc_sidecar_file, "mode"))
assert.Equals("file", lfs.attributes(d.doc_sidecar_file .. ".old", "mode"))
-- The content should come from sidecar_file.old.
assert.Equals("a", d:readSetting("a"))
d:close()
-- metadata.pdf.lua should be generated.
assert.Equals("file", lfs.attributes(d.sidecar_file, "mode"))
assert.Equals("file", lfs.attributes(d.sidecar_file .. ".old", "mode"))
assert.Equals("file", lfs.attributes(d.doc_sidecar_file, "mode"))
assert.Equals("file", lfs.attributes(d.doc_sidecar_file .. ".old", "mode"))
end)
describe("ignore empty sidecar file", function()
it("should ignore empty file", function()
G_reader_settings:delSetting("document_metadata_folder")
local file = "file.pdf"
local d = docsettings:open(file)
d:saveSetting("a", "a")
d:flush()
-- metadata.pdf.lua should be generated.
assert.Equals("file", lfs.attributes(d.sidecar_file, "mode"))
assert.Equals("file", lfs.attributes(d.doc_sidecar_file, "mode"))
-- make metadata.pdf.lua older to bypass 60s age needed for .old rotation
local minutes_ago = os.time() - 120
lfs.touch(d.sidecar_file, minutes_ago)
lfs.touch(d.doc_sidecar_file, minutes_ago)
d:close()
-- metadata.pdf.lua and metadata.pdf.lua.old should be generated.
assert.Equals("file", lfs.attributes(d.sidecar_file, "mode"))
assert.Equals("file", lfs.attributes(d.sidecar_file .. ".old", "mode"))
assert.Equals("file", lfs.attributes(d.doc_sidecar_file, "mode"))
assert.Equals("file", lfs.attributes(d.doc_sidecar_file .. ".old", "mode"))
-- reset the sidecar_file to an empty file.
local f_out = io.open(d.sidecar_file, "w")
local f_out = io.open(d.doc_sidecar_file, "w")
f_out:close()
d = docsettings:open(file)
-- metadata.pdf.lua should be removed.
assert.are.not_equal("file", lfs.attributes(d.sidecar_file, "mode"))
assert.Equals("file", lfs.attributes(d.sidecar_file .. ".old", "mode"))
assert.are.not_equal("file", lfs.attributes(d.doc_sidecar_file, "mode"))
assert.Equals("file", lfs.attributes(d.doc_sidecar_file .. ".old", "mode"))
assert.Equals("a", d:readSetting("a"))
d:saveSetting("a", "b")
d:close()
-- metadata.pdf.lua should be generated.
assert.Equals("file", lfs.attributes(d.sidecar_file, "mode"))
assert.Equals("file", lfs.attributes(d.sidecar_file .. ".old", "mode"))
assert.Equals("file", lfs.attributes(d.doc_sidecar_file, "mode"))
assert.Equals("file", lfs.attributes(d.doc_sidecar_file .. ".old", "mode"))
-- The contents in sidecar_file and sidecar_file.old are different.
-- a:b v.s. a:a
end)
it("should ignore empty table", function()
G_reader_settings:delSetting("document_metadata_folder")
local file = "file.pdf"
local d = docsettings:open(file)
d:saveSetting("a", "a")
d:flush()
-- metadata.pdf.lua should be generated.
assert.Equals("file", lfs.attributes(d.sidecar_file, "mode"))
assert.Equals("file", lfs.attributes(d.doc_sidecar_file, "mode"))
-- make metadata.pdf.lua older to bypass 60s age needed for .old rotation
local minutes_ago = os.time() - 120
lfs.touch(d.sidecar_file, minutes_ago)
lfs.touch(d.doc_sidecar_file, minutes_ago)
d:close()
-- metadata.pdf.lua and metadata.pdf.lua.old should be generated.
assert.Equals("file", lfs.attributes(d.sidecar_file, "mode"))
assert.Equals("file", lfs.attributes(d.sidecar_file .. ".old", "mode"))
assert.Equals("file", lfs.attributes(d.doc_sidecar_file, "mode"))
assert.Equals("file", lfs.attributes(d.doc_sidecar_file .. ".old", "mode"))
-- reset the sidecar_file to an empty file.
local f_out = io.open(d.sidecar_file, "w")
local f_out = io.open(d.doc_sidecar_file, "w")
f_out:write("{ } ")
f_out:close()
d = docsettings:open(file)
-- metadata.pdf.lua should be removed.
assert.are.not_equal("file", lfs.attributes(d.sidecar_file, "mode"))
assert.Equals("file", lfs.attributes(d.sidecar_file .. ".old", "mode"))
assert.are.not_equal("file", lfs.attributes(d.doc_sidecar_file, "mode"))
assert.Equals("file", lfs.attributes(d.doc_sidecar_file .. ".old", "mode"))
assert.Equals("a", d:readSetting("a"))
d:saveSetting("a", "b")
d:close()
-- metadata.pdf.lua should be generated.
assert.Equals("file", lfs.attributes(d.sidecar_file, "mode"))
assert.Equals("file", lfs.attributes(d.sidecar_file .. ".old", "mode"))
assert.Equals("file", lfs.attributes(d.doc_sidecar_file, "mode"))
assert.Equals("file", lfs.attributes(d.doc_sidecar_file .. ".old", "mode"))
-- The contents in sidecar_file and sidecar_file.old are different.
-- a:b v.s. a:a
end)

Loading…
Cancel
Save