Compare commits

..

2 Commits

Author SHA1 Message Date
CaptainFrito
328f78365d
Merge 111d4b8b16 into f67c544e16 2026-02-03 06:24:59 +07:00
CaptainFrito
111d4b8b16 Fixed recent books migration 2026-02-03 06:24:47 +07:00
3 changed files with 74 additions and 70 deletions

View File

@ -1,11 +1,15 @@
#include "RecentBooksStore.h" #include "RecentBooksStore.h"
#include <Epub.h>
#include <HardwareSerial.h> #include <HardwareSerial.h>
#include <SDCardManager.h> #include <SDCardManager.h>
#include <Serialization.h> #include <Serialization.h>
#include <Xtc.h>
#include <algorithm> #include <algorithm>
#include "util/StringUtils.h"
namespace { namespace {
constexpr uint8_t RECENT_BOOKS_FILE_VERSION = 3; constexpr uint8_t RECENT_BOOKS_FILE_VERSION = 3;
constexpr char RECENT_BOOKS_FILE[] = "/.crosspoint/recent.bin"; constexpr char RECENT_BOOKS_FILE[] = "/.crosspoint/recent.bin";
@ -59,6 +63,34 @@ bool RecentBooksStore::saveToFile() const {
return true; return true;
} }
RecentBook RecentBooksStore::getDataFromBook(std::string path) const {
std::string lastBookFileName = "";
const size_t lastSlash = path.find_last_of('/');
if (lastSlash != std::string::npos) {
lastBookFileName = path.substr(lastSlash + 1);
}
Serial.printf("Loading recent book: %s\n", path.c_str());
// If epub, try to load the metadata for title/author and cover
if (StringUtils::checkFileExtension(lastBookFileName, ".epub")) {
Epub epub(path, "/.crosspoint");
epub.load(false);
return RecentBook{path, epub.getTitle(), epub.getAuthor(), epub.getThumbBmpPath()};
} else if (StringUtils::checkFileExtension(lastBookFileName, ".xtch") ||
StringUtils::checkFileExtension(lastBookFileName, ".xtc")) {
// Handle XTC file
Xtc xtc(path, "/.crosspoint");
if (xtc.load()) {
return RecentBook{path, xtc.getTitle(), xtc.getAuthor(), xtc.getThumbBmpPath()};
}
} else if (StringUtils::checkFileExtension(lastBookFileName, ".txt") ||
StringUtils::checkFileExtension(lastBookFileName, ".md")) {
return RecentBook{path, lastBookFileName, "", ""};
}
return RecentBook{path, "", "", ""};
}
bool RecentBooksStore::loadFromFile() { bool RecentBooksStore::loadFromFile() {
FsFile inputFile; FsFile inputFile;
if (!SdMan.openFileForRead("RBS", RECENT_BOOKS_FILE, inputFile)) { if (!SdMan.openFileForRead("RBS", RECENT_BOOKS_FILE, inputFile)) {
@ -68,7 +100,7 @@ bool RecentBooksStore::loadFromFile() {
uint8_t version; uint8_t version;
serialization::readPod(inputFile, version); serialization::readPod(inputFile, version);
if (version != RECENT_BOOKS_FILE_VERSION) { if (version != RECENT_BOOKS_FILE_VERSION) {
if (version == 1) { if (version == 1 || version == 2) {
// Old version, just read paths // Old version, just read paths
uint8_t count; uint8_t count;
serialization::readPod(inputFile, count); serialization::readPod(inputFile, count);
@ -77,25 +109,18 @@ bool RecentBooksStore::loadFromFile() {
for (uint8_t i = 0; i < count; i++) { for (uint8_t i = 0; i < count; i++) {
std::string path; std::string path;
serialization::readString(inputFile, path); serialization::readString(inputFile, path);
// Title and author will be empty, they will be filled when the book is
// opened again // load book to get missing data
std::string fileName = path.substr(path.rfind('/') + 1); RecentBook book = getDataFromBook(path);
recentBooks.push_back({path, fileName, "", "-"}); if (book.title.empty() && book.author.empty() && version == 2) {
} // Fall back to loading what we can from the store
} else if (version == 2) { std::string title, author;
// Old version, just read paths, titles and authors
uint8_t count;
serialization::readPod(inputFile, count);
recentBooks.clear();
recentBooks.reserve(count);
for (uint8_t i = 0; i < count; i++) {
std::string path, title, author;
serialization::readString(inputFile, path);
serialization::readString(inputFile, title); serialization::readString(inputFile, title);
serialization::readString(inputFile, author); serialization::readString(inputFile, author);
// Title and author will be empty, they will be filled when the book is recentBooks.push_back({path, title, author, ""});
// opened again } else {
recentBooks.push_back({path, title, author, "-"}); recentBooks.push_back(book);
}
} }
} else { } else {
Serial.printf("[%lu] [RBS] Deserialization failed: Unknown version %u\n", millis(), version); Serial.printf("[%lu] [RBS] Deserialization failed: Unknown version %u\n", millis(), version);

View File

@ -36,6 +36,7 @@ class RecentBooksStore {
bool saveToFile() const; bool saveToFile() const;
bool loadFromFile(); bool loadFromFile();
RecentBook getDataFromBook(std::string path) const;
}; };
// Helper macro to access recent books store // Helper macro to access recent books store

View File

@ -44,11 +44,8 @@ void HomeActivity::loadRecentBooks(int maxBooks, int coverHeight) {
const auto& books = RECENT_BOOKS.getBooks(); const auto& books = RECENT_BOOKS.getBooks();
recentBooks.reserve(std::min(static_cast<int>(books.size()), maxBooks)); recentBooks.reserve(std::min(static_cast<int>(books.size()), maxBooks));
bool mustReloadRecents = false; // We're migrating from an older version of recent books
int progress = 0; int progress = 0;
for (const RecentBook& book : books) { for (const RecentBook& book : books) {
bool mustLoadCover = false;
// Limit to maximum number of recent books // Limit to maximum number of recent books
if (recentBooks.size() >= maxBooks) { if (recentBooks.size() >= maxBooks) {
break; break;
@ -59,16 +56,9 @@ void HomeActivity::loadRecentBooks(int maxBooks, int coverHeight) {
continue; continue;
} }
if (book.coverBmpPath == "-") { if (!book.coverBmpPath.empty()) {
mustReloadRecents = true;
} else {
std::string coverPath = UITheme::getCoverThumbPath(book.coverBmpPath, coverHeight); std::string coverPath = UITheme::getCoverThumbPath(book.coverBmpPath, coverHeight);
if (!SdMan.exists(coverPath.c_str())) { if (!SdMan.exists(coverPath.c_str())) {
mustLoadCover = true;
}
}
if (mustReloadRecents || mustLoadCover) {
std::string lastBookFileName = ""; std::string lastBookFileName = "";
const size_t lastSlash = book.path.find_last_of('/'); const size_t lastSlash = book.path.find_last_of('/');
if (lastSlash != std::string::npos) { if (lastSlash != std::string::npos) {
@ -87,14 +77,8 @@ void HomeActivity::loadRecentBooks(int maxBooks, int coverHeight) {
showingLoading = true; showingLoading = true;
popupRect = GUI.drawPopup(renderer, "Loading..."); popupRect = GUI.drawPopup(renderer, "Loading...");
} }
GUI.fillPopupProgress(renderer, popupRect, progress * (100 / maxBooks)); GUI.fillPopupProgress(renderer, popupRect, progress * 30);
epub.generateThumbBmp(coverHeight); epub.generateThumbBmp(coverHeight);
if (mustReloadRecents) {
// Update recent book entry with title/author/cover path
RECENT_BOOKS.addBook(book.path, epub.getTitle(), epub.getAuthor(), epub.getThumbBmpPath());
recentBooks.push_back({book.path, epub.getTitle(), epub.getAuthor(), epub.getThumbBmpPath()});
}
} else if (StringUtils::checkFileExtension(lastBookFileName, ".xtch") || } else if (StringUtils::checkFileExtension(lastBookFileName, ".xtch") ||
StringUtils::checkFileExtension(lastBookFileName, ".xtc")) { StringUtils::checkFileExtension(lastBookFileName, ".xtc")) {
// Handle XTC file // Handle XTC file
@ -105,24 +89,18 @@ void HomeActivity::loadRecentBooks(int maxBooks, int coverHeight) {
showingLoading = true; showingLoading = true;
popupRect = GUI.drawPopup(renderer, "Loading..."); popupRect = GUI.drawPopup(renderer, "Loading...");
} }
GUI.fillPopupProgress(renderer, popupRect, progress * (100 / maxBooks)); GUI.fillPopupProgress(renderer, popupRect, progress * 30);
xtc.generateThumbBmp(coverHeight); xtc.generateThumbBmp(coverHeight);
if (mustReloadRecents) {
// Update recent book entry with title/author/cover path
RECENT_BOOKS.addBook(book.path, xtc.getTitle(), xtc.getAuthor(), xtc.getThumbBmpPath());
recentBooks.push_back({book.path, xtc.getTitle(), xtc.getAuthor(), xtc.getThumbBmpPath()});
} }
} }
} }
} }
if (!mustReloadRecents) {
recentBooks.push_back(book); recentBooks.push_back(book);
}
progress++; progress++;
} }
Serial.printf("Recent books loaded: %d\n", recentBooks.size());
recentsLoaded = true; recentsLoaded = true;
recentsLoading = false; recentsLoading = false;
updateRequired = true; updateRequired = true;