97 lines
3.2 KiB
Lua
97 lines
3.2 KiB
Lua
local DocSettings = require("docsettings")
|
|
local FileManagerBookInfo = require("apps/filemanager/filemanagerbookinfo")
|
|
local DataStorage = require("datastorage")
|
|
local random = require("random")
|
|
local logger = require("logger")
|
|
local lfs = require("libs/libkoreader-lfs")
|
|
|
|
local Reader = {}
|
|
local MAX_BYTES = 4 * 1024 * 1024
|
|
|
|
local function sidecar_md5(file_path)
|
|
if not file_path then return nil end
|
|
local sidecar = DocSettings:findSidecarFile(file_path)
|
|
if not sidecar then return nil end
|
|
local settings = DocSettings.openSettingsFile(sidecar)
|
|
return settings:readSetting("partial_md5_checksum")
|
|
end
|
|
|
|
local function paths_for(md5s)
|
|
local wanted, remaining = {}, 0
|
|
for _, md5 in ipairs(md5s or {}) do
|
|
if md5 and md5 ~= "" and not wanted[md5] then
|
|
wanted[md5] = false
|
|
remaining = remaining + 1
|
|
end
|
|
end
|
|
if remaining == 0 then return wanted end
|
|
|
|
local ok, ReadHistory = pcall(require, "readhistory")
|
|
if not ok or not ReadHistory or type(ReadHistory.hist) ~= "table" then
|
|
logger.warn("[kovi] Could not read KOReader history for embedded covers")
|
|
return wanted
|
|
end
|
|
|
|
for _, entry in ipairs(ReadHistory.hist) do
|
|
if remaining == 0 then break end
|
|
if entry.file and not entry.dim then
|
|
local success, md5 = pcall(sidecar_md5, entry.file)
|
|
if success and md5 and wanted[md5] == false then
|
|
wanted[md5] = entry.file
|
|
remaining = remaining - 1
|
|
end
|
|
end
|
|
end
|
|
return wanted
|
|
end
|
|
|
|
local function read_all(file_path)
|
|
local f, err = io.open(file_path, "rb")
|
|
if not f then return nil, tostring(err or "cannot open temporary cover") end
|
|
local data = f:read("*all")
|
|
f:close()
|
|
if not data or data == "" then return nil, "temporary cover is empty" end
|
|
if #data > MAX_BYTES then return nil, "embedded cover is larger than 4 MB" end
|
|
return data
|
|
end
|
|
|
|
local function extract_jpeg(file_path)
|
|
local cover = FileManagerBookInfo:getCoverImage(nil, file_path)
|
|
if not cover then return nil, "book has no extractable embedded cover" end
|
|
|
|
local cache_dir = DataStorage:getDataDir() .. "/cache/"
|
|
lfs.mkdir(cache_dir)
|
|
local tmp = cache_dir .. "kovi-cover-" .. random.uuid() .. ".jpg"
|
|
local ok = cover:writeToFile(tmp, "jpg", 82, false)
|
|
cover:free()
|
|
if not ok then
|
|
os.remove(tmp)
|
|
return nil, "KOReader could not encode the cover as JPEG"
|
|
end
|
|
local data, err = read_all(tmp)
|
|
os.remove(tmp)
|
|
return data, err
|
|
end
|
|
|
|
function Reader.extractRequested(md5s)
|
|
local paths = paths_for(md5s)
|
|
local out = {}
|
|
for _, md5 in ipairs(md5s or {}) do
|
|
local file_path = paths[md5]
|
|
if file_path then
|
|
local ok, data, err = pcall(extract_jpeg, file_path)
|
|
if ok and data then
|
|
table.insert(out, { book_md5 = md5, data = data })
|
|
logger.info("[kovi] Extracted embedded cover", md5, #data, "bytes")
|
|
else
|
|
logger.warn("[kovi] Embedded cover extraction failed", md5, tostring(ok and err or data))
|
|
end
|
|
else
|
|
logger.info("[kovi] No local file found for requested cover", md5)
|
|
end
|
|
end
|
|
return out
|
|
end
|
|
|
|
return Reader
|