Quote:
Originally Posted by PetBest
Can you share your modification?
Great idea!
I tried/, try to figure out how to update immediately the collection without a restart.
So far without success. If you find a way to do it please share.
|
Quote:
Originally Posted by PetBest
On which device are you running KOreader.
Is that a Kobo Libra 2?
|
I'm using the Kobo Aura n236, here it is;
Code:
-- KOReader plugin to add all ebooks found on device to separate collections by author
require "defaults"
package.path = "common/?.lua;frontend/?.lua;" .. package.path
package.cpath = "common/?.so;common/?.dll;/usr/lib/lua/?.so;" .. package.cpath
local Dispatcher = require("dispatcher") -- luacheck:ignore
local InfoMessage = require("ui/widget/infomessage")
local UIManager = require("ui/uimanager")
local WidgetContainer = require("ui/widget/container/widgetcontainer")
local _ = require("gettext")
local LuaSettings = require("luasettings")
local ffiUtil = require("ffi/util")
local lfs = require("libs/libkoreader-lfs")
local logger = require("logger")
local util = require("util")
local DataStorage = require("datastorage")
local ReadCollection = require("readcollection")
local FileManagerCollection = require("apps/filemanager/filemanagercollection")
local DocumentRegistry = require("document/documentregistry")
if G_reader_settings == nil then
G_reader_settings = require("luasettings"):open(
DataStorage:getDataDir().."/settings.reader.lua")
end
-- Get the base ebooks folder on the ereader to start the search for ebooks
local ebooks_directory_path = G_reader_settings:readSetting("home_dir")
-- Get the Koreader base folder on the ereader where the settings are stored
local koreader_settings_directory_path = DataStorage:getFullDataDir()
-- Get the Koreader file on the ereader where the settings are stored for collections
local collection_file = DataStorage:getSettingsDir() .. "/collection.lua"
local ReadCollection = {
coll = nil, -- hash table
coll_settings = nil, -- hash table
last_read_time = 0,
default_collection_name = "favorites",
}
-- read, write
local function buildEntry(file, order, attr)
file = ffiUtil.realpath(file)
if file then
attr = attr or lfs.attributes(file)
if attr and attr.mode == "file" then
return {
file = file,
text = file:gsub(".*/", ""),
order = order,
attr = attr,
}
end
end
end
function ReadCollection:_read()
local collection_file_modification_time = lfs.attributes(collection_file, "modification")
if collection_file_modification_time then
if collection_file_modification_time <= self.last_read_time then return end
self.last_read_time = collection_file_modification_time
end
local collections = LuaSettings:open(collection_file)
if collections:hasNot(self.default_collection_name) then
collections:saveSetting(self.default_collection_name, {})
end
logger.dbg("ReadCollection: reading from collection file")
self.coll = {}
self.coll_settings = {}
for coll_name, collection in pairs(collections.data) do
local coll = {}
for _, v in ipairs(collection) do
local item = buildEntry(v.file, v.order)
if item then -- exclude deleted files
coll[item.file] = item
end
end
self.coll[coll_name] = coll
self.coll_settings[coll_name] = collection.settings or { order = 1 } -- favorites, first run
end
end
function ReadCollection:write(updated_collections)
local collections = LuaSettings:open(collection_file)
for coll_name in pairs(collections.data) do
if not self.coll[coll_name] then
collections:delSetting(coll_name)
end
end
for coll_name, coll in pairs(self.coll) do
if updated_collections == nil or updated_collections[1] or updated_collections[coll_name] then
local is_manual_collate = not self.coll_settings[coll_name].collate or nil
local data = { settings = self.coll_settings[coll_name] }
for _, item in pairs(coll) do
table.insert(data, { file = item.file, order = is_manual_collate and item.order })
end
collections:saveSetting(coll_name, data)
end
end
logger.dbg("ReadCollection: writing to collection file")
collections:flush()
end
-- info
function ReadCollection:isFileInCollection(file, collection_name)
file = ffiUtil.realpath(file) or file
return self.coll[collection_name][file] and true or false
end
function ReadCollection:getCollectionNextOrder(collection_name)
if self.coll_settings[collection_name].collate then return end
local max_order = 0
for _, item in pairs(self.coll[collection_name]) do
if max_order < item.order then
max_order = item.order
end
end
return max_order + 1
end
-- manage items
function ReadCollection:addItem(file, collection_name)
local item = buildEntry(file, self:getCollectionNextOrder(collection_name))
self.coll[collection_name][item.file] = item
end
-- manage collections
function ReadCollection:addCollection(coll_name)
local max_order = 0
for _, settings in pairs(self.coll_settings) do
if max_order < settings.order then
max_order = settings.order
end
end
self.coll_settings[coll_name] = { order = max_order + 1 }
self.coll[coll_name] = {}
end
-- Extract author name from metadata or file name
local function extractAuthor(file_path)
-- Try to get author from metadata
local doc = DocumentRegistry:openDocument(file_path)
if doc then
local metadata = doc:getProps()
if metadata and metadata.author then
local meta_author = metadata.author
doc:close()
return meta_author
end
doc:close()
end
-- Fallback to extracting author from file name
local file_name = file_path:match(".*/([^/]+)$") -- Extract file name from path
if file_name then
local author, _ = file_name:match("^(.-) %- (.+)$") -- Split by " - "
if author then
return author:gsub("%s+$", "") -- Trim trailing spaces
end
end
return "Unknown Author" -- Default if format is not matched
end
local AllMyeBooks = WidgetContainer:extend{
name = "All eBooks",
is_doc_only = false,
}
function start(ebooks_directory_path)
local files = scandir(ebooks_directory_path)
ReadCollection:_read()
-- Group files by author
local books_by_author = {}
for _, file in ipairs(files) do
local author = extractAuthor(file)
if not books_by_author[author] then
books_by_author[author] = {}
end
table.insert(books_by_author[author], file)
end
-- Sort authors alphabetically
local sorted_authors = {}
for author in pairs(books_by_author) do
table.insert(sorted_authors, author)
end
table.sort(sorted_authors, function(a, b) return a < b end)
-- Create a collection for each author and add their books
for _, author in ipairs(sorted_authors) do
local collection_name = author
ReadCollection:addCollection(collection_name)
add_ebooks_to_collection(books_by_author[author], collection_name)
end
UIManager:show(InfoMessage:new{
text = _("Collections by author have been created. Please restart KOReader for changes to take effect."),
timeout=9,
})
end
function scandir(directory)
local i, t, popen = 0, {}, io.popen
local pfile = popen('find "'..directory..'" -maxdepth 10 -type f -name "*.epub" -o -name "*.pdf" -o -name "*.azw3" -o -name "*.mobi" -o -name "*.docx" -o -name "*.cbz" | sort ') -- on linux
for filename in pfile:lines() do
i = i + 1
t[i] = filename
end
pfile:close()
return t
end
function add_ebooks_to_collection(files, collection_name)
for _, file in ipairs(files) do
ReadCollection:addItem(file, collection_name)
end
ReadCollection:write({collection_name})
end
function AllMyeBooks:onDispatcherRegisterActions()
Dispatcher:registerAction("AllMyeBooks_action", {category="none", event="AllMyeBooks", title=_("Create Collections by Author"), general=true,})
end
function AllMyeBooks:init()
self:onDispatcherRegisterActions()
self.ui.menu:registerToMainMenu(self)
end
function AllMyeBooks:addToMainMenu(menu_items)
menu_items.AllMyeBooks = {
text = _("Create Collections by Author"),
sorting_hint = "more_tools",
callback = function()
UIManager:show(InfoMessage:new{
text = _("Creating collections by author... Please wait."),
timeout=5,
})
start(ebooks_directory_path)
end,
}
end
function AllMyeBooks:onAllMyeBooks()
local popup = InfoMessage:new{
text = _("Creating collections by author... Please wait."),
timeout=5,
}
UIManager:show(popup)
start(ebooks_directory_path)
local popup = InfoMessage:new{
text = _("Collections by author have been created. Please restart KOReader for changes to take effect."),
timeout=9,
}
UIManager:show(popup)
end
return AllMyeBooks
Takes a little longer, but works great on my device.