fix: show "Failed to index" when failing to parse epub (#2556)

This commit is contained in:
Uri Tauber
2026-07-08 21:22:35 +03:00
committed by GitHub
parent c2c1badc7e
commit 7ec07b4d9d
3 changed files with 34 additions and 8 deletions
+1
View File
@@ -18,6 +18,7 @@ STR_NO_CHAPTERS: "No chapters"
STR_END_OF_BOOK: "End of book" STR_END_OF_BOOK: "End of book"
STR_EMPTY_CHAPTER: "Empty chapter" STR_EMPTY_CHAPTER: "Empty chapter"
STR_INDEXING: "Indexing" STR_INDEXING: "Indexing"
STR_INDEX_FAILED: "Failed to index - invalid book"
STR_MEMORY_ERROR: "Memory error" STR_MEMORY_ERROR: "Memory error"
STR_PAGE_LOAD_ERROR: "Page load error" STR_PAGE_LOAD_ERROR: "Page load error"
STR_EMPTY_FILE: "Empty file" STR_EMPTY_FILE: "Empty file"
+30 -9
View File
@@ -893,6 +893,15 @@ void EpubReaderActivity::render(RenderLock&& lock) {
GUI.drawPopup(renderer, tr(STR_SAVE_PROGRESS_FAILED)); GUI.drawPopup(renderer, tr(STR_SAVE_PROGRESS_FAILED));
}; };
// A section build failure (e.g. an invalid/corrupt EPUB that fails XML parsing) leaves the
// "Indexing" popup on screen with no way forward. Surface an explicit error instead of hanging.
// clearScreen first so the error popup doesn't overlay the stale "Indexing" popup.
const auto showBuildError = [this]() {
renderer.clearScreen();
GUI.drawPopup(renderer, tr(STR_INDEX_FAILED));
automaticPageTurnActive = false;
};
// edge case handling for sub-zero spine index // edge case handling for sub-zero spine index
if (currentSpineIndex < 0) { if (currentSpineIndex < 0) {
currentSpineIndex = 0; currentSpineIndex = 0;
@@ -991,7 +1000,7 @@ void EpubReaderActivity::render(RenderLock&& lock) {
SETTINGS.imageRendering, SETTINGS.focusReadingEnabled, popupFn)) { SETTINGS.imageRendering, SETTINGS.focusReadingEnabled, popupFn)) {
LOG_ERR("ERS", "Failed to persist page data to SD"); LOG_ERR("ERS", "Failed to persist page data to SD");
section.reset(); section.reset();
showPendingSyncSaveError(); showBuildError();
return; return;
} }
} else { } else {
@@ -1033,7 +1042,7 @@ void EpubReaderActivity::render(RenderLock&& lock) {
SETTINGS.imageRendering, SETTINGS.focusReadingEnabled)) { SETTINGS.imageRendering, SETTINGS.focusReadingEnabled)) {
LOG_ERR("ERS", "Failed to start section build"); LOG_ERR("ERS", "Failed to start section build");
section.reset(); section.reset();
showPendingSyncSaveError(); showBuildError();
return; return;
} }
while (!section->isBuildComplete() && while (!section->isBuildComplete() &&
@@ -1044,7 +1053,7 @@ void EpubReaderActivity::render(RenderLock&& lock) {
if (!section->buildSomeMore(BUILD_PAGES_PER_CHUNK)) { if (!section->buildSomeMore(BUILD_PAGES_PER_CHUNK)) {
LOG_ERR("ERS", "Failed during incremental section build"); LOG_ERR("ERS", "Failed during incremental section build");
section.reset(); section.reset();
showPendingSyncSaveError(); showBuildError();
return; return;
} }
} }
@@ -1098,7 +1107,7 @@ void EpubReaderActivity::render(RenderLock&& lock) {
SETTINGS.focusReadingEnabled)) { SETTINGS.focusReadingEnabled)) {
LOG_ERR("ERS", "Failed to start partial extension build"); LOG_ERR("ERS", "Failed to start partial extension build");
section.reset(); section.reset();
showPendingSyncSaveError(); showBuildError();
return; return;
} }
// Extend until either the target page exists or the build completes. // Extend until either the target page exists or the build completes.
@@ -1106,7 +1115,7 @@ void EpubReaderActivity::render(RenderLock&& lock) {
if (!section->buildSomeMore(BUILD_PAGES_PER_CHUNK)) { if (!section->buildSomeMore(BUILD_PAGES_PER_CHUNK)) {
LOG_ERR("ERS", "Failed during incremental section build"); LOG_ERR("ERS", "Failed during incremental section build");
section.reset(); section.reset();
showPendingSyncSaveError(); showBuildError();
return; return;
} }
} }
@@ -1117,7 +1126,7 @@ void EpubReaderActivity::render(RenderLock&& lock) {
if (!section->buildSomeMore(BUILD_PAGES_PER_CHUNK)) { if (!section->buildSomeMore(BUILD_PAGES_PER_CHUNK)) {
LOG_ERR("ERS", "Failed during incremental section build"); LOG_ERR("ERS", "Failed during incremental section build");
section.reset(); section.reset();
showPendingSyncSaveError(); showBuildError();
return; return;
} }
} }
@@ -1167,17 +1176,29 @@ void EpubReaderActivity::render(RenderLock&& lock) {
auto p = section->loadPage(section->currentPage); auto p = section->loadPage(section->currentPage);
if (!p) { if (!p) {
LOG_ERR("ERS", "Failed to load page from SD - clearing section cache"); LOG_ERR("ERS", "Failed to load page from SD - clearing section cache");
automaticPageTurnActive = false;
// Retrying rebuilds a transiently corrupt section and usually recovers, but a page that keeps
// failing would loop forever on a blank screen, so bound the retries before giving up.
const bool giveUp = ++pageLoadRetryCount > MAX_PAGE_LOAD_RETRIES;
// Abandon (not suspend) any active build BEFORE clearing: clearCache deletes the files, // Abandon (not suspend) any active build BEFORE clearing: clearCache deletes the files,
// and the destructor's suspend would otherwise commit tables into a deleted handle. // and the destructor's suspend would otherwise commit tables into a deleted handle.
section->abandonBuild(); section->abandonBuild();
section->clearCache(); section->clearCache();
section.reset(); section.reset();
requestUpdate(); // Try again after clearing cache if (giveUp) {
// TODO: prevent infinite loop if the page keeps failing to load for some reason LOG_ERR("ERS", "Page load retry limit reached, aborting");
automaticPageTurnActive = false; pageLoadRetryCount = 0; // Reset so a later user-initiated navigation can try afresh
renderer.clearScreen();
renderer.drawCenteredText(UI_12_FONT_ID, 300, tr(STR_PAGE_LOAD_ERROR), true, EpdFontFamily::BOLD);
renderer.displayBuffer();
showPendingSyncSaveError(); showPendingSyncSaveError();
return; return;
} }
requestUpdate(); // Try again after clearing cache
showPendingSyncSaveError();
return;
}
pageLoadRetryCount = 0; // Reset the retry counter once a page loads cleanly
// Collect footnotes from the loaded page // Collect footnotes from the loaded page
currentPageFootnotes = std::move(p->footnotes); currentPageFootnotes = std::move(p->footnotes);
@@ -32,6 +32,10 @@ class EpubReaderActivity final : public Activity {
float pendingSpineProgress = 0.0f; float pendingSpineProgress = 0.0f;
bool pendingScreenshot = false; bool pendingScreenshot = false;
bool pendingSyncSaveError = false; bool pendingSyncSaveError = false;
// Consecutive page-load failures. Each failure drops the section and rebuilds on the next render,
// which recovers a transiently corrupt cache; capped so a persistently bad page can't spin forever.
uint8_t pageLoadRetryCount = 0;
static constexpr uint8_t MAX_PAGE_LOAD_RETRIES = 3;
bool skipNextButtonCheck = false; // Skip button processing for one frame after subactivity exit bool skipNextButtonCheck = false; // Skip button processing for one frame after subactivity exit
bool automaticPageTurnActive = false; bool automaticPageTurnActive = false;
bool showBookmarkMessage = false; bool showBookmarkMessage = false;