From e7618d5e515c1e4154e3f9819b5c045b89e65f80 Mon Sep 17 00:00:00 2001 From: Justin Oros Date: Thu, 23 Apr 2026 15:49:48 -0700 Subject: [PATCH] fix(hardcover): skip epub load for books without progress, eliminate double scan and load --- plugins/hardcover/HardcoverPlugin.cpp | 79 +++++++++++---------------- 1 file changed, 33 insertions(+), 46 deletions(-) diff --git a/plugins/hardcover/HardcoverPlugin.cpp b/plugins/hardcover/HardcoverPlugin.cpp index 5d923bb..8c716cf 100644 --- a/plugins/hardcover/HardcoverPlugin.cpp +++ b/plugins/hardcover/HardcoverPlugin.cpp @@ -75,13 +75,7 @@ std::string extractIsbnFromOpf(const uint8_t* data, size_t len) { return {}; } -std::string getIsbnFromEpub(const std::string& path) { - Epub epub(path, CACHE_DIR); - if (!epub.load(false, true) && !epub.load(true, true)) { - LOG_DBG("HCV", "Could not load epub: %s", path.c_str()); - return {}; - } - +std::string getIsbnFromEpub(Epub& epub, const std::string& path) { const char* opfCandidates[] = { "OEBPS/content.opf", "OPS/content.opf", "content.opf", "EPUB/content.opf", nullptr @@ -124,7 +118,7 @@ std::string getIsbnFromEpub(const std::string& path) { // ---- Progress reading ------------------------------------------------------- -int epubProgressPercent(const std::string& path, const std::string& cachePath, int& outPage) { +int epubProgressPercent(const std::string& cachePath, int& outPage, Epub& epub) { FsFile f; if (!Storage.openFileForRead("HCV", (cachePath + "/progress.bin").c_str(), f)) return -1; @@ -142,9 +136,6 @@ int epubProgressPercent(const std::string& path, const std::string& cachePath, i float spineProgress = static_cast(currentPage) / static_cast(pageCount); - Epub epub(path, CACHE_DIR); - if (!epub.load(false, true) && !epub.load(true, true)) return -1; - float pct = epub.calculateProgress(spineIndex, spineProgress) * 100.0f; return static_cast(pct + 0.5f); } @@ -179,43 +170,33 @@ std::string buildCachePath(const std::string& filePath) { std::to_string(std::hash{}(filePath)); } -std::vector collectInProgressBooks() { +void collectBooks(std::vector& inProgress, std::vector& completed) { std::vector allBooks; scanBooks("/", allBooks); - std::vector result; for (const auto& path : allBooks) { std::string cachePath = buildCachePath(path); - int page = 0; - int pct = epubProgressPercent(path, cachePath, page); - if (pct < 1) continue; // Skip 0% books, include 1-100%+ - std::string isbn = getIsbnFromEpub(path); + // Check progress.bin exists before loading the EPUB + FsFile f; + if (!Storage.openFileForRead("HCV", (cachePath + "/progress.bin").c_str(), f)) continue; + f.close(); + + int page = 0; + Epub epub(path, CACHE_DIR); + if (!epub.load(false, true) && !epub.load(true, true)) continue; + + int pct = epubProgressPercent(cachePath, page, epub); + if (pct < 1) continue; + + std::string isbn = getIsbnFromEpub(epub, path); if (isbn.empty()) continue; - result.push_back({isbn, page, pct}); + if (pct >= 100) + completed.push_back({isbn, page, pct}); + else + inProgress.push_back({isbn, page, pct}); } - return result; -} - -// New function to collect completed books (100%+) -std::vector collectCompletedBooks() { - std::vector allBooks; - scanBooks("/", allBooks); - - std::vector result; - for (const auto& path : allBooks) { - std::string cachePath = buildCachePath(path); - int page = 0; - int pct = epubProgressPercent(path, cachePath, page); - if (pct < 100) continue; // Only include 100%+ books - - std::string isbn = getIsbnFromEpub(path); - if (isbn.empty()) continue; - - result.push_back({isbn, page, pct}); - } - return result; } // ---- HTTP helper ----------------------------------------------------------- @@ -223,9 +204,11 @@ std::vector collectCompletedBooks() { String graphqlPost(const char* body, const std::string& token) { WiFiClientSecure client; client.setInsecure(); + client.setTimeout(15); HTTPClient http; http.begin(client, HARDCOVER_API); + http.setTimeout(15000); http.addHeader("Content-Type", "application/json"); char authHeader[648]; @@ -233,6 +216,11 @@ String graphqlPost(const char* body, const std::string& token) { http.addHeader("Authorization", authHeader); int code = http.POST(body); + if (code <= 0) { + LOG_ERR("HCV", "HTTP error %d (connection failed — check WiFi/firewall)", code); + http.end(); + return ""; + } if (code != 200) { LOG_ERR("HCV", "HTTP %d", code); http.end(); @@ -482,9 +470,11 @@ SyncResult syncProgress() { } const std::string token(SETTINGS.hardcoverApiToken); - auto books = collectInProgressBooks(); + std::vector books; + std::vector completedBooks; + collectBooks(books, completedBooks); - if (books.empty()) { + if (books.empty() && completedBooks.empty()) { LOG_DBG("HCV", "No in-progress epub books with ISBN found."); return SyncResult::NO_BOOKS; } @@ -493,15 +483,12 @@ SyncResult syncProgress() { if (userId == 0) return SyncResult::API_ERROR; bool allOk = true; - - // Sync progress for all books with > 0% completion + for (const auto& b : books) { if (!syncBook(b, userId, token)) allOk = false; } - - // Mark 100%+ books as Read - auto completedBooks = collectCompletedBooks(); + for (const auto& b : completedBooks) { if (!markAsRead(b.isbn, token)) allOk = false;