DocSettings: Move book metadata to preferred location (#10149)

reviewable/pr10172/r1
hius07 1 year ago committed by GitHub
parent 189739ac4c
commit 38bd768d5e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -15,6 +15,7 @@ local lfs = require("libs/libkoreader-lfs")
local logger = require("logger") local logger = require("logger")
local util = require("util") local util = require("util")
local _ = require("gettext") local _ = require("gettext")
local N_ = _.ngettext
local T = FFIUtil.template local T = FFIUtil.template
local FileManagerMenu = InputContainer:extend{ local FileManagerMenu = InputContainer:extend{
@ -423,6 +424,15 @@ To:
self.menu_items[id] = common_setting self.menu_items[id] = common_setting
end end
-- settings tab - Document submenu
self.menu_items.document_metadata_location_move = {
text = _("Move book metadata"),
keep_menu_open = true,
callback = function()
self:moveBookMetadata()
end,
}
-- tools tab -- tools tab
self.menu_items.advanced_settings = { self.menu_items.advanced_settings = {
text = _("Advanced settings"), text = _("Advanced settings"),
@ -790,6 +800,74 @@ dbg:guard(FileManagerMenu, 'setUpdateItemTable',
end end
end) 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) ..
_("\nMove 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:update(book, book)
end
FileChooser:refreshPath()
end,
})
end
end,
})
end
function FileManagerMenu:exitOrRestart(callback, force) function FileManagerMenu:exitOrRestart(callback, force)
UIManager:close(self.menu_container) UIManager:close(self.menu_container)

@ -98,11 +98,6 @@ function DocSettings:hasSidecarFile(doc_path)
or lfs.attributes(self:getHistoryPath(doc_path), "mode") == "file" or lfs.attributes(self:getHistoryPath(doc_path), "mode") == "file"
end 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(doc_path) function DocSettings:getHistoryPath(doc_path)
if doc_path == nil or doc_path == "" then return "" end if doc_path == nil or doc_path == "" then return "" end
return HISTORY_DIR .. "/[" .. doc_path:gsub("(.*/)([^/]+)", "%1] %2"):gsub("/", "#") .. ".lua" return HISTORY_DIR .. "/[" .. doc_path:gsub("(.*/)([^/]+)", "%1] %2"):gsub("/", "#") .. ".lua"
@ -204,6 +199,7 @@ function DocSettings:open(doc_path)
else else
new.data = {} new.data = {}
end end
new.data.doc_path = doc_path
return new return new
end end
@ -248,7 +244,7 @@ function DocSettings:flush(data)
ffiutil.fsyncDirectory(sidecar_file) ffiutil.fsyncDirectory(sidecar_file)
end end
self:purge(false, sidecar_file) -- remove old candidates and empty sidecar folders self:purge(sidecar_file) -- remove old candidates and empty sidecar folders
break break
end end
@ -256,7 +252,7 @@ function DocSettings:flush(data)
end end
--- Purges (removes) sidecar directory. --- Purges (removes) sidecar directory.
function DocSettings:purge(full, sidecar_to_keep) function DocSettings:purge(sidecar_to_keep)
-- Remove any of the old ones we may consider as candidates in DocSettings:open() -- Remove any of the old ones we may consider as candidates in DocSettings:open()
if self.candidates then if self.candidates then
for _, t in ipairs(self.candidates) do for _, t in ipairs(self.candidates) do
@ -271,20 +267,12 @@ function DocSettings:purge(full, sidecar_to_keep)
end end
end end
local function purgeDir(dir, full_purge) if lfs.attributes(self.doc_sidecar_dir, "mode") == "directory" then
if lfs.attributes(dir, "mode") == "directory" then os.remove(self.doc_sidecar_dir) -- keep parent folders
if full_purge then end
-- Asked to remove all the content of this .sdr directory, whether it's ours or not if lfs.attributes(self.dir_sidecar_dir, "mode") == "directory" then
ffiutil.purgeDir(dir) util.removePath(self.dir_sidecar_dir) -- remove empty parent folders
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 end
purgeDir(self.doc_sidecar_dir, full)
purgeDir(self.dir_sidecar_dir, full)
end end
--- Updates sidecar info for file rename/copy/move/delete operations. --- Updates sidecar info for file rename/copy/move/delete operations.

@ -528,9 +528,16 @@ common_settings.document = {
local metadata_folder_str = { local metadata_folder_str = {
["doc"] = _("book folder"), ["doc"] = _("book folder"),
["dir"] = "koreader/docsettings", ["dir"] = "koreader/docsettings/",
} }
local metadata_folder_help_text = _([[
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 two locations where these will be saved:
- alongside the book file itself (the long time default): these 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.
- all inside koreader/docsettings/: these 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.]])
local function genMetadataFolderMenuItem(value) local function genMetadataFolderMenuItem(value)
return { return {
text = metadata_folder_str[value], text = metadata_folder_str[value],
@ -543,12 +550,21 @@ local function genMetadataFolderMenuItem(value)
} }
end end
common_settings.document_metadata_folder = { common_settings.document_metadata_location = {
text_func = function() text_func = function()
local value = G_reader_settings:readSetting("document_metadata_folder", "doc") local value = G_reader_settings:readSetting("document_metadata_folder", "doc")
return T(_("Book metadata folder: %1"), metadata_folder_str[value]) return T(_("Book metadata location: %1"), metadata_folder_str[value])
end, end,
help_text = metadata_folder_help_text,
sub_item_table = { sub_item_table = {
{
text = _("About book metadata location"),
keep_menu_open = true,
callback = function()
UIManager:show(InfoMessage:new{ text = metadata_folder_help_text, })
end,
separator = true,
},
genMetadataFolderMenuItem("doc"), genMetadataFolderMenuItem("doc"),
genMetadataFolderMenuItem("dir"), genMetadataFolderMenuItem("dir"),
}, },

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

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

@ -101,7 +101,7 @@ end
function DocSettingTweak:onDocSettingsLoad(doc_settings, document) function DocSettingTweak:onDocSettingsLoad(doc_settings, document)
-- check that the documents settings are empty & and that we have defaults to customize -- check that the documents settings are empty & and that we have defaults to customize
if next(doc_settings.data) == nil and directory_defaults.data ~= nil then if util.tableSize(doc_settings.data) == 1 and directory_defaults.data ~= nil then
local base = G_reader_settings:readSetting("home_dir") or filemanagerutil.getDefaultDir() local base = G_reader_settings:readSetting("home_dir") or filemanagerutil.getDefaultDir()
if document.file == nil or document.file == "" then if document.file == nil or document.file == "" then
return return
@ -111,6 +111,7 @@ function DocSettingTweak:onDocSettingsLoad(doc_settings, document)
while directory:sub(1, #base) == base do while directory:sub(1, #base) == base do
if directory_defaults:has(directory) then if directory_defaults:has(directory) then
doc_settings.data = util.tableDeepCopy(directory_defaults:readSetting(directory)) doc_settings.data = util.tableDeepCopy(directory_defaults:readSetting(directory))
doc_settings.data.doc_path = document.file
break break
else else
if directory == "/" or directory == "." then if directory == "/" or directory == "." then

@ -19,9 +19,9 @@ describe("Readerui module", function()
-- remove history settings and sidecar settings -- remove history settings and sidecar settings
DocSettings:open(sample_epub):purge() DocSettings:open(sample_epub):purge()
local doc_settings = DocSettings:open(sample_epub) local doc_settings = DocSettings:open(sample_epub)
assert.are.same(doc_settings.data, {}) assert.are.same(doc_settings.data, {doc_path = sample_epub})
readerui:saveSettings() readerui:saveSettings()
assert.are_not.same(readerui.doc_settings.data, {}) assert.are_not.same(readerui.doc_settings.data, {doc_path = sample_epub})
doc_settings = DocSettings:open(sample_epub) doc_settings = DocSettings:open(sample_epub)
assert.truthy(doc_settings.data.last_xpointer) assert.truthy(doc_settings.data.last_xpointer)
assert.are.same(doc_settings.data.last_xpointer, assert.are.same(doc_settings.data.last_xpointer,

Loading…
Cancel
Save