diff --git a/frontend/apps/filemanager/filemanagermenu.lua b/frontend/apps/filemanager/filemanagermenu.lua index 134fa87d8..28960b05d 100644 --- a/frontend/apps/filemanager/filemanagermenu.lua +++ b/frontend/apps/filemanager/filemanagermenu.lua @@ -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) diff --git a/frontend/docsettings.lua b/frontend/docsettings.lua index 14cfd4c5d..2198acc83 100644 --- a/frontend/docsettings.lua +++ b/frontend/docsettings.lua @@ -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. diff --git a/frontend/ui/elements/common_settings_menu_table.lua b/frontend/ui/elements/common_settings_menu_table.lua index a87b2551c..a3bb70f1b 100644 --- a/frontend/ui/elements/common_settings_menu_table.lua +++ b/frontend/ui/elements/common_settings_menu_table.lua @@ -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 .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"), }, diff --git a/frontend/ui/elements/filemanager_menu_order.lua b/frontend/ui/elements/filemanager_menu_order.lua index e01b38d24..f219eda4b 100644 --- a/frontend/ui/elements/filemanager_menu_order.lua +++ b/frontend/ui/elements/filemanager_menu_order.lua @@ -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", diff --git a/frontend/ui/elements/reader_menu_order.lua b/frontend/ui/elements/reader_menu_order.lua index 00054a113..b124d99d1 100644 --- a/frontend/ui/elements/reader_menu_order.lua +++ b/frontend/ui/elements/reader_menu_order.lua @@ -79,7 +79,7 @@ local order = { "status_bar", }, document = { - "document_metadata_folder", + "document_metadata_location", "document_auto_save", "document_save", "document_end_action", diff --git a/plugins/docsettingtweak.koplugin/main.lua b/plugins/docsettingtweak.koplugin/main.lua index 9a27557e5..289b598f1 100644 --- a/plugins/docsettingtweak.koplugin/main.lua +++ b/plugins/docsettingtweak.koplugin/main.lua @@ -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 diff --git a/spec/unit/readerui_spec.lua b/spec/unit/readerui_spec.lua index 1def3804c..f88c7794d 100644 --- a/spec/unit/readerui_spec.lua +++ b/spec/unit/readerui_spec.lua @@ -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,