View Single Post
Old 02-28-2025, 08:25 AM   #10
gereksizuser
Enthusiast
gereksizuser began at the beginning.
 
gereksizuser's Avatar
 
Posts: 34
Karma: 10
Join Date: Aug 2021
Device: Kobo Aura Edition 2
Quote:
Originally Posted by PetBest View Post
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 View Post
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.
gereksizuser is offline   Reply With Quote