feat(koreader): hide audiobook-only books from the KOReader plugin

Add a per-login setting, on by default, that leaves books whose content files are all audio out
of the plugin catalog, its counts and bulk downloads, and drops a library holding nothing else.
The plugin detail page also names the first file the device can open as the main file.

Closes #1345
This commit is contained in:
neon
2026-10-20 16:49:58 +00:00
parent a728bf293b
commit b95a285c1b
30 changed files with 25547 additions and 127 deletions
@@ -124,16 +124,21 @@ function DetailData.files(detail, opts)
if pa ~= pb then return pa < pb end
return false
end)
local by_format = {}
for _i, file in ipairs(list) do
local by_format, opens_at = {}, {}
-- The main file is the first one this reader can open: the primary when it
-- can, so a book whose primary is an audiobook leads with its ebook.
local main
for i, file in ipairs(list) do
local fmt = string.lower(file.format or "")
by_format[fmt] = (by_format[fmt] or 0) + 1
opens_at[i] = AUDIO_FORMATS[fmt] ~= true and (opts.opens == nil or opts.opens(fmt) == true)
if main == nil and opens_at[i] then main = file end
end
local usable, other = {}, {}
for _i, file in ipairs(list) do
for i, file in ipairs(list) do
local fmt = string.lower(file.format or "")
local audio = AUDIO_FORMATS[fmt] == true
local opens = not audio and (opts.opens == nil or opts.opens(fmt) == true)
local opens = opens_at[i]
local read_along = fmt == "epub" and file.sourceMediaOverlayAvailable == true
local text_only = read_along and file.downloadVariant == "audioless_epub"
local on_device = opts.on_device ~= nil and opts.on_device(file) == true
@@ -147,8 +152,8 @@ function DetailData.files(detail, opts)
if audio then title = _("Audiobook")
elseif read_along then title = _("Read-along edition")
elseif not opens then title = T(_("%1 file"), fmt:upper())
elseif by_format[fmt] > 1 then title = file.role == "primary" and _("Main edition") or _("Another edition")
elseif file.role == "primary" then title = _("Main file")
elseif by_format[fmt] > 1 then title = file == main and _("Main edition") or _("Another edition")
elseif file == main then title = _("Main file")
else title = _("Also available") end
local entry = {
file = file,
@@ -97,6 +97,33 @@ assertEqual(other[1].audio, true, "the M4B is audio")
assertEqual(other[2].opens, false, "the AZW3 cannot be opened")
assertEqual(DetailData.fileSubs(usable[1])[1], "Text only here; narration plays in BookOrbit", "the read-along explains itself")
-- The main file is the first this reader can open, so a primary it cannot use
-- never leaves the ebook it can labelled as a spare.
local function titles(entries)
local out = {}
for _, entry in ipairs(entries) do table.insert(out, entry.title) end
return table.concat(out, ", ")
end
local audio_primary, audio_rest = DetailData.files({ files = {
{ format = "epub", role = "content" },
{ format = "m4b", role = "primary", durationSeconds = 3600 },
} }, { opens = opens })
assertEqual(titles(audio_primary), "Main file", "the only ebook beside an audiobook primary is the main file")
assertEqual(titles(audio_rest), "Audiobook", "the audiobook primary is listed apart")
assertEqual(titles((DetailData.files({ files = {
{ format = "mp3", role = "primary" },
{ format = "epub", role = "content" },
{ format = "epub", role = "content" },
} }, { opens = opens }))), "Main edition, Another edition", "two editions behind an audiobook primary still lead with one")
assertEqual(titles((DetailData.files({ files = {
{ format = "azw3", role = "primary" },
{ format = "epub", role = "content" },
} }, { opens = opens }))), "Main file", "a primary this reader cannot open hands the main file on")
assertEqual(titles((DetailData.files({ files = {
{ format = "mobi", role = "content" },
{ format = "epub", role = "primary" },
} }, { opens = opens }))), "Main file, Also available", "a primary this reader opens stays the main file")
-- --------------------------------------------------------------- action
local download = DetailData.primary({}, usable, other)