mirror of
https://github.com/koreader/koreader.git
synced 2025-12-13 20:36:53 +01:00
@@ -10,6 +10,7 @@ local DocSettings = require("docsettings")
|
||||
local DocumentRegistry = require("document/documentregistry")
|
||||
local Event = require("ui/event")
|
||||
local FileChooser = require("ui/widget/filechooser")
|
||||
local FileManagerBookInfo = require("apps/filemanager/filemanagerbookinfo")
|
||||
local FileManagerCollection = require("apps/filemanager/filemanagercollection")
|
||||
local FileManagerConverter = require("apps/filemanager/filemanagerconverter")
|
||||
local FileManagerFileSearcher = require("apps/filemanager/filemanagerfilesearcher")
|
||||
@@ -415,6 +416,7 @@ function FileManager:init()
|
||||
|
||||
self:registerModule("menu", self.menu)
|
||||
self:registerModule("history", FileManagerHistory:new{ ui = self })
|
||||
self:registerModule("bookinfo", FileManagerBookInfo:new{ ui = self })
|
||||
self:registerModule("collections", FileManagerCollection:new{ ui = self })
|
||||
self:registerModule("filesearcher", FileManagerFileSearcher:new{ ui = self })
|
||||
self:registerModule("folder_shortcuts", FileManagerShortcuts:new{ ui = self })
|
||||
@@ -879,7 +881,7 @@ function FileManager:pasteHere(file)
|
||||
local function infoCopyFile()
|
||||
if self:copyRecursive(orig_file, dest_path) then
|
||||
if is_file then
|
||||
DocSettings:updateLocation(orig_file, dest_file, true)
|
||||
DocSettings.updateLocation(orig_file, dest_file, true)
|
||||
end
|
||||
return true
|
||||
else
|
||||
@@ -893,7 +895,7 @@ function FileManager:pasteHere(file)
|
||||
local function infoMoveFile()
|
||||
if self:moveFile(orig_file, dest_path) then
|
||||
if is_file then
|
||||
DocSettings:updateLocation(orig_file, dest_file)
|
||||
DocSettings.updateLocation(orig_file, dest_file)
|
||||
ReadHistory:updateItemByPath(orig_file, dest_file) -- (will update "lastfile" if needed)
|
||||
else
|
||||
ReadHistory:updateItemsByPath(orig_file, dest_file)
|
||||
@@ -1018,7 +1020,7 @@ function FileManager:deleteFile(file, is_file)
|
||||
end
|
||||
if ok and not err then
|
||||
if is_file then
|
||||
DocSettings:updateLocation(file)
|
||||
DocSettings.updateLocation(file)
|
||||
ReadHistory:fileDeleted(file)
|
||||
end
|
||||
ReadCollection:removeItemByPath(file, not is_file)
|
||||
@@ -1067,7 +1069,7 @@ function FileManager:renameFile(file, basename, is_file)
|
||||
local function doRenameFile()
|
||||
if self:moveFile(file, dest) then
|
||||
if is_file then
|
||||
DocSettings:updateLocation(file, dest)
|
||||
DocSettings.updateLocation(file, dest)
|
||||
ReadHistory:updateItemByPath(file, dest) -- (will update "lastfile" if needed)
|
||||
else
|
||||
ReadHistory:updateItemsByPath(file, dest)
|
||||
|
||||
@@ -19,6 +19,8 @@ local filemanagerutil = require("apps/filemanager/filemanagerutil")
|
||||
local lfs = require("libs/libkoreader-lfs")
|
||||
local util = require("util")
|
||||
local _ = require("gettext")
|
||||
local N_ = _.ngettext
|
||||
local T = require("ffi/util").template
|
||||
|
||||
local BookInfo = WidgetContainer:extend{
|
||||
props = {
|
||||
@@ -44,7 +46,7 @@ local BookInfo = WidgetContainer:extend{
|
||||
}
|
||||
|
||||
function BookInfo:init()
|
||||
if self.ui then -- only for Reader menu
|
||||
if self.document then -- only for Reader menu
|
||||
self.ui.menu:registerToMainMenu(self)
|
||||
end
|
||||
end
|
||||
@@ -83,7 +85,7 @@ function BookInfo:show(file, book_props)
|
||||
book_props = BookInfo.getDocProps(file, book_props)
|
||||
end
|
||||
-- cover image
|
||||
self.custom_book_cover = DocSettings:findCoverFile(file)
|
||||
self.custom_book_cover = DocSettings:findCustomCoverFile(file)
|
||||
local key_text = self.prop_text["cover"]
|
||||
if self.custom_book_cover then
|
||||
key_text = "\u{F040} " .. key_text
|
||||
@@ -99,9 +101,9 @@ function BookInfo:show(file, book_props)
|
||||
})
|
||||
-- metadata
|
||||
local custom_props
|
||||
local custom_metadata_file = DocSettings:getCustomMetadataFile(file)
|
||||
local custom_metadata_file = DocSettings:findCustomMetadataFile(file)
|
||||
if custom_metadata_file then
|
||||
self.custom_doc_settings = DocSettings:openCustomMetadata(custom_metadata_file)
|
||||
self.custom_doc_settings = DocSettings.openSettingsFile(custom_metadata_file)
|
||||
custom_props = self.custom_doc_settings:readSetting("custom_props")
|
||||
end
|
||||
local values_lang
|
||||
@@ -173,17 +175,17 @@ function BookInfo:show(file, book_props)
|
||||
end
|
||||
|
||||
function BookInfo.getCustomProp(prop_key, filepath)
|
||||
local custom_metadata_file = DocSettings:getCustomMetadataFile(filepath)
|
||||
local custom_metadata_file = DocSettings:findCustomMetadataFile(filepath)
|
||||
return custom_metadata_file
|
||||
and DocSettings:openCustomMetadata(custom_metadata_file):readSetting("custom_props")[prop_key]
|
||||
and DocSettings.openSettingsFile(custom_metadata_file):readSetting("custom_props")[prop_key]
|
||||
end
|
||||
|
||||
-- Returns extended and customized metadata.
|
||||
function BookInfo.extendProps(original_props, filepath)
|
||||
-- do not customize if filepath is not passed (eg from covermenu)
|
||||
local custom_metadata_file = filepath and DocSettings:getCustomMetadataFile(filepath)
|
||||
local custom_metadata_file = filepath and DocSettings:findCustomMetadataFile(filepath)
|
||||
local custom_props = custom_metadata_file
|
||||
and DocSettings:openCustomMetadata(custom_metadata_file):readSetting("custom_props") or {}
|
||||
and DocSettings.openSettingsFile(custom_metadata_file):readSetting("custom_props") or {}
|
||||
original_props = original_props or {}
|
||||
|
||||
local props = {}
|
||||
@@ -226,9 +228,9 @@ function BookInfo.getDocProps(file, book_props, no_open_document)
|
||||
-- If still no book_props (book never opened or empty "stats"),
|
||||
-- but custom metadata exists, it has a copy of original doc_props
|
||||
if not book_props then
|
||||
local custom_metadata_file = DocSettings:getCustomMetadataFile(file)
|
||||
local custom_metadata_file = DocSettings:findCustomMetadataFile(file)
|
||||
if custom_metadata_file then
|
||||
book_props = DocSettings:openCustomMetadata(custom_metadata_file):readSetting("doc_props")
|
||||
book_props = DocSettings.openSettingsFile(custom_metadata_file):readSetting("doc_props")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -320,7 +322,7 @@ function BookInfo:getCoverImage(doc, file, force_orig)
|
||||
local cover_bb
|
||||
-- check for a custom cover (orig cover is forcibly requested in "Book information" only)
|
||||
if not force_orig then
|
||||
local custom_cover = DocSettings:findCoverFile(file or (doc and doc.file))
|
||||
local custom_cover = DocSettings:findCustomCoverFile(file or (doc and doc.file))
|
||||
if custom_cover then
|
||||
local cover_doc = DocumentRegistry:openDocument(custom_cover)
|
||||
if cover_doc then
|
||||
@@ -348,8 +350,8 @@ function BookInfo:getCoverImage(doc, file, force_orig)
|
||||
end
|
||||
|
||||
function BookInfo:updateBookInfo(file, book_props, prop_updated, prop_value_old)
|
||||
if prop_updated == "cover" and self.ui then
|
||||
self.ui.doc_settings:getCoverFile(true) -- reset cover file cache
|
||||
if self.document and prop_updated == "cover" then
|
||||
self.ui.doc_settings:getCustomCoverFile(true) -- reset cover file cache
|
||||
end
|
||||
self.prop_updated = {
|
||||
filepath = file,
|
||||
@@ -361,10 +363,10 @@ function BookInfo:updateBookInfo(file, book_props, prop_updated, prop_value_old)
|
||||
self:show(file, book_props)
|
||||
end
|
||||
|
||||
function BookInfo:setCustomBookCover(file, book_props)
|
||||
function BookInfo:setCustomCover(file, book_props)
|
||||
if self.custom_book_cover then -- reset custom cover
|
||||
if os.remove(self.custom_book_cover) then
|
||||
DocSettings:removeSidecarDir(file, util.splitFilePathName(self.custom_book_cover))
|
||||
DocSettings.removeSidecarDir(util.splitFilePathName(self.custom_book_cover))
|
||||
self:updateBookInfo(file, book_props, "cover")
|
||||
end
|
||||
else -- choose an image and set custom cover
|
||||
@@ -386,28 +388,27 @@ end
|
||||
|
||||
function BookInfo:setCustomMetadata(file, book_props, prop_key, prop_value)
|
||||
-- in file
|
||||
local custom_doc_settings, custom_props, display_title
|
||||
local custom_doc_settings, custom_props, display_title, no_custom_metadata
|
||||
if self.custom_doc_settings then
|
||||
custom_doc_settings = self.custom_doc_settings
|
||||
custom_props = custom_doc_settings:readSetting("custom_props")
|
||||
else -- no custom metadata file, create new
|
||||
custom_doc_settings = DocSettings:openCustomMetadata()
|
||||
custom_props = {}
|
||||
custom_doc_settings = DocSettings.openSettingsFile()
|
||||
display_title = book_props.display_title -- backup
|
||||
book_props.display_title = nil
|
||||
custom_doc_settings:saveSetting("doc_props", book_props) -- save a copy of original props
|
||||
end
|
||||
custom_props = custom_doc_settings:readSetting("custom_props", {})
|
||||
local prop_value_old = custom_props[prop_key] or book_props[prop_key]
|
||||
custom_props[prop_key] = prop_value -- nil when resetting a custom prop
|
||||
if next(custom_props) == nil then -- no more custom metadata
|
||||
os.remove(custom_doc_settings.custom_metadata_file)
|
||||
DocSettings:removeSidecarDir(file, util.splitFilePathName(custom_doc_settings.custom_metadata_file))
|
||||
os.remove(custom_doc_settings.sidecar_file)
|
||||
DocSettings.removeSidecarDir(util.splitFilePathName(custom_doc_settings.sidecar_file))
|
||||
no_custom_metadata = true
|
||||
else
|
||||
if book_props.pages then -- keep a copy of original 'pages' up to date
|
||||
local original_props = custom_doc_settings:readSetting("doc_props")
|
||||
original_props.pages = book_props.pages
|
||||
end
|
||||
custom_doc_settings:saveSetting("custom_props", custom_props)
|
||||
custom_doc_settings:flushCustomMetadata(file)
|
||||
end
|
||||
book_props.display_title = book_props.display_title or display_title -- restore
|
||||
@@ -417,11 +418,13 @@ function BookInfo:setCustomMetadata(file, book_props, prop_key, prop_value)
|
||||
if prop_key == "title" then -- generate when resetting the customized title and original is empty
|
||||
book_props.display_title = book_props.title or filemanagerutil.splitFileNameType(file)
|
||||
end
|
||||
local ui = self.ui or require("apps/reader/readerui").instance
|
||||
if ui and ui.document and ui.document.file == file then -- currently opened document
|
||||
ui.doc_props[prop_key] = prop_value
|
||||
if self.document and self.document.file == file then -- currently opened document
|
||||
self.ui.doc_props[prop_key] = prop_value
|
||||
if prop_key == "title" then
|
||||
ui.doc_props.display_title = book_props.display_title
|
||||
self.ui.doc_props.display_title = book_props.display_title
|
||||
end
|
||||
if no_custom_metadata then
|
||||
self.ui.doc_settings:getCustomMetadataFile(true) -- reset metadata file cache
|
||||
end
|
||||
end
|
||||
self:updateBookInfo(file, book_props, prop_key, prop_value_old)
|
||||
@@ -517,7 +520,7 @@ function BookInfo:showCustomDialog(file, book_props, prop_key)
|
||||
ok_callback = function()
|
||||
UIManager:close(button_dialog)
|
||||
if prop_is_cover then
|
||||
self:setCustomBookCover(file, book_props)
|
||||
self:setCustomCover(file, book_props)
|
||||
else
|
||||
self:setCustomMetadata(file, book_props, prop_key)
|
||||
end
|
||||
@@ -532,7 +535,7 @@ function BookInfo:showCustomDialog(file, book_props, prop_key)
|
||||
callback = function()
|
||||
UIManager:close(button_dialog)
|
||||
if prop_is_cover then
|
||||
self:setCustomBookCover(file, book_props)
|
||||
self:setCustomCover(file, book_props)
|
||||
else
|
||||
self:showCustomEditDialog(file, book_props, prop_key)
|
||||
end
|
||||
@@ -548,4 +551,98 @@ function BookInfo:showCustomDialog(file, book_props, prop_key)
|
||||
UIManager:show(button_dialog)
|
||||
end
|
||||
|
||||
function BookInfo:moveBookMetadata()
|
||||
-- called by filemanagermenu only
|
||||
local file_chooser = self.ui.file_chooser
|
||||
local function scanPath()
|
||||
local sys_folders = { -- do not scan sys_folders
|
||||
["/dev"] = true,
|
||||
["/proc"] = true,
|
||||
["/sys"] = true,
|
||||
}
|
||||
local books_to_move = {}
|
||||
local dirs = { file_chooser.path }
|
||||
while #dirs ~= 0 do
|
||||
local new_dirs = {}
|
||||
for _, d in ipairs(dirs) do
|
||||
local ok, iter, dir_obj = pcall(lfs.dir, d)
|
||||
if ok then
|
||||
for f in iter, dir_obj do
|
||||
local fullpath = "/" .. f
|
||||
if d ~= "/" then
|
||||
fullpath = d .. fullpath
|
||||
end
|
||||
local attributes = lfs.attributes(fullpath) or {}
|
||||
if attributes.mode == "directory" and f ~= "." and f ~= ".."
|
||||
and file_chooser:show_dir(f) and not sys_folders[fullpath] then
|
||||
table.insert(new_dirs, fullpath)
|
||||
elseif attributes.mode == "file" and not util.stringStartsWith(f, "._")
|
||||
and file_chooser:show_file(f)
|
||||
and DocSettings.isSidecarFileNotInPreferredLocation(fullpath) then
|
||||
table.insert(books_to_move, fullpath)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
dirs = new_dirs
|
||||
end
|
||||
return books_to_move
|
||||
end
|
||||
UIManager:show(ConfirmBox:new{
|
||||
text = _("Scan books in current folder and subfolders for their metadata location?"),
|
||||
ok_text = _("Scan"),
|
||||
ok_callback = function()
|
||||
local books_to_move = scanPath()
|
||||
local books_to_move_nb = #books_to_move
|
||||
if books_to_move_nb == 0 then
|
||||
UIManager:show(InfoMessage:new{
|
||||
text = _("No books with metadata not in your preferred location found."),
|
||||
})
|
||||
else
|
||||
UIManager:show(ConfirmBox:new{
|
||||
text = T(N_("1 book with metadata not in your preferred location found.",
|
||||
"%1 books with metadata not in your preferred location found.",
|
||||
books_to_move_nb), books_to_move_nb) .. "\n" ..
|
||||
_("Move book metadata to your preferred location?"),
|
||||
ok_text = _("Move"),
|
||||
ok_callback = function()
|
||||
UIManager:close(self.menu_container)
|
||||
for _, book in ipairs(books_to_move) do
|
||||
DocSettings.updateLocation(book, book)
|
||||
end
|
||||
file_chooser:refreshPath()
|
||||
end,
|
||||
})
|
||||
end
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
function BookInfo.showBooksWithHashBasedMetadata()
|
||||
local header = T(_("Hash-based metadata has been saved in %1 for the following documents. Hash-based storage may slow down file browser navigation in large directories. Thus, if not using hash-based metadata storage, it is recommended to open the associated documents in KOReader to automatically migrate their metadata to the preferred storage location, or to delete %1, which will speed up file browser navigation."),
|
||||
DocSettings.getSidecarStorage("hash"))
|
||||
local file_info = { header .. "\n" }
|
||||
local sdrs = DocSettings.findSidecarFilesInHashLocation()
|
||||
for i, sdr in ipairs(sdrs) do
|
||||
local sidecar_file, custom_metadata_file = unpack(sdr)
|
||||
local doc_settings = DocSettings.openSettingsFile(sidecar_file)
|
||||
local doc_props = doc_settings:readSetting("doc_props")
|
||||
local custom_props = custom_metadata_file
|
||||
and DocSettings.openSettingsFile(custom_metadata_file):readSetting("custom_props") or {}
|
||||
local doc_path = doc_settings:readSetting("doc_path")
|
||||
local title = custom_props.title or doc_props.title or filemanagerutil.splitFileNameType(doc_path)
|
||||
local author = custom_props.authors or doc_props.authors or _("N/A")
|
||||
doc_path = lfs.attributes(doc_path, "mode") == "file" and doc_path or _("N/A")
|
||||
local text = T(_("%1. Title: %2; Author: %3\nDocument: %4"), i, title, author, doc_path)
|
||||
table.insert(file_info, text)
|
||||
end
|
||||
local doc_nb = #file_info - 1
|
||||
UIManager:show(TextViewer:new{
|
||||
title = T(N_("1 document with hash-based metadata", "%1 documents with hash-based metadata", doc_nb), doc_nb),
|
||||
title_multilines = true,
|
||||
justified = false,
|
||||
text = table.concat(file_info, "\n"),
|
||||
})
|
||||
end
|
||||
|
||||
return BookInfo
|
||||
|
||||
@@ -15,7 +15,6 @@ local lfs = require("libs/libkoreader-lfs")
|
||||
local logger = require("logger")
|
||||
local util = require("util")
|
||||
local _ = require("gettext")
|
||||
local N_ = _.ngettext
|
||||
local T = FFIUtil.template
|
||||
|
||||
local FileManagerMenu = InputContainer:extend{
|
||||
@@ -489,7 +488,7 @@ To:
|
||||
text = _("Move book metadata"),
|
||||
keep_menu_open = true,
|
||||
callback = function()
|
||||
self:moveBookMetadata()
|
||||
self.ui.bookinfo:moveBookMetadata()
|
||||
end,
|
||||
}
|
||||
|
||||
@@ -930,74 +929,6 @@ function FileManagerMenu:getStartWithMenuTable()
|
||||
}
|
||||
end
|
||||
|
||||
function FileManagerMenu:moveBookMetadata()
|
||||
local DocSettings = require("docsettings")
|
||||
local FileChooser = self.ui.file_chooser
|
||||
local function scanPath()
|
||||
local sys_folders = { -- do not scan sys_folders
|
||||
["/dev"] = true,
|
||||
["/proc"] = true,
|
||||
["/sys"] = true,
|
||||
}
|
||||
local books_to_move = {}
|
||||
local dirs = {FileChooser.path}
|
||||
while #dirs ~= 0 do
|
||||
local new_dirs = {}
|
||||
for _, d in ipairs(dirs) do
|
||||
local ok, iter, dir_obj = pcall(lfs.dir, d)
|
||||
if ok then
|
||||
for f in iter, dir_obj do
|
||||
local fullpath = "/" .. f
|
||||
if d ~= "/" then
|
||||
fullpath = d .. fullpath
|
||||
end
|
||||
local attributes = lfs.attributes(fullpath) or {}
|
||||
if attributes.mode == "directory" and f ~= "." and f ~= ".."
|
||||
and FileChooser:show_dir(f) and not sys_folders[fullpath] then
|
||||
table.insert(new_dirs, fullpath)
|
||||
elseif attributes.mode == "file" and not util.stringStartsWith(f, "._")
|
||||
and FileChooser:show_file(f) and DocSettings:hasSidecarFile(fullpath)
|
||||
and lfs.attributes(DocSettings:getSidecarFile(fullpath), "mode") ~= "file" then
|
||||
table.insert(books_to_move, fullpath)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
dirs = new_dirs
|
||||
end
|
||||
return books_to_move
|
||||
end
|
||||
UIManager:show(ConfirmBox:new{
|
||||
text = _("Scan books in current folder and subfolders for their metadata location?"),
|
||||
ok_text = _("Scan"),
|
||||
ok_callback = function()
|
||||
local books_to_move = scanPath()
|
||||
local books_to_move_nb = #books_to_move
|
||||
if books_to_move_nb == 0 then
|
||||
local InfoMessage = require("ui/widget/infomessage")
|
||||
UIManager:show(InfoMessage:new{
|
||||
text = _("No books with metadata not in your preferred location found."),
|
||||
})
|
||||
else
|
||||
UIManager:show(ConfirmBox:new{
|
||||
text = T(N_("1 book with metadata not in your preferred location found.",
|
||||
"%1 books with metadata not in your preferred location found.",
|
||||
books_to_move_nb), books_to_move_nb) .. "\n" ..
|
||||
_("Move book metadata to your preferred location?"),
|
||||
ok_text = _("Move"),
|
||||
ok_callback = function()
|
||||
UIManager:close(self.menu_container)
|
||||
for _, book in ipairs(books_to_move) do
|
||||
DocSettings:updateLocation(book, book)
|
||||
end
|
||||
FileChooser:refreshPath()
|
||||
end,
|
||||
})
|
||||
end
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
function FileManagerMenu:exitOrRestart(callback, force)
|
||||
UIManager:close(self.menu_container)
|
||||
|
||||
|
||||
@@ -142,9 +142,9 @@ end
|
||||
function filemanagerutil.genResetSettingsButton(file, caller_callback, button_disabled)
|
||||
file = ffiutil.realpath(file) or file
|
||||
local has_sidecar_file = DocSettings:hasSidecarFile(file)
|
||||
local custom_cover_file = DocSettings:findCoverFile(file)
|
||||
local custom_cover_file = DocSettings:findCustomCoverFile(file)
|
||||
local has_custom_cover_file = custom_cover_file and true or false
|
||||
local custom_metadata_file = DocSettings:getCustomMetadataFile(file)
|
||||
local custom_metadata_file = DocSettings:findCustomMetadataFile(file)
|
||||
local has_custom_metadata_file = custom_metadata_file and true or false
|
||||
return {
|
||||
text = _("Reset"),
|
||||
@@ -162,8 +162,8 @@ function filemanagerutil.genResetSettingsButton(file, caller_callback, button_di
|
||||
ok_callback = function()
|
||||
local data_to_purge = {
|
||||
doc_settings = check_button_settings.checked,
|
||||
custom_cover_file = check_button_cover.checked,
|
||||
custom_metadata_file = check_button_metadata.checked,
|
||||
custom_cover_file = check_button_cover.checked and custom_cover_file,
|
||||
custom_metadata_file = check_button_metadata.checked and custom_metadata_file,
|
||||
}
|
||||
DocSettings:open(file):purge(nil, data_to_purge)
|
||||
if data_to_purge.custom_cover_file or data_to_purge.custom_metadata_file then
|
||||
|
||||
@@ -19,12 +19,28 @@ local DOCSETTINGS_DIR = DataStorage:getDocSettingsDir()
|
||||
local DOCSETTINGS_HASH_DIR = DataStorage:getDocSettingsHashDir()
|
||||
local custom_metadata_filename = "custom_metadata.lua"
|
||||
|
||||
local is_hash_location_enabled
|
||||
function DocSettings.getSidecarStorage(location)
|
||||
if location == "dir" then
|
||||
return DOCSETTINGS_DIR
|
||||
elseif location == "hash" then
|
||||
return DOCSETTINGS_HASH_DIR
|
||||
end
|
||||
end
|
||||
|
||||
local function isDir(dir)
|
||||
return lfs.attributes(dir, "mode") == "directory"
|
||||
end
|
||||
|
||||
local function isFile(file)
|
||||
return lfs.attributes(file, "mode") == "file"
|
||||
end
|
||||
|
||||
local doc_hash_cache = {}
|
||||
local is_hash_location_enabled
|
||||
|
||||
function DocSettings.isHashLocationEnabled()
|
||||
if is_hash_location_enabled == nil then
|
||||
is_hash_location_enabled = lfs.attributes(DOCSETTINGS_HASH_DIR, "mode") == "directory"
|
||||
is_hash_location_enabled = isDir(DOCSETTINGS_HASH_DIR)
|
||||
end
|
||||
return is_hash_location_enabled
|
||||
end
|
||||
@@ -33,14 +49,13 @@ function DocSettings.setIsHashLocationEnabled(value)
|
||||
is_hash_location_enabled = value
|
||||
end
|
||||
|
||||
|
||||
local function buildCandidates(list)
|
||||
local candidates = {}
|
||||
local previous_entry_exists = false
|
||||
|
||||
for i, file_path in ipairs(list) do
|
||||
-- Ignore missing files.
|
||||
if file_path ~= "" and lfs.attributes(file_path, "mode") == "file" then
|
||||
if file_path ~= "" and isFile(file_path) then
|
||||
local mtime = lfs.attributes(file_path, "modification")
|
||||
-- NOTE: Extra trickery: if we're inserting a "backup" file, and its primary buddy exists,
|
||||
-- make sure it will *never* sort ahead of it by using the same mtime.
|
||||
@@ -81,6 +96,18 @@ local function buildCandidates(list)
|
||||
return candidates
|
||||
end
|
||||
|
||||
local function getOrderedLocationCandidates()
|
||||
local preferred_location = G_reader_settings:readSetting("document_metadata_folder", "doc")
|
||||
if preferred_location == "hash" then
|
||||
return { "hash", "doc", "dir" }
|
||||
end
|
||||
local candidates = preferred_location == "doc" and { "doc", "dir" } or { "dir", "doc" }
|
||||
if DocSettings.isHashLocationEnabled() then
|
||||
table.insert(candidates, "hash")
|
||||
end
|
||||
return candidates
|
||||
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`)
|
||||
@@ -111,52 +138,44 @@ function DocSettings:getSidecarDir(doc_path, force_location)
|
||||
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, 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(".*%.(.+)") or ""
|
||||
return self:getSidecarDir(doc_path, force_location) .. "/metadata." .. suffix .. ".lua"
|
||||
function DocSettings.getSidecarFilename(doc_path)
|
||||
local suffix = doc_path:match(".*%.(.+)") or "_"
|
||||
return "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 self:getDocSidecarFile(doc_path) and true or false
|
||||
return self:findSidecarFile(doc_path) and true or false
|
||||
end
|
||||
|
||||
--- Returns path of `metadata.lua` file if it exists, or nil.
|
||||
-- @string doc_path path to the document (e.g., `/foo/bar.pdf`)
|
||||
-- @bool no_legacy set to true to skip check of the legacy history file
|
||||
-- @treturn string
|
||||
function DocSettings:getDocSidecarFile(doc_path, no_legacy)
|
||||
local sidecar_file = self:getSidecarFile(doc_path, "doc")
|
||||
if lfs.attributes(sidecar_file, "mode") == "file" then
|
||||
return sidecar_file
|
||||
end
|
||||
sidecar_file = self:getSidecarFile(doc_path, "dir")
|
||||
if lfs.attributes(sidecar_file, "mode") == "file" then
|
||||
return sidecar_file
|
||||
end
|
||||
-- Calculate partial hash and check for hash-based files only if there are files to check
|
||||
if DocSettings.isHashLocationEnabled() then
|
||||
sidecar_file = self:getSidecarFile(doc_path, "hash")
|
||||
if lfs.attributes(sidecar_file, "mode") == "file" then
|
||||
return sidecar_file
|
||||
function DocSettings:findSidecarFile(doc_path, no_legacy)
|
||||
local sidecar_filename = DocSettings.getSidecarFilename(doc_path)
|
||||
local sidecar_file
|
||||
for _, location in ipairs(getOrderedLocationCandidates()) do
|
||||
sidecar_file = self:getSidecarDir(doc_path, location) .. "/" .. sidecar_filename
|
||||
if isFile(sidecar_file) then
|
||||
return sidecar_file, location
|
||||
end
|
||||
end
|
||||
if not no_legacy then
|
||||
sidecar_file = self:getHistoryPath(doc_path)
|
||||
if lfs.attributes(sidecar_file, "mode") == "file" then
|
||||
return sidecar_file
|
||||
if isFile(sidecar_file) then
|
||||
return sidecar_file, "hist" -- for isSidecarFileNotInPreferredLocation() used in moveBookMetadata
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function DocSettings.isSidecarFileNotInPreferredLocation(doc_path)
|
||||
local _, location = DocSettings:findSidecarFile(doc_path)
|
||||
return location and location ~= G_reader_settings:readSetting("document_metadata_folder", "doc")
|
||||
end
|
||||
|
||||
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"
|
||||
@@ -193,20 +212,6 @@ function DocSettings:getFileFromHistory(hist_name)
|
||||
end
|
||||
end
|
||||
|
||||
--- Returns the directory and full filepath of a hash-ID-based sidecar metadata store
|
||||
-- @string doc_path path to the document (e.g., `/foo/bar.pdf`)
|
||||
function DocSettings:getSidecarHashDirAndFilepath(doc_path)
|
||||
-- Getting PDF ID from trailer via mupdf has not been implemented - everything uses partial MD5
|
||||
local path = self:getSidecarDir(doc_path, "hash")
|
||||
local filetype = doc_path:match(".+%.(%w+)$")
|
||||
if not filetype or filetype == "" then
|
||||
return "", ""
|
||||
end
|
||||
local hash_file = "metadata." .. filetype .. ".lua"
|
||||
local hash_filepath = path .. "/" .. hash_file
|
||||
return path, hash_filepath
|
||||
end
|
||||
|
||||
--- Opens a document's individual settings (font, margin, dictionary, etc.)
|
||||
-- @string doc_path path to the document (e.g., `/foo/bar.pdf`)
|
||||
-- @treturn DocSettings object
|
||||
@@ -214,29 +219,26 @@ function DocSettings:open(doc_path)
|
||||
-- NOTE: Beware, our new instance is new, but self is still DocSettings!
|
||||
local new = DocSettings:extend{}
|
||||
|
||||
new.sidecar_filename = DocSettings.getSidecarFilename(doc_path)
|
||||
|
||||
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
|
||||
if isDir(new.doc_sidecar_dir) then
|
||||
doc_sidecar_file = new.doc_sidecar_dir .. "/" .. new.sidecar_filename
|
||||
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
|
||||
if isDir(new.dir_sidecar_dir) then
|
||||
dir_sidecar_file = new.dir_sidecar_dir .. "/" .. new.sidecar_filename
|
||||
end
|
||||
local hash_sidecar_file
|
||||
if DocSettings.isHashLocationEnabled() then
|
||||
new.hash_sidecar_dir = new:getSidecarDir(doc_path, "hash")
|
||||
hash_sidecar_file = new.hash_sidecar_dir .. "/" .. new.sidecar_filename
|
||||
end
|
||||
local history_file = new:getHistoryPath(doc_path)
|
||||
|
||||
local hash_sidecar_dir, hash_sidecar_file
|
||||
if DocSettings.isHashLocationEnabled() then
|
||||
hash_sidecar_dir, hash_sidecar_file =
|
||||
new:getSidecarHashDirAndFilepath(doc_path)
|
||||
new.hash_sidecar_dir = hash_sidecar_dir
|
||||
new.hash_sidecar_file = hash_sidecar_file
|
||||
end
|
||||
|
||||
-- Candidates list, in order of priority:
|
||||
local candidates_list = {
|
||||
-- New sidecar file in doc folder
|
||||
@@ -249,10 +251,10 @@ function DocSettings:open(doc_path)
|
||||
dir_sidecar_file or "",
|
||||
-- Backup file of new sidecar file in docsettings folder
|
||||
dir_sidecar_file and (dir_sidecar_file .. ".old") or "",
|
||||
-- Hash or PDF fingerprint-based sidecar file lookup
|
||||
-- New sidecar file in hashdocsettings folder
|
||||
hash_sidecar_file or "",
|
||||
-- Backup file of hash or PDF fingerprint-based sidecar file lookup
|
||||
hash_sidecar_file and (new.hash_sidecar_file .. ".old") or "",
|
||||
-- Backup file of new sidecar file in hashdocsettings folder
|
||||
hash_sidecar_file and (hash_sidecar_file .. ".old") or "",
|
||||
-- Legacy history folder
|
||||
history_file,
|
||||
-- Backup file in legacy history folder
|
||||
@@ -290,52 +292,69 @@ function DocSettings:open(doc_path)
|
||||
return new
|
||||
end
|
||||
|
||||
--- Serializes settings and writes them to `metadata.lua`.
|
||||
function DocSettings:flush(data, no_custom_metadata)
|
||||
-- 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
|
||||
local preferred_metdata_storage = G_reader_settings:readSetting("document_metadata_folder", "doc")
|
||||
if preferred_metdata_storage == "doc" then
|
||||
serials = { {self.doc_sidecar_dir, self.doc_sidecar_file},
|
||||
{self.dir_sidecar_dir, self.dir_sidecar_file}, }
|
||||
elseif preferred_metdata_storage == "dir" then
|
||||
serials = { {self.dir_sidecar_dir, self.dir_sidecar_file}, }
|
||||
elseif preferred_metdata_storage == "hash" then
|
||||
if self.hash_sidecar_dir == nil or self.hash_sidecar_file == nil then
|
||||
self.hash_sidecar_dir, self.hash_sidecar_file =
|
||||
self:getSidecarHashDirAndFilepath(self.data.doc_path)
|
||||
--- Light version of open(). Opens a sidecar file or a custom metadata file.
|
||||
-- Returned object cannot be used to save changes to the sidecar file (flush()).
|
||||
-- Must be used to save changes to the custom metadata file (flushCustomMetadata()).
|
||||
function DocSettings.openSettingsFile(sidecar_file)
|
||||
local new = DocSettings:extend{}
|
||||
local ok, stored
|
||||
if sidecar_file then
|
||||
ok, stored = pcall(dofile, sidecar_file)
|
||||
end
|
||||
serials = { {self.hash_sidecar_dir, self.hash_sidecar_file } }
|
||||
if ok and next(stored) ~= nil then
|
||||
new.data = stored
|
||||
else
|
||||
new.data = {}
|
||||
end
|
||||
new.sidecar_file = sidecar_file
|
||||
return new
|
||||
end
|
||||
|
||||
local s_out = dump(data or self.data, nil, true)
|
||||
for _, s in ipairs(serials) do
|
||||
local sidecar_dir, sidecar_file = unpack(s)
|
||||
--- Serializes settings and writes them to `metadata.lua`.
|
||||
function DocSettings:flush(data, no_custom_metadata)
|
||||
data = data or self.data
|
||||
local sidecar_dirs
|
||||
local preferred_location = G_reader_settings:readSetting("document_metadata_folder", "doc")
|
||||
if preferred_location == "doc" then
|
||||
sidecar_dirs = { self.doc_sidecar_dir, self.dir_sidecar_dir } -- fallback for read-only book storage
|
||||
elseif preferred_location == "dir" then
|
||||
sidecar_dirs = { self.dir_sidecar_dir }
|
||||
elseif preferred_location == "hash" then
|
||||
if self.hash_sidecar_dir == nil then
|
||||
self.hash_sidecar_dir = self:getSidecarDir(data.doc_path, "hash")
|
||||
end
|
||||
sidecar_dirs = { self.hash_sidecar_dir }
|
||||
end
|
||||
|
||||
local ser_data = dump(data, nil, true)
|
||||
for _, sidecar_dir in ipairs(sidecar_dirs) do
|
||||
local sidecar_dir_slash = sidecar_dir .. "/"
|
||||
local sidecar_file = sidecar_dir_slash .. self.sidecar_filename
|
||||
util.makePath(sidecar_dir)
|
||||
logger.dbg("DocSettings: Writing to", sidecar_file)
|
||||
local directory_updated = LuaSettings:backup(sidecar_file)
|
||||
if util.writeToFile(s_out, sidecar_file, true, true, directory_updated) then
|
||||
local directory_updated = LuaSettings:backup(sidecar_file) -- "*.old"
|
||||
if util.writeToFile(ser_data, sidecar_file, true, true, directory_updated) then
|
||||
-- move custom cover file and custom metadata file to the metadata file location
|
||||
if not no_custom_metadata then
|
||||
local metadata_file, filepath, filename
|
||||
-- custom cover
|
||||
metadata_file = self:getCoverFile()
|
||||
metadata_file = self:getCustomCoverFile()
|
||||
if metadata_file then
|
||||
filepath, filename = util.splitFilePathName(metadata_file)
|
||||
if filepath ~= sidecar_dir .. "/" then
|
||||
ffiutil.copyFile(metadata_file, sidecar_dir .. "/" .. filename)
|
||||
if filepath ~= sidecar_dir_slash then
|
||||
ffiutil.copyFile(metadata_file, sidecar_dir_slash .. filename)
|
||||
os.remove(metadata_file)
|
||||
self:getCoverFile(true) -- reset cache
|
||||
self:getCustomCoverFile(true) -- reset cache
|
||||
end
|
||||
end
|
||||
-- custom metadata
|
||||
metadata_file = self:getCustomMetadataFile()
|
||||
if metadata_file then
|
||||
filepath, filename = util.splitFilePathName(metadata_file)
|
||||
if filepath ~= sidecar_dir .. "/" then
|
||||
ffiutil.copyFile(metadata_file, sidecar_dir .. "/" .. filename)
|
||||
if filepath ~= sidecar_dir_slash then
|
||||
ffiutil.copyFile(metadata_file, sidecar_dir_slash .. filename)
|
||||
os.remove(metadata_file)
|
||||
self:getCustomMetadataFile(true) -- reset cache
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -351,10 +370,10 @@ end
|
||||
function DocSettings:purge(sidecar_to_keep, data_to_purge)
|
||||
local custom_cover_file, custom_metadata_file
|
||||
if sidecar_to_keep == nil then
|
||||
custom_cover_file = self:getCoverFile()
|
||||
custom_cover_file = self:getCustomCoverFile()
|
||||
custom_metadata_file = self:getCustomMetadataFile()
|
||||
end
|
||||
if data_to_purge == nil then
|
||||
if data_to_purge == nil then -- purge all
|
||||
data_to_purge = {
|
||||
doc_settings = true,
|
||||
custom_cover_file = custom_cover_file,
|
||||
@@ -366,7 +385,7 @@ function DocSettings:purge(sidecar_to_keep, data_to_purge)
|
||||
if data_to_purge.doc_settings and self.candidates then
|
||||
for _, t in ipairs(self.candidates) do
|
||||
local candidate_path = t.path
|
||||
if lfs.attributes(candidate_path, "mode") == "file" then
|
||||
if isFile(candidate_path) then
|
||||
if (not sidecar_to_keep)
|
||||
or (candidate_path ~= sidecar_to_keep and candidate_path ~= sidecar_to_keep .. ".old") then
|
||||
os.remove(candidate_path)
|
||||
@@ -376,105 +395,111 @@ function DocSettings:purge(sidecar_to_keep, data_to_purge)
|
||||
end
|
||||
end
|
||||
|
||||
-- Remove custom
|
||||
if data_to_purge.custom_cover_file then
|
||||
os.remove(data_to_purge.custom_cover_file)
|
||||
self:getCoverFile(true) -- reset cache
|
||||
self:getCustomCoverFile(true) -- reset cache
|
||||
end
|
||||
if data_to_purge.custom_metadata_file then
|
||||
os.remove(data_to_purge.custom_metadata_file)
|
||||
self:getCustomMetadataFile(true) -- reset cache
|
||||
end
|
||||
|
||||
-- Remove empty sidecar dirs
|
||||
if data_to_purge.doc_settings or data_to_purge.custom_cover_file or data_to_purge.custom_metadata_file then
|
||||
-- remove sidecar dirs iff empty
|
||||
if lfs.attributes(self.doc_sidecar_dir, "mode") == "directory" then
|
||||
os.remove(self.doc_sidecar_dir) -- keep parent folders
|
||||
end
|
||||
if lfs.attributes(self.dir_sidecar_dir, "mode") == "directory" then
|
||||
util.removePath(self.dir_sidecar_dir) -- remove empty parent folders
|
||||
end
|
||||
if self.hash_sidecar_dir and lfs.attributes(self.hash_sidecar_dir, "mode") == "directory" then
|
||||
util.removePath(self.hash_sidecar_dir) -- remove empty parent folders
|
||||
for _, dir in ipairs({ self.doc_sidecar_dir, self.dir_sidecar_dir, self.hash_sidecar_dir }) do
|
||||
DocSettings.removeSidecarDir(dir)
|
||||
end
|
||||
end
|
||||
|
||||
DocSettings.setIsHashLocationEnabled(nil) -- reset this in case last hash book is purged
|
||||
end
|
||||
|
||||
--- Removes empty sidecar dir.
|
||||
function DocSettings:removeSidecarDir(doc_path, sidecar_dir)
|
||||
if sidecar_dir == self:getSidecarDir(doc_path, "doc") then
|
||||
os.remove(sidecar_dir)
|
||||
--- Removes sidecar dir iff empty.
|
||||
function DocSettings.removeSidecarDir(dir)
|
||||
if dir and isDir(dir) then
|
||||
if dir:match("^"..DOCSETTINGS_DIR) or dir:match("^"..DOCSETTINGS_HASH_DIR) then
|
||||
util.removePath(dir) -- remove empty parent folders
|
||||
else
|
||||
util.removePath(sidecar_dir)
|
||||
os.remove(dir) -- keep parent folders
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Updates sdr location for file rename/copy/move/delete operations.
|
||||
function DocSettings:updateLocation(doc_path, new_doc_path, copy)
|
||||
local doc_settings, new_sidecar_dir, cover_file
|
||||
if G_reader_settings:readSetting("document_metadata_folder") == "hash" then
|
||||
-- none of these operations (except delete) changes the hash -> no location change
|
||||
if not new_doc_path then
|
||||
doc_settings = DocSettings:open(doc_path)
|
||||
local cache_file_path = doc_settings:readSetting("cache_file_path")
|
||||
if cache_file_path then os.remove(cache_file_path) end
|
||||
cover_file = doc_settings:getCoverFile()
|
||||
doc_settings:purge()
|
||||
end
|
||||
else
|
||||
-- update metadata
|
||||
if DocSettings:hasSidecarFile(doc_path) then
|
||||
doc_settings = DocSettings:open(doc_path)
|
||||
if new_doc_path then
|
||||
function DocSettings.updateLocation(doc_path, new_doc_path, copy)
|
||||
local has_sidecar_file = DocSettings:hasSidecarFile(doc_path)
|
||||
local custom_cover_file = DocSettings:findCustomCoverFile(doc_path)
|
||||
local custom_metadata_file = DocSettings:findCustomMetadataFile(doc_path)
|
||||
if not (has_sidecar_file or custom_cover_file or custom_metadata_file) then return end
|
||||
|
||||
local doc_settings = DocSettings:open(doc_path)
|
||||
local do_purge
|
||||
|
||||
if new_doc_path then -- copy/rename/move
|
||||
if G_reader_settings:readSetting("document_metadata_folder") ~= "hash" then -- keep hash location unchanged
|
||||
local new_sidecar_dir
|
||||
if has_sidecar_file then
|
||||
local new_doc_settings = DocSettings:open(new_doc_path)
|
||||
-- save doc settings to the new location, no custom metadata yet
|
||||
new_sidecar_dir = new_doc_settings:flush(doc_settings.data, true)
|
||||
else
|
||||
doc_settings.data.doc_path = new_doc_path
|
||||
new_sidecar_dir = new_doc_settings:flush(doc_settings.data, true) -- without custom
|
||||
end
|
||||
if not new_sidecar_dir then
|
||||
new_sidecar_dir = DocSettings:getSidecarDir(new_doc_path)
|
||||
util.makePath(new_sidecar_dir)
|
||||
end
|
||||
if custom_cover_file then
|
||||
local _, filename = util.splitFilePathName(custom_cover_file)
|
||||
ffiutil.copyFile(custom_cover_file, new_sidecar_dir .. "/" .. filename)
|
||||
end
|
||||
if custom_metadata_file then
|
||||
ffiutil.copyFile(custom_metadata_file, new_sidecar_dir .. "/" .. custom_metadata_filename)
|
||||
end
|
||||
do_purge = not copy
|
||||
end
|
||||
else -- delete
|
||||
if has_sidecar_file then
|
||||
local cache_file_path = doc_settings:readSetting("cache_file_path")
|
||||
if cache_file_path then
|
||||
os.remove(cache_file_path)
|
||||
end
|
||||
end
|
||||
do_purge = true
|
||||
end
|
||||
|
||||
-- update custom metadata
|
||||
if not doc_settings then
|
||||
doc_settings = DocSettings:open(doc_path)
|
||||
end
|
||||
cover_file = doc_settings:getCoverFile()
|
||||
if new_doc_path then
|
||||
-- custom cover
|
||||
if cover_file then
|
||||
if not new_sidecar_dir then
|
||||
new_sidecar_dir = DocSettings:getSidecarDir(new_doc_path)
|
||||
util.makePath(new_sidecar_dir)
|
||||
end
|
||||
local _, filename = util.splitFilePathName(cover_file)
|
||||
ffiutil.copyFile(cover_file, new_sidecar_dir .. "/" .. filename)
|
||||
end
|
||||
-- custom metadata
|
||||
local metadata_file = self:getCustomMetadataFile(doc_path)
|
||||
if metadata_file then
|
||||
if not new_sidecar_dir then
|
||||
new_sidecar_dir = DocSettings:getSidecarDir(new_doc_path)
|
||||
util.makePath(new_sidecar_dir)
|
||||
end
|
||||
ffiutil.copyFile(metadata_file, new_sidecar_dir .. "/" .. custom_metadata_filename)
|
||||
end
|
||||
end
|
||||
|
||||
if not copy then
|
||||
if do_purge then
|
||||
doc_settings.custom_cover_file = custom_cover_file -- cache
|
||||
doc_settings.custom_metadata_file = custom_metadata_file -- cache
|
||||
doc_settings:purge()
|
||||
end
|
||||
end
|
||||
|
||||
if cover_file then -- after purge because purge uses cover file cache
|
||||
doc_settings:getCoverFile(true) -- reset cache
|
||||
-- custom section
|
||||
|
||||
function DocSettings:getCustomLocationCandidates(doc_path)
|
||||
local sidecar_dir
|
||||
local sidecar_file = self:findSidecarFile(doc_path, true) -- new locations only
|
||||
if sidecar_file then -- book was opened, write custom metadata to its sidecar dir
|
||||
sidecar_dir = util.splitFilePathName(sidecar_file):sub(1, -2)
|
||||
return { sidecar_dir }
|
||||
end
|
||||
-- new book, create sidecar dir in accordance with sdr location setting
|
||||
local preferred_location = G_reader_settings:readSetting("document_metadata_folder", "doc")
|
||||
if preferred_location ~= "hash" then
|
||||
sidecar_dir = self:getSidecarDir(doc_path, "dir")
|
||||
if preferred_location == "doc" then
|
||||
local doc_sidecar_dir = self:getSidecarDir(doc_path, "doc")
|
||||
return { doc_sidecar_dir, sidecar_dir } -- fallback for read-only book storage
|
||||
end
|
||||
else -- "hash"
|
||||
sidecar_dir = self:getSidecarDir(doc_path, "hash")
|
||||
end
|
||||
return { sidecar_dir }
|
||||
end
|
||||
|
||||
-- custom cover
|
||||
|
||||
local function findCoverFileInDir(dir)
|
||||
local function findCustomCoverFileInDir(dir)
|
||||
local ok, iter, dir_obj = pcall(lfs.dir, dir)
|
||||
if ok then
|
||||
for f in iter, dir_obj do
|
||||
@@ -486,57 +511,30 @@ local function findCoverFileInDir(dir)
|
||||
end
|
||||
|
||||
--- Returns path to book custom cover file if it exists, or nil.
|
||||
function DocSettings:findCoverFile(doc_path)
|
||||
function DocSettings:findCustomCoverFile(doc_path)
|
||||
doc_path = doc_path or self.data.doc_path
|
||||
local location = G_reader_settings:readSetting("document_metadata_folder", "doc")
|
||||
for _, location in ipairs(getOrderedLocationCandidates()) do
|
||||
local sidecar_dir = self:getSidecarDir(doc_path, location)
|
||||
local cover_file = findCoverFileInDir(sidecar_dir)
|
||||
if cover_file then return cover_file end
|
||||
local candidates = {"doc", "dir"}
|
||||
if DocSettings.isHashLocationEnabled() then
|
||||
table.insert(candidates, "hash")
|
||||
end
|
||||
for _, mode in ipairs(candidates) do
|
||||
if mode ~= location then
|
||||
sidecar_dir = self:getSidecarDir(doc_path, mode)
|
||||
cover_file = findCoverFileInDir(sidecar_dir)
|
||||
if cover_file then return cover_file end
|
||||
local custom_cover_file = findCustomCoverFileInDir(sidecar_dir)
|
||||
if custom_cover_file then
|
||||
return custom_cover_file
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function DocSettings:getCoverFile(reset_cache)
|
||||
function DocSettings:getCustomCoverFile(reset_cache)
|
||||
if reset_cache then
|
||||
self.cover_file = nil
|
||||
self.custom_cover_file = nil
|
||||
else
|
||||
if self.cover_file == nil then -- fill empty cache
|
||||
self.cover_file = self:findCoverFile() or false
|
||||
if self.custom_cover_file == nil then -- fill empty cache
|
||||
self.custom_cover_file = self:findCustomCoverFile() or false
|
||||
end
|
||||
return self.cover_file
|
||||
return self.custom_cover_file
|
||||
end
|
||||
end
|
||||
|
||||
function DocSettings:getCustomCandidateSidecarDirs(doc_path)
|
||||
local sidecar_file = self:getDocSidecarFile(doc_path, true) -- new locations only
|
||||
if sidecar_file then -- book was opened, write custom metadata to its sidecar dir
|
||||
local sidecar_dir = util.splitFilePathName(sidecar_file):sub(1, -2)
|
||||
return { sidecar_dir }
|
||||
end
|
||||
-- new book, create sidecar dir in accordance with sdr location setting
|
||||
local dir_sidecar_dir = self:getSidecarDir(doc_path, "dir")
|
||||
local preferred_metadata_storage = G_reader_settings:readSetting("document_metadata_folder", "doc")
|
||||
if preferred_metadata_storage == "doc" then
|
||||
local doc_sidecar_dir = self:getSidecarDir(doc_path, "doc")
|
||||
return { doc_sidecar_dir, dir_sidecar_dir } -- fallback in case of readonly book storage
|
||||
elseif preferred_metadata_storage == "hash" then
|
||||
local hash_sidecar_dir = self:getSidecarDir(doc_path, "hash")
|
||||
return { hash_sidecar_dir }
|
||||
end
|
||||
return { dir_sidecar_dir }
|
||||
end
|
||||
|
||||
function DocSettings:flushCustomCover(doc_path, image_file)
|
||||
local sidecar_dirs = self:getCustomCandidateSidecarDirs(doc_path)
|
||||
local sidecar_dirs = self:getCustomLocationCandidates(doc_path)
|
||||
local new_cover_filename = "/cover." .. util.getFileNameSuffix(image_file):lower()
|
||||
for _, sidecar_dir in ipairs(sidecar_dirs) do
|
||||
util.makePath(sidecar_dir)
|
||||
@@ -550,130 +548,57 @@ end
|
||||
-- custom metadata
|
||||
|
||||
--- Returns path to book custom metadata file if it exists, or nil.
|
||||
function DocSettings:getCustomMetadataFile(doc_path)
|
||||
function DocSettings:findCustomMetadataFile(doc_path)
|
||||
doc_path = doc_path or self.data.doc_path
|
||||
|
||||
local candidates = {"doc", "dir"}
|
||||
if DocSettings.isHashLocationEnabled() then
|
||||
table.insert(candidates, "hash")
|
||||
end
|
||||
for _, mode in ipairs(candidates) do
|
||||
local file = self:getSidecarDir(doc_path, mode) .. "/" .. custom_metadata_filename
|
||||
if lfs.attributes(file, "mode") == "file" then
|
||||
return file
|
||||
for _, location in ipairs(getOrderedLocationCandidates()) do
|
||||
local sidecar_dir = self:getSidecarDir(doc_path, location)
|
||||
local custom_metadata_file = sidecar_dir .. "/" .. custom_metadata_filename
|
||||
if isFile(custom_metadata_file) then
|
||||
return custom_metadata_file
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function DocSettings:openCustomMetadata(custom_metadata_file)
|
||||
local new = DocSettings:extend{}
|
||||
local ok, stored
|
||||
if custom_metadata_file then
|
||||
ok, stored = pcall(dofile, custom_metadata_file)
|
||||
end
|
||||
if ok and next(stored) ~= nil then
|
||||
new.data = stored
|
||||
function DocSettings:getCustomMetadataFile(reset_cache)
|
||||
if reset_cache then
|
||||
self.custom_metadata_file = nil
|
||||
else
|
||||
new.data = {}
|
||||
if self.custom_metadata_file == nil then -- fill empty cache
|
||||
self.custom_metadata_file = self:findCustomMetadataFile() or false
|
||||
end
|
||||
return self.custom_metadata_file
|
||||
end
|
||||
new.custom_metadata_file = custom_metadata_file
|
||||
return new
|
||||
end
|
||||
|
||||
function DocSettings:flushCustomMetadata(doc_path)
|
||||
local sidecar_dirs = self:getCustomCandidateSidecarDirs(doc_path)
|
||||
local new_sidecar_dir
|
||||
local sidecar_dirs = self:getCustomLocationCandidates(doc_path)
|
||||
local s_out = dump(self.data, nil, true)
|
||||
for _, sidecar_dir in ipairs(sidecar_dirs) do
|
||||
util.makePath(sidecar_dir)
|
||||
if util.writeToFile(s_out, sidecar_dir .. "/" .. custom_metadata_filename, true, true) then
|
||||
new_sidecar_dir = sidecar_dir .. "/"
|
||||
break
|
||||
end
|
||||
end
|
||||
-- remove old custom metadata file if it was in alternative location
|
||||
if self.custom_metadata_file then
|
||||
local old_sidecar_dir = util.splitFilePathName(self.custom_metadata_file)
|
||||
if old_sidecar_dir ~= new_sidecar_dir then
|
||||
os.remove(self.custom_metadata_file)
|
||||
self:removeSidecarDir(doc_path, old_sidecar_dir)
|
||||
local new_metadata_file = sidecar_dir .. "/" .. custom_metadata_filename
|
||||
if util.writeToFile(s_out, new_metadata_file, true, true) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- hash-based SDR storage
|
||||
local function getSdrsInDir(path)
|
||||
-- Get all the metadata.filetype.lua files under directory path.
|
||||
-- Derived from readerdictionary.getIfosInDir()
|
||||
local sdrs = {}
|
||||
local ok, iter, dir_obj = pcall(lfs.dir, path)
|
||||
if ok then
|
||||
for name in iter, dir_obj do
|
||||
if name ~= "." and name ~= ".." then
|
||||
local fullpath = path .. "/" .. name
|
||||
local attributes = lfs.attributes(fullpath)
|
||||
if attributes ~= nil then
|
||||
if attributes.mode == "directory" then
|
||||
local dirifos = getSdrsInDir(fullpath) -- recurse
|
||||
for _, ifo in pairs(dirifos) do
|
||||
table.insert(sdrs, ifo)
|
||||
end
|
||||
elseif name:match("metadata%..+%.lua$") then
|
||||
table.insert(sdrs, fullpath)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return sdrs
|
||||
end
|
||||
-- "hash" section
|
||||
|
||||
function DocSettings.getHashDirSdrInfos()
|
||||
local sdrs = getSdrsInDir(DOCSETTINGS_HASH_DIR)
|
||||
local title_author_strs = {}
|
||||
for _, sdr in ipairs(sdrs) do
|
||||
-- Ignore empty files
|
||||
if lfs.attributes(sdr, "size") > 0 then
|
||||
local ok, stored
|
||||
ok, stored = pcall(dofile, sdr)
|
||||
-- Ignore empty tables
|
||||
if ok and next(stored) ~= nil then
|
||||
local info_str, custom_authors
|
||||
local sdr_path = sdr:sub(1, sdr:match(".*/()") - 1) -- SDR path
|
||||
local custom_metadata_file = sdr_path .. custom_metadata_filename
|
||||
if custom_metadata_file then
|
||||
local custom = DocSettings:openCustomMetadata(custom_metadata_file)
|
||||
local custom_props = custom:readSetting("custom_props")
|
||||
if custom_props then
|
||||
if custom_props.title then info_str = custom_props.title end
|
||||
if custom_props.authors then custom_authors = custom_props.authors end
|
||||
-- Returns the list of pairs {sidecar_file, custom_metadata_file}.
|
||||
function DocSettings.findSidecarFilesInHashLocation()
|
||||
local res = {}
|
||||
local callback = function(fullpath, name)
|
||||
if name:match("metadata%..+%.lua$") then
|
||||
local sdr = { fullpath }
|
||||
local custom_metadata_file = fullpath:gsub(name, custom_metadata_filename)
|
||||
if isFile(custom_metadata_file) then
|
||||
table.insert(sdr, custom_metadata_file)
|
||||
end
|
||||
table.insert(res, sdr)
|
||||
end
|
||||
end
|
||||
if not info_str then info_str = stored.doc_props.title end
|
||||
if not info_str then info_str = "untitled document" end
|
||||
if custom_authors then
|
||||
info_str = info_str .. ", author: " .. custom_authors
|
||||
elseif stored.doc_props.authors then
|
||||
info_str = info_str .. ", author: " .. stored.doc_props.authors
|
||||
end
|
||||
if stored.stats then
|
||||
if stored.stats.highlights > 0 then
|
||||
info_str = info_str .. ", highlights: " .. stored.stats.highlights
|
||||
end
|
||||
if stored.stats.notes > 0 then
|
||||
info_str = info_str .. ", notes: " .. stored.stats.notes
|
||||
end
|
||||
end
|
||||
info_str = info_str .. ", path: " .. sdr:sub(sdr:find("/", 3) + 1)
|
||||
table.insert(title_author_strs, info_str)
|
||||
else
|
||||
table.insert(title_author_strs, "error " .. sdr)
|
||||
end
|
||||
else
|
||||
table.insert(title_author_strs, "zero-size file " .. sdr)
|
||||
end
|
||||
end
|
||||
return title_author_strs
|
||||
util.findFiles(DOCSETTINGS_HASH_DIR, callback)
|
||||
return res
|
||||
end
|
||||
|
||||
return DocSettings
|
||||
|
||||
@@ -149,7 +149,7 @@ function CreDocument:init()
|
||||
self.flows = {}
|
||||
self.page_in_flow = {}
|
||||
|
||||
local file_type = string.lower(string.match(self.file, ".+%.([^.]+)"))
|
||||
local file_type = string.lower(string.match(self.file, ".+%.([^.]+)") or "")
|
||||
if file_type == "zip" then
|
||||
-- NuPogodi, 20.05.12: read the content of zip-file
|
||||
-- and return extention of the 1st file
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
local DataStorage = require("datastorage")
|
||||
local DateTimeWidget = require("ui/widget/datetimewidget")
|
||||
local Device = require("device")
|
||||
local DocSettings = require("docsettings")
|
||||
local Event = require("ui/event")
|
||||
local FileManagerBookInfo = require("apps/filemanager/filemanagerbookinfo")
|
||||
local InfoMessage = require("ui/widget/infomessage")
|
||||
local Language = require("ui/language")
|
||||
local NetworkMgr = require("ui/network/manager")
|
||||
local PowerD = Device:getPowerDevice()
|
||||
local UIManager = require("ui/uimanager")
|
||||
local DocSettings = require("docsettings")
|
||||
local _ = require("gettext")
|
||||
local N_ = _.ngettext
|
||||
local C_ = _.pgettext
|
||||
@@ -542,22 +542,22 @@ common_settings.document = {
|
||||
|
||||
local metadata_folder_str = {
|
||||
["doc"] = _("book folder"),
|
||||
["dir"] = DataStorage:getDocSettingsDir(),
|
||||
["hash"] = DataStorage:getDocSettingsHashDir()
|
||||
["dir"] = DocSettings.getSidecarStorage("dir"),
|
||||
["hash"] = DocSettings.getSidecarStorage("hash"),
|
||||
}
|
||||
|
||||
local metadata_folder_help_header = T(_([[Book view settings, reading progress, highlights, bookmarks and notes (collectively known as metadata) are stored in a separate folder named <book-filename>.sdr (".sdr" meaning "sidecar").
|
||||
local metadata_folder_help_table = {
|
||||
_("Book view settings, reading progress, highlights, bookmarks and notes (collectively known as metadata) are stored in a separate folder named <book-filename>.sdr (\".sdr\" meaning \"sidecar\")."),
|
||||
"",
|
||||
_("You can decide between three locations/methods where these will be saved:"),
|
||||
_(" - alongside the book file itself (the long time default): sdr folders will be visible when you browse your library directories with another file browser or from your computer, which may clutter your vision of your library. But this allows you to move them along when you reorganize your library, and also survives any renaming of parent directories. Also, if you perform directory synchronization or backups, your settings will be part of them."),
|
||||
T(_(" - all in %1: sdr folders will only be visible and used by KOReader, and won't clutter your vision of your library directories with another file browser or from your computer. But any reorganisation of your library (directories or filename moves and renamings) may result in KOReader not finding your previous settings for these books. These settings won't be part of any synchronization or backups of your library."), metadata_folder_str.dir),
|
||||
T(_(" - all inside %1 as hashes: sdr folders are identified not by filepath/filename but by partial MD5 hash, allowing you to rename, move, and copy documents outside of KOReader without sdr folder clutter while keeping them linked to their metadata. However, any file modifications such as writing highlights into PDFs or downloading from calibre may change the hash, and thus lose their linked metadata. Calculating file hashes may also slow down file browser navigation. This option may suit users with multiple copies of documents across different devices and directories."), metadata_folder_str.hash),
|
||||
}
|
||||
local metadata_folder_help_text = table.concat(metadata_folder_help_table, "\n")
|
||||
|
||||
You can decide between three locations/methods where these will be saved:]]))
|
||||
local metadata_folder_help_doc = T(_(" - alongside the book file itself (the long time default): sdr folders will be visible when you browse your library directories with another file browser or from your computer, which may clutter your vision of your library. But this allows you to move them along when you reorganize your library, and also survives any renaming of parent directories. Also, if you perform directory synchronization or backups, your settings will be part of them."))
|
||||
local metadata_folder_help_dir = T(_(" - all in %1: sdr folders will only be visible and used by KOReader, and won't clutter your vision of your library directories with another file browser or from your computer. But any reorganisation of your library (directories or filename moves and renamings) may result in KOReader not finding your previous settings for these books. These settings won't be part of any synchronization or backups of your library."), DataStorage:getDocSettingsDir())
|
||||
local metadata_folder_help_hash = T(_(" - all inside %1 as hashes: sdr folders are identified not by filepath/filename but by partial MD5 hash, allowing you to rename, move, and copy documents outside of KOReader without sdr folder clutter while keeping them linked to their metadata. However, any file modifications such as writing highlights into PDFs or downloading from calibre may change the hash, and thus lose their linked metadata. Calculating file hashes may also slow down file browser navigation. This option may suit users with multiple copies of documents across different devices and directories."), DataStorage:getDocSettingsHashDir())
|
||||
local metadata_folder_help_text = metadata_folder_help_header .. "\n" .. metadata_folder_help_doc .. "\n" .. metadata_folder_help_dir .. "\n" .. metadata_folder_help_hash
|
||||
|
||||
local hash_filemod_warn = T(_([[%1 requires calculating partial file hashes of documents which may slow down file browser navigation. Any file modifications (such as embedding annotations into PDF files or downloading from calibre) may change the partial hash, thereby losing track of any highlights, bookmarks, and progress data. Embedding PDF annotations is currently set to "%s" and can be disabled at (⚙ → Document → Save Document (write highlights into PDF)).]]), DataStorage:getDocSettingsHashDir())
|
||||
local leaving_hash_sdr_warn = T(_("Warning: You currently have documents with hash-based metadata. Until this metadata is moved by opening those documents, or deleted, file browser navigation may remain slower."))
|
||||
local hash_metadata_file_list_header = T(_([[
|
||||
Hash-based metadata has been saved in %1 for the following documents. Hash-based storage may slow down file browser navigation in large directories. Thus, if not using hash-based metadata storage, it is recommended to open the associated documents in KOReader to automatically migrate their metadata to the preferred storage location, or to delete %1, which will speed up file browser navigation.]]), DataStorage:getDocSettingsHashDir())
|
||||
local hash_filemod_warn = T(_("%1 requires calculating partial file hashes of documents which may slow down file browser navigation. Any file modifications (such as embedding annotations into PDF files or downloading from calibre) may change the partial hash, thereby losing track of any highlights, bookmarks, and progress data. Embedding PDF annotations is currently set to \"%s\" and can be disabled at (⚙ → Document → Save Document (write highlights into PDF))."), metadata_folder_str.hash)
|
||||
local leaving_hash_sdr_warn = _("Warning: You currently have documents with hash-based metadata. Until this metadata is moved by opening those documents, or deleted, file browser navigation may remain slower.")
|
||||
|
||||
local function genMetadataFolderMenuItem(value)
|
||||
return {
|
||||
@@ -574,7 +574,7 @@ local function genMetadataFolderMenuItem(value)
|
||||
local save_document_setting = G_reader_settings:readSetting("save_document")
|
||||
UIManager:show(InfoMessage:new{ text = string.format(hash_filemod_warn, save_document_setting), icon = "notice-warning" })
|
||||
else
|
||||
DocSettings.setIsHashLocationEnabled(nil) -- setting to nil will let it reset itself appropriately
|
||||
DocSettings.setIsHashLocationEnabled(nil) -- reset
|
||||
if DocSettings.isHashLocationEnabled() then
|
||||
UIManager:show(InfoMessage:new{ text = leaving_hash_sdr_warn, icon = "notice-warning" })
|
||||
end
|
||||
@@ -604,7 +604,7 @@ common_settings.document_metadata_location = {
|
||||
genMetadataFolderMenuItem("doc"),
|
||||
genMetadataFolderMenuItem("dir"),
|
||||
genMetadataFolderMenuItem("hash"),
|
||||
{ -- hash-based metadata count / TextViewer
|
||||
{
|
||||
text_func = function()
|
||||
local hash_text = _("Show documents with hash-based metadata")
|
||||
local no_hash_text = _("No documents with hash-based metadata")
|
||||
@@ -621,18 +621,7 @@ common_settings.document_metadata_location = {
|
||||
return DocSettings.isHashLocationEnabled()
|
||||
end,
|
||||
callback = function()
|
||||
local hash_file_infos = DocSettings.getHashDirSdrInfos()
|
||||
local book_info_items = {}
|
||||
for i, file_info in ipairs(hash_file_infos) do
|
||||
table.insert(book_info_items, table.concat({"\n", i, ". ", file_info}))
|
||||
end
|
||||
local book_info_str = table.concat(book_info_items)
|
||||
UIManager:show(require("ui/widget/textviewer"):new{
|
||||
title = T(N_("1 document with hash-based metadata", "%1 documents with hash-based metadata", #hash_file_infos), #hash_file_infos),
|
||||
title_multilines = true,
|
||||
justified = false,
|
||||
text = hash_metadata_file_list_header .. book_info_str,
|
||||
})
|
||||
FileManagerBookInfo.showBooksWithHashBasedMetadata()
|
||||
end,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
describe("docsettings module", function()
|
||||
local DataStorage, docsettings, docsettings_dir, ffiutil, lfs
|
||||
local getSidecarFile = function(doc_path)
|
||||
return docsettings:getSidecarDir(doc_path).."/"..docsettings.getSidecarFilename(doc_path)
|
||||
end
|
||||
|
||||
setup(function()
|
||||
require("commonrequire")
|
||||
@@ -33,20 +36,15 @@ describe("docsettings module", function()
|
||||
|
||||
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",
|
||||
docsettings:getSidecarFile("/foo/bar.pdf"))
|
||||
assert.Equals("baz.sdr/metadata.epub.lua",
|
||||
docsettings:getSidecarFile("baz.epub"))
|
||||
assert.Equals("../../foo.sdr/metadata.pdf.lua", getSidecarFile("../../foo.pdf"))
|
||||
assert.Equals("/foo/bar.sdr/metadata.pdf.lua", getSidecarFile("/foo/bar.pdf"))
|
||||
assert.Equals("baz.sdr/metadata.epub.lua", 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"))
|
||||
assert.Equals(docsettings_dir.."/foo/bar.sdr/metadata.pdf.lua", getSidecarFile("/foo/bar.pdf"))
|
||||
assert.Equals(docsettings_dir.."baz.sdr/metadata.epub.lua", getSidecarFile("baz.epub"))
|
||||
end)
|
||||
|
||||
it("should read legacy history file", function()
|
||||
@@ -65,9 +63,9 @@ describe("docsettings module", function()
|
||||
}
|
||||
|
||||
for _, f in ipairs(legacy_files) do
|
||||
assert.False(os.rename(d.doc_sidecar_file, f) == nil)
|
||||
assert.False(os.rename(d.doc_sidecar_dir.."/"..d.sidecar_filename, f) == nil)
|
||||
d = docsettings:open(file)
|
||||
assert.True(os.remove(d.doc_sidecar_file) == nil)
|
||||
assert.True(os.remove(d.doc_sidecar_dir.."/"..d.sidecar_filename) == nil)
|
||||
-- Legacy history files should not be removed before flush has been
|
||||
-- called.
|
||||
assert.Equals(lfs.attributes(f, "mode"), "file")
|
||||
@@ -80,7 +78,7 @@ describe("docsettings module", function()
|
||||
assert.True(os.remove(f) == nil)
|
||||
end
|
||||
|
||||
assert.False(os.remove(d.doc_sidecar_file) == nil)
|
||||
assert.False(os.remove(d.doc_sidecar_dir.."/"..d.sidecar_filename) == nil)
|
||||
d:purge()
|
||||
end)
|
||||
|
||||
@@ -98,7 +96,7 @@ describe("docsettings module", function()
|
||||
for i, v in ipairs(legacy_files) do
|
||||
d:saveSetting("a", i)
|
||||
d:flush()
|
||||
assert.False(os.rename(d.doc_sidecar_file, v.."1") == nil)
|
||||
assert.False(os.rename(d.doc_sidecar_dir.."/"..d.sidecar_filename, v.."1") == nil)
|
||||
end
|
||||
|
||||
d:close()
|
||||
@@ -127,33 +125,33 @@ describe("docsettings module", function()
|
||||
d:saveSetting("a", "a")
|
||||
d:flush()
|
||||
-- metadata.pdf.lua should be generated.
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_file, "mode"))
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_dir.."/"..d.sidecar_filename, "mode"))
|
||||
d:flush()
|
||||
-- metadata.pdf.lua.old should not yet be generated.
|
||||
assert.are.not_equal("file", lfs.attributes(d.doc_sidecar_file .. ".old", "mode"))
|
||||
assert.are.not_equal("file", lfs.attributes(d.doc_sidecar_dir.."/"..d.sidecar_filename .. ".old", "mode"))
|
||||
-- make metadata.pdf.lua older to bypass 60s age needed for .old rotation
|
||||
local minutes_ago = os.time() - 120
|
||||
lfs.touch(d.doc_sidecar_file, minutes_ago)
|
||||
lfs.touch(d.doc_sidecar_dir.."/"..d.sidecar_filename, minutes_ago)
|
||||
d:close()
|
||||
-- metadata.pdf.lua and metadata.pdf.lua.old should be generated.
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_file, "mode"))
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_file .. ".old", "mode"))
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_dir.."/"..d.sidecar_filename, "mode"))
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_dir.."/"..d.sidecar_filename .. ".old", "mode"))
|
||||
|
||||
-- write some garbage to sidecar-file.
|
||||
local f_out = io.open(d.doc_sidecar_file, "w")
|
||||
local f_out = io.open(d.doc_sidecar_dir.."/"..d.sidecar_filename, "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.doc_sidecar_file, "mode"))
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_file .. ".old", "mode"))
|
||||
assert.are.not_equal("file", lfs.attributes(d.doc_sidecar_dir.."/"..d.sidecar_filename, "mode"))
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_dir.."/"..d.sidecar_filename .. ".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.doc_sidecar_file, "mode"))
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_file .. ".old", "mode"))
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_dir.."/"..d.sidecar_filename, "mode"))
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_dir.."/"..d.sidecar_filename .. ".old", "mode"))
|
||||
-- The contents in sidecar_file and sidecar_file.old are different.
|
||||
-- a:b v.s. a:a
|
||||
|
||||
@@ -161,21 +159,21 @@ 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.doc_sidecar_file, "w")
|
||||
f_out = io.open(d.doc_sidecar_dir.."/"..d.sidecar_filename, "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.doc_sidecar_file, "mode"))
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_file .. ".old", "mode"))
|
||||
assert.are.not_equal("file", lfs.attributes(d.doc_sidecar_dir.."/"..d.sidecar_filename, "mode"))
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_dir.."/"..d.sidecar_filename .. ".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.doc_sidecar_file, "mode"))
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_file .. ".old", "mode"))
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_dir.."/"..d.sidecar_filename, "mode"))
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_dir.."/"..d.sidecar_filename .. ".old", "mode"))
|
||||
end)
|
||||
|
||||
describe("ignore empty sidecar file", function()
|
||||
@@ -186,29 +184,29 @@ describe("docsettings module", function()
|
||||
d:saveSetting("a", "a")
|
||||
d:flush()
|
||||
-- metadata.pdf.lua should be generated.
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_file, "mode"))
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_dir.."/"..d.sidecar_filename, "mode"))
|
||||
-- make metadata.pdf.lua older to bypass 60s age needed for .old rotation
|
||||
local minutes_ago = os.time() - 120
|
||||
lfs.touch(d.doc_sidecar_file, minutes_ago)
|
||||
lfs.touch(d.doc_sidecar_dir.."/"..d.sidecar_filename, minutes_ago)
|
||||
d:close()
|
||||
-- metadata.pdf.lua and metadata.pdf.lua.old should be generated.
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_file, "mode"))
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_file .. ".old", "mode"))
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_dir.."/"..d.sidecar_filename, "mode"))
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_dir.."/"..d.sidecar_filename .. ".old", "mode"))
|
||||
|
||||
-- reset the sidecar_file to an empty file.
|
||||
local f_out = io.open(d.doc_sidecar_file, "w")
|
||||
local f_out = io.open(d.doc_sidecar_dir.."/"..d.sidecar_filename, "w")
|
||||
f_out:close()
|
||||
|
||||
d = docsettings:open(file)
|
||||
-- metadata.pdf.lua should be removed.
|
||||
assert.are.not_equal("file", lfs.attributes(d.doc_sidecar_file, "mode"))
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_file .. ".old", "mode"))
|
||||
assert.are.not_equal("file", lfs.attributes(d.doc_sidecar_dir.."/"..d.sidecar_filename, "mode"))
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_dir.."/"..d.sidecar_filename .. ".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.doc_sidecar_file, "mode"))
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_file .. ".old", "mode"))
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_dir.."/"..d.sidecar_filename, "mode"))
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_dir.."/"..d.sidecar_filename .. ".old", "mode"))
|
||||
-- The contents in sidecar_file and sidecar_file.old are different.
|
||||
-- a:b v.s. a:a
|
||||
end)
|
||||
@@ -220,30 +218,30 @@ describe("docsettings module", function()
|
||||
d:saveSetting("a", "a")
|
||||
d:flush()
|
||||
-- metadata.pdf.lua should be generated.
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_file, "mode"))
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_dir.."/"..d.sidecar_filename, "mode"))
|
||||
-- make metadata.pdf.lua older to bypass 60s age needed for .old rotation
|
||||
local minutes_ago = os.time() - 120
|
||||
lfs.touch(d.doc_sidecar_file, minutes_ago)
|
||||
lfs.touch(d.doc_sidecar_dir.."/"..d.sidecar_filename, minutes_ago)
|
||||
d:close()
|
||||
-- metadata.pdf.lua and metadata.pdf.lua.old should be generated.
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_file, "mode"))
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_file .. ".old", "mode"))
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_dir.."/"..d.sidecar_filename, "mode"))
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_dir.."/"..d.sidecar_filename .. ".old", "mode"))
|
||||
|
||||
-- reset the sidecar_file to an empty file.
|
||||
local f_out = io.open(d.doc_sidecar_file, "w")
|
||||
local f_out = io.open(d.doc_sidecar_dir.."/"..d.sidecar_filename, "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.doc_sidecar_file, "mode"))
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_file .. ".old", "mode"))
|
||||
assert.are.not_equal("file", lfs.attributes(d.doc_sidecar_dir.."/"..d.sidecar_filename, "mode"))
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_dir.."/"..d.sidecar_filename .. ".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.doc_sidecar_file, "mode"))
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_file .. ".old", "mode"))
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_dir.."/"..d.sidecar_filename, "mode"))
|
||||
assert.Equals("file", lfs.attributes(d.doc_sidecar_dir.."/"..d.sidecar_filename .. ".old", "mode"))
|
||||
-- The contents in sidecar_file and sidecar_file.old are different.
|
||||
-- a:b v.s. a:a
|
||||
end)
|
||||
|
||||
@@ -47,7 +47,7 @@ describe("FileManager module", function()
|
||||
|
||||
local tmp_sidecar = docsettings:getSidecarDir(util.realpath(tmp_fn))
|
||||
lfs.mkdir(tmp_sidecar)
|
||||
local tmp_sidecar_file = docsettings:getSidecarFile(util.realpath(tmp_fn))
|
||||
local tmp_sidecar_file = docsettings:getSidecarDir(util.realpath(tmp_fn)).."/"..docsettings.getSidecarFilename(util.realpath(tmp_fn))
|
||||
local tmp_sidecar_file_foo = tmp_sidecar_file .. ".foo" -- non-docsettings file
|
||||
local tmpsf = io.open(tmp_sidecar_file, "w")
|
||||
tmpsf:write("{}")
|
||||
@@ -85,7 +85,7 @@ describe("FileManager module", function()
|
||||
|
||||
local tmp_sidecar = docsettings:getSidecarDir(util.realpath(tmp_fn))
|
||||
lfs.mkdir(tmp_sidecar)
|
||||
local tmp_sidecar_file = docsettings:getSidecarFile(util.realpath(tmp_fn))
|
||||
local tmp_sidecar_file = docsettings:getSidecarDir(util.realpath(tmp_fn)).."/"..docsettings.getSidecarFilename(util.realpath(tmp_fn))
|
||||
local tmpsf = io.open(tmp_sidecar_file, "w")
|
||||
tmpsf:write("{}")
|
||||
tmpsf:close()
|
||||
|
||||
Reference in New Issue
Block a user