mirror of
https://github.com/bookorbit/bookorbit.git
synced 2026-11-03 00:11:37 -06:00
fix(series): load complete KOReader series lists
Fetch every bounded API page before applying local pagination so large series remain complete in KOReader and available offline. Closes #1883
This commit is contained in:
@@ -1124,7 +1124,6 @@ function BookOrbitCatalog:paramsForEntry(section, entry)
|
||||
else
|
||||
params.series = entry.id
|
||||
end
|
||||
params.sort = "series"
|
||||
end
|
||||
return params
|
||||
end
|
||||
@@ -1143,23 +1142,40 @@ function BookOrbitCatalog:fetchBookPage(params, title, push)
|
||||
local body, err = self:fetch(title and T(_("Opening %1…"), title) or _("Opening books…"), function()
|
||||
return self.client:catalogBooks(query)
|
||||
end)
|
||||
local offline_saved_at
|
||||
if not body then
|
||||
if err ~= "cancelled" then
|
||||
-- Unreachable: the page this device saved for the same request.
|
||||
local saved = self:listSavedPage(query, err)
|
||||
if saved then
|
||||
self:showBookPage(saved.body, query, title or _("Books"), push ~= false, { offline_saved_at = saved.saved_at or 0 })
|
||||
return
|
||||
body = saved.body
|
||||
offline_saved_at = saved.saved_at or 0
|
||||
else
|
||||
self:showRetry(err, function()
|
||||
self:loadBooks(params, title, push)
|
||||
end)
|
||||
end
|
||||
self:showRetry(err, function()
|
||||
end
|
||||
if not body then return end
|
||||
else
|
||||
self:listSavePage(query, body)
|
||||
end
|
||||
|
||||
local complete, complete_err, tail_saved_at = self:listFetchCompleteSeries(query, body, title)
|
||||
if not complete then
|
||||
if complete_err ~= "cancelled" then
|
||||
self:showRetry(complete_err, function()
|
||||
self:loadBooks(params, title, push)
|
||||
end)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
self:listSavePage(query, body)
|
||||
self:showBookPage(body, query, title or _("Books"), push ~= false)
|
||||
if tail_saved_at then
|
||||
offline_saved_at = offline_saved_at and math.min(offline_saved_at, tail_saved_at) or tail_saved_at
|
||||
end
|
||||
local complete_series = query.seriesId ~= nil or query.series ~= nil
|
||||
self:showBookPage(complete, query, title or _("Books"), push ~= false,
|
||||
offline_saved_at and { offline_saved_at = offline_saved_at } or { complete = complete_series })
|
||||
end
|
||||
|
||||
function BookOrbitCatalog:loadBooks(params, title, push)
|
||||
@@ -1387,7 +1403,7 @@ function BookOrbitCatalog:showBookPage(body, query, title, push, opts)
|
||||
list_saved_at = opts.offline_saved_at,
|
||||
}, push)
|
||||
|
||||
if not opts.offline_saved_at then
|
||||
if not opts.offline_saved_at and not opts.complete then
|
||||
if offset then
|
||||
if offset + size < total then self:prefetchNextPage(stored, nil, offset + size) end
|
||||
elseif page < page_count then
|
||||
|
||||
@@ -54,11 +54,11 @@ end
|
||||
function CatalogDetail.detailSeriesParams(detail)
|
||||
if not detail then return nil end
|
||||
if detail.seriesId then
|
||||
return { seriesId = tonumber(detail.seriesId), sort = "series" }
|
||||
return { seriesId = tonumber(detail.seriesId), sort = "title" }
|
||||
end
|
||||
local name = cleanInlineText(detail.seriesName)
|
||||
if not name then return nil end
|
||||
return { series = name, sort = "series" }
|
||||
return { series = name, sort = "title" }
|
||||
end
|
||||
|
||||
function CatalogDetail.detailSeriesText(detail)
|
||||
|
||||
@@ -152,6 +152,69 @@ function CatalogLists:listSavedPage(query, err)
|
||||
return ok and saved or nil
|
||||
end
|
||||
|
||||
-- A series is drawn as one local list, while the API deliberately caps each
|
||||
-- response. Keep requesting bounded server pages before the local paginator
|
||||
-- takes over.
|
||||
function CatalogLists:listFetchCompleteSeries(query, first_body, title)
|
||||
if not (query.seriesId or query.series) then return first_body end
|
||||
|
||||
local items, seen = {}, {}
|
||||
local function append(body)
|
||||
local added = 0
|
||||
for _i, book in ipairs(body.items or {}) do
|
||||
local key = book.id ~= nil and tostring(book.id) or nil
|
||||
if not key or not seen[key] then
|
||||
if key then seen[key] = true end
|
||||
table.insert(items, book)
|
||||
added = added + 1
|
||||
end
|
||||
end
|
||||
return added
|
||||
end
|
||||
|
||||
append(first_body)
|
||||
local body = first_body
|
||||
local total = tonumber(body.total)
|
||||
local page = tonumber(body.page) or tonumber(query.page) or 1
|
||||
local offline_saved_at
|
||||
local has_next = body.hasNext == true or (total and #items < total)
|
||||
|
||||
while has_next do
|
||||
page = page + 1
|
||||
local page_query = cloneParams(query)
|
||||
page_query.page = page
|
||||
page_query.offset = nil
|
||||
local next_body, err = self:fetch(title and T(_("Opening %1…"), title) or _("Opening books…"), function()
|
||||
return self.client:catalogBooks(page_query)
|
||||
end)
|
||||
if not next_body then
|
||||
local saved = self:listSavedPage(page_query, err)
|
||||
if not saved then return nil, err end
|
||||
next_body = saved.body
|
||||
local saved_at = tonumber(saved.saved_at) or 0
|
||||
offline_saved_at = offline_saved_at and math.min(offline_saved_at, saved_at) or saved_at
|
||||
else
|
||||
self:listSavePage(page_query, next_body)
|
||||
end
|
||||
|
||||
if append(next_body) == 0 then return nil, "incomplete series response" end
|
||||
body = next_body
|
||||
total = tonumber(body.total) or total
|
||||
has_next = body.hasNext == true or (total and #items < total)
|
||||
end
|
||||
|
||||
local complete = {}
|
||||
for key, value in pairs(first_body) do complete[key] = value end
|
||||
complete.items = items
|
||||
complete.total = math.max(total or 0, #items)
|
||||
complete.page = 1
|
||||
complete.hasNext = false
|
||||
complete.hasPrevious = false
|
||||
complete.nextUrl = nil
|
||||
complete.previousUrl = nil
|
||||
return complete, nil, offline_saved_at
|
||||
end
|
||||
|
||||
-- The server page size for a book list. A series or an author page draws
|
||||
-- all its books (local pages when there are more than fit).
|
||||
function CatalogLists:listBookPageSize(params)
|
||||
@@ -1162,6 +1225,7 @@ function CatalogLists:listBody(kind, context)
|
||||
local m = {
|
||||
entries = books, view = self:listView(kind), screen_w = sw, screen_h = sh, local_page = context.local_page,
|
||||
offline = context.list_offline and {} or nil,
|
||||
order_label = (context.params or {}).sort == "title" and _("Title A–Z") or _("Reading order"),
|
||||
states = states, indexes = indexes, current_index = cur, next_book = next_book,
|
||||
range = #books > 0 and T(_("Books %1 – %2"), first, last) or nil,
|
||||
next_minutes = next_book and self:listMinutesLeft(next_book),
|
||||
|
||||
@@ -231,7 +231,7 @@ function Pages.seriesBody(m, f)
|
||||
else
|
||||
hero = hstack({ w = f.w, h = hero_h, gap = px(20) }, { deck, vstack({ w = tw, h = hero_h }, { flex(0), summary_col, flex(0) }) })
|
||||
end
|
||||
local head = ListView.sectionHead(list_w, { icon = "layers-2", label = _("Reading order"), count = #all_books, right = ListView.viewToggle(m.view, "series-head") })
|
||||
local head = ListView.sectionHead(list_w, { icon = "layers-2", label = m.order_label or _("Reading order"), count = #all_books, right = ListView.viewToggle(m.view, "series-head") })
|
||||
local list_h = page == 1 and layout.list_h1 or layout.list_hn
|
||||
local content
|
||||
if m.view == "grid" then
|
||||
|
||||
Reference in New Issue
Block a user