mirror of
https://github.com/koreader/koreader.git
synced 2025-12-13 20:36:53 +01:00
DocSettings: Move book metadata to preferred location (#10149)
This commit is contained in:
@@ -15,6 +15,7 @@ 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{
|
||||
@@ -423,6 +424,15 @@ To:
|
||||
self.menu_items[id] = common_setting
|
||||
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
|
||||
self.menu_items.advanced_settings = {
|
||||
text = _("Advanced settings"),
|
||||
@@ -790,6 +800,74 @@ dbg:guard(FileManagerMenu, 'setUpdateItemTable',
|
||||
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)
|
||||
UIManager:close(self.menu_container)
|
||||
|
||||
|
||||
@@ -98,11 +98,6 @@ function DocSettings:hasSidecarFile(doc_path)
|
||||
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(doc_path)
|
||||
if doc_path == nil or doc_path == "" then return "" end
|
||||
return HISTORY_DIR .. "/[" .. doc_path:gsub("(.*/)([^/]+)", "%1] %2"):gsub("/", "#") .. ".lua"
|
||||
@@ -204,6 +199,7 @@ function DocSettings:open(doc_path)
|
||||
else
|
||||
new.data = {}
|
||||
end
|
||||
new.data.doc_path = doc_path
|
||||
|
||||
return new
|
||||
end
|
||||
@@ -248,7 +244,7 @@ function DocSettings:flush(data)
|
||||
ffiutil.fsyncDirectory(sidecar_file)
|
||||
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
|
||||
end
|
||||
@@ -256,7 +252,7 @@ function DocSettings:flush(data)
|
||||
end
|
||||
|
||||
--- 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()
|
||||
if self.candidates then
|
||||
for _, t in ipairs(self.candidates) do
|
||||
@@ -271,20 +267,12 @@ function DocSettings:purge(full, sidecar_to_keep)
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
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
|
||||
purgeDir(self.doc_sidecar_dir, full)
|
||||
purgeDir(self.dir_sidecar_dir, full)
|
||||
end
|
||||
|
||||
--- Updates sidecar info for file rename/copy/move/delete operations.
|
||||
|
||||
@@ -528,9 +528,16 @@ common_settings.document = {
|
||||
|
||||
local metadata_folder_str = {
|
||||
["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)
|
||||
return {
|
||||
text = metadata_folder_str[value],
|
||||
@@ -543,12 +550,21 @@ local function genMetadataFolderMenuItem(value)
|
||||
}
|
||||
end
|
||||
|
||||
common_settings.document_metadata_folder = {
|
||||
common_settings.document_metadata_location = {
|
||||
text_func = function()
|
||||
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,
|
||||
help_text = metadata_folder_help_text,
|
||||
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("dir"),
|
||||
},
|
||||
|
||||
@@ -36,7 +36,8 @@ local order = {
|
||||
-- end common settings
|
||||
},
|
||||
document = {
|
||||
"document_metadata_folder",
|
||||
"document_metadata_location",
|
||||
"document_metadata_location_move",
|
||||
"document_auto_save",
|
||||
"document_save",
|
||||
"document_end_action",
|
||||
|
||||
@@ -79,7 +79,7 @@ local order = {
|
||||
"status_bar",
|
||||
},
|
||||
document = {
|
||||
"document_metadata_folder",
|
||||
"document_metadata_location",
|
||||
"document_auto_save",
|
||||
"document_save",
|
||||
"document_end_action",
|
||||
|
||||
@@ -101,7 +101,7 @@ end
|
||||
|
||||
function DocSettingTweak:onDocSettingsLoad(doc_settings, document)
|
||||
-- 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()
|
||||
if document.file == nil or document.file == "" then
|
||||
return
|
||||
@@ -111,6 +111,7 @@ function DocSettingTweak:onDocSettingsLoad(doc_settings, document)
|
||||
while directory:sub(1, #base) == base do
|
||||
if directory_defaults:has(directory) then
|
||||
doc_settings.data = util.tableDeepCopy(directory_defaults:readSetting(directory))
|
||||
doc_settings.data.doc_path = document.file
|
||||
break
|
||||
else
|
||||
if directory == "/" or directory == "." then
|
||||
|
||||
@@ -19,9 +19,9 @@ describe("Readerui module", function()
|
||||
-- remove history settings and sidecar settings
|
||||
DocSettings:open(sample_epub):purge()
|
||||
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()
|
||||
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)
|
||||
assert.truthy(doc_settings.data.last_xpointer)
|
||||
assert.are.same(doc_settings.data.last_xpointer,
|
||||
|
||||
Reference in New Issue
Block a user