Merge pull request #210 from jpirnay/chore-cacheclean

chore: Consolidate cache directories
This commit is contained in:
jpirnay
2026-05-14 07:04:44 +02:00
committed by GitHub
9 changed files with 60 additions and 28 deletions
+6
View File
@@ -49,6 +49,9 @@ inline bool hasEpubExtension(const String& fileName) {
// Check for either .xtc or .xtch extension (case-insensitive)
bool hasXtcExtension(std::string_view fileName);
inline bool hasXtcExtension(const String& fileName) {
return hasXtcExtension(std::string_view{fileName.c_str(), fileName.length()});
}
// Check for .txt extension (case-insensitive)
bool hasTxtExtension(std::string_view fileName);
@@ -58,5 +61,8 @@ inline bool hasTxtExtension(const String& fileName) {
// Check for .md extension (case-insensitive)
bool hasMarkdownExtension(std::string_view fileName);
inline bool hasMarkdownExtension(const String& fileName) {
return hasMarkdownExtension(std::string_view{fileName.c_str(), fileName.length()});
}
} // namespace FsHelpers
+1
View File
@@ -37,6 +37,7 @@ build_flags =
# Default is (320*4+1)*2=2562, we need more for larger images
-DPNG_MAX_BUFFERED_PIXELS=16416
-Wno-bidi-chars
-Wno-deprecated-declarations
-Wl,--wrap=panic_print_backtrace,--wrap=panic_abort,--wrap=bootloader_common_check_efuse_blk_validity
-fno-exceptions
+5 -9
View File
@@ -28,11 +28,8 @@
#include "fontIds.h"
namespace {
// Convert a sidecar JPG/PNG cover to a 1-bit BMP in the cache and return the BMP path, or "" on failure.
// fileName is the basename of the output file (without directory), e.g. "340x540.bmp" or "400.bmp".
std::string convertSidecarToBmp(const std::string& bookPath, const std::string& sidecarPath, int width, int height,
std::string convertSidecarToBmp(const std::string& cacheDir, const std::string& sidecarPath, int width, int height,
const std::string& fileName) {
const std::string cacheDir = "/.crosspoint/sidecar_" + std::to_string(std::hash<std::string>{}(bookPath));
Storage.mkdir(cacheDir.c_str());
const std::string bmpPath = cacheDir + "/" + fileName;
if (Storage.exists(bmpPath.c_str())) return bmpPath;
@@ -180,8 +177,7 @@ void HomeActivity::loadRecentBooks(int maxBooks) {
// Also catches books registered before sidecar support (empty coverBmpPath).
const std::string sidecar = ReaderActivity::sidecarCoverPath(book.path);
if (!sidecar.empty()) {
const bool sidecarAlreadyStored =
book.coverBmpPath == sidecar || book.coverBmpPath.find("sidecar_") != std::string::npos;
const bool sidecarAlreadyStored = book.coverBmpPath == sidecar;
LOG_DBG("HOME", "Sidecar for %s: stored=%s alreadyStored=%d", book.path.c_str(), book.coverBmpPath.c_str(),
sidecarAlreadyStored ? 1 : 0);
if (!sidecarAlreadyStored) {
@@ -222,13 +218,13 @@ void HomeActivity::loadRecentCovers(int coverHeight) {
// The cache will be rebuilt on the next render.
UITheme::getInstance().getMutableTheme().invalidateFrameCache();
const std::string cacheBase = "/.crosspoint/sidecar_" + std::to_string(std::hash<std::string>{}(book.path));
const std::string cacheBase = ReaderActivity::bookCacheDir(book.path);
const std::string placeholder = cacheBase + "/[HEIGHT].bmp";
bool success = true;
if (!thumbSizes.empty()) {
for (const auto& sz : thumbSizes) {
const std::string name = std::to_string(sz.first) + "x" + std::to_string(sz.second) + ".bmp";
if (convertSidecarToBmp(book.path, book.coverBmpPath, sz.first, sz.second, name).empty()) {
if (convertSidecarToBmp(cacheBase, book.coverBmpPath, sz.first, sz.second, name).empty()) {
success = false;
break;
}
@@ -236,7 +232,7 @@ void HomeActivity::loadRecentCovers(int coverHeight) {
} else {
const int w = coverHeight * 6 / 10;
const std::string name = std::to_string(coverHeight) + ".bmp";
if (convertSidecarToBmp(book.path, book.coverBmpPath, w, coverHeight, name).empty()) success = false;
if (convertSidecarToBmp(cacheBase, book.coverBmpPath, w, coverHeight, name).empty()) success = false;
}
if (success) {
LOG_DBG("HOME", "Sidecar converted, placeholder: %s", placeholder.c_str());
@@ -65,9 +65,8 @@ std::string findUniquePathWithSuffix(const std::string& basePath) {
std::string findUniqueCompletedSidecarPath(const std::string& basePath) { return findUniquePathWithSuffix(basePath); }
std::string convertSidecarToBmp(const std::string& bookPath, const std::string& sidecarPath, int width, int height,
std::string convertSidecarToBmp(const std::string& cacheDir, const std::string& sidecarPath, int width, int height,
const std::string& fileName) {
const std::string cacheDir = "/.crosspoint/sidecar_" + std::to_string(std::hash<std::string>{}(bookPath));
if (!Storage.exists(cacheDir.c_str())) {
Storage.mkdir(cacheDir.c_str());
}
@@ -120,7 +119,7 @@ std::string getSidecarCoverBmpPath(const std::string& bookPath, int width, int h
}
const std::string fileName = "thumb_" + std::to_string(width) + "x" + std::to_string(height) + ".bmp";
return convertSidecarToBmp(bookPath, sidecarPath, width, height, fileName);
return convertSidecarToBmp(ReaderActivity::bookCacheDir(bookPath), sidecarPath, width, height, fileName);
}
bool moveSidecarFilesToCompleted(const std::string& currentBookPath, const std::string& targetBookPath) {
+6
View File
@@ -71,6 +71,12 @@ std::string ReaderActivity::sidecarCoverPath(const std::string& bookPath) {
return "";
}
std::string ReaderActivity::bookCacheDir(const std::string& bookPath) {
if (FsHelpers::hasEpubExtension(bookPath)) return Epub(bookPath, "/.crosspoint").getCachePath();
if (FsHelpers::hasXtcExtension(bookPath)) return Xtc(bookPath, "/.crosspoint").getCachePath();
return Txt(bookPath, "/.crosspoint").getCachePath();
}
std::unique_ptr<Epub> ReaderActivity::loadEpub(const std::string& path) {
if (!Storage.exists(path.c_str())) {
LOG_ERR("READER", "File does not exist: %s", path.c_str());
+1
View File
@@ -31,6 +31,7 @@ class ReaderActivity final : public Activity {
public:
static std::string sidecarCoverPath(const std::string& bookPath);
static std::string bookCacheDir(const std::string& bookPath);
explicit ReaderActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, std::string initialBookPath)
: Activity("Reader", renderer, mappedInput), initialBookPath(std::move(initialBookPath)) {}
@@ -105,8 +105,8 @@ void ClearCacheActivity::clearCache() {
file.getName(name, sizeof(name));
String itemName(name);
// Only delete directories starting with epub_ or xtc_
if (file.isDirectory() && (itemName.startsWith("epub_") || itemName.startsWith("xtc_"))) {
if (file.isDirectory() &&
(itemName.startsWith("epub_") || itemName.startsWith("xtc_") || itemName.startsWith("txt_"))) {
String fullPath = "/.crosspoint/" + itemName;
LOG_DBG("CLEAR_CACHE", "Removing cache: %s", fullPath.c_str());
+25 -14
View File
@@ -5,7 +5,9 @@
#include <FsHelpers.h>
#include <HalStorage.h>
#include <Logging.h>
#include <Txt.h>
#include <WiFi.h>
#include <Xtc.h>
#include <esp_task_wdt.h>
#include <algorithm>
@@ -58,16 +60,25 @@ String wsLastCompleteName;
size_t wsLastCompleteSize = 0;
unsigned long wsLastCompleteAt = 0;
// Helper function to clear epub cache after upload
void clearEpubCacheIfNeeded(const String& filePath) {
void clearBookCacheIfNeeded(const String& filePath) {
if (FsHelpers::hasEpubExtension(filePath)) {
Epub(filePath.c_str(), "/.crosspoint").clearCache();
LOG_DBG("WEB", "Cleared epub cache for: %s", filePath.c_str());
} else if (FsHelpers::hasXtcExtension(filePath)) {
Xtc(filePath.c_str(), "/.crosspoint").clearCache();
LOG_DBG("WEB", "Cleared xtc cache for: %s", filePath.c_str());
} else if (FsHelpers::hasTxtExtension(filePath) || FsHelpers::hasMarkdownExtension(filePath)) {
const Txt txt(filePath.c_str(), "/.crosspoint");
const String cachePath = txt.getCachePath().c_str();
if (Storage.exists(cachePath.c_str())) {
Storage.removeDir(cachePath.c_str());
LOG_DBG("WEB", "Cleared txt cache for: %s", filePath.c_str());
}
}
}
// Recursively clear epub caches for all EPUBs inside a directory
void clearEpubCachesInDirectory(const String& dirPath) {
// Recursively clear book caches for all ebooks inside a directory
void clearBookCachesInDirectory(const String& dirPath) {
esp_task_wdt_reset();
yield();
FsFile dir = Storage.open(dirPath.c_str());
@@ -86,10 +97,10 @@ void clearEpubCachesInDirectory(const String& dirPath) {
childPath += name;
if (entry.isDirectory()) {
entry.close();
clearEpubCachesInDirectory(childPath);
clearBookCachesInDirectory(childPath);
} else {
entry.close();
clearEpubCacheIfNeeded(childPath);
clearBookCacheIfNeeded(childPath);
}
entry = dir.openNextFile();
}
@@ -847,7 +858,7 @@ void CrossPointWebServer::handleUpload(UploadState& state) const {
String filePath = state.path;
if (!filePath.endsWith("/")) filePath += "/";
filePath += state.fileName;
clearEpubCacheIfNeeded(filePath);
clearBookCacheIfNeeded(filePath);
}
}
} else if (upload.status == UPLOAD_FILE_ABORTED) {
@@ -990,9 +1001,9 @@ void CrossPointWebServer::handleRename() const {
}
if (isDir) {
clearEpubCachesInDirectory(itemPath);
clearBookCachesInDirectory(itemPath);
} else {
clearEpubCacheIfNeeded(itemPath);
clearBookCacheIfNeeded(itemPath);
}
const bool success = file.rename(newPath.c_str());
file.close();
@@ -1088,9 +1099,9 @@ void CrossPointWebServer::handleMove() const {
}
if (isDir) {
clearEpubCachesInDirectory(itemPath);
clearBookCachesInDirectory(itemPath);
} else {
clearEpubCacheIfNeeded(itemPath);
clearBookCacheIfNeeded(itemPath);
}
const bool success = file.rename(newPath.c_str());
file.close();
@@ -1210,7 +1221,7 @@ void CrossPointWebServer::handleDelete() const {
// It's a file (or couldn't open as dir) — remove file
if (f) f.close();
success = Storage.remove(itemPath.c_str());
clearEpubCacheIfNeeded(itemPath);
clearBookCacheIfNeeded(itemPath);
}
if (!success) {
@@ -2397,7 +2408,7 @@ void CrossPointWebServer::onWebSocketEvent(uint8_t num, WStype_t type, uint8_t*
wsLastCompleteSize = 0;
wsLastCompleteAt = millis();
LOG_DBG("WS", "Zero-byte upload complete: %s", filePath.c_str());
clearEpubCacheIfNeeded(filePath);
clearBookCacheIfNeeded(filePath);
wsServer->sendTXT(num, "DONE");
wsLastProgressSent = 0;
break;
@@ -2465,7 +2476,7 @@ void CrossPointWebServer::onWebSocketEvent(uint8_t num, WStype_t type, uint8_t*
String filePath = wsUploadPath;
if (!filePath.endsWith("/")) filePath += "/";
filePath += wsUploadFileName;
clearEpubCacheIfNeeded(filePath);
clearBookCacheIfNeeded(filePath);
wsServer->sendTXT(num, "DONE");
wsLastProgressSent = 0;
+12
View File
@@ -4,6 +4,8 @@
#include <FsHelpers.h>
#include <HalStorage.h>
#include <Logging.h>
#include <Txt.h>
#include <Xtc.h>
#include <esp_task_wdt.h>
namespace {
@@ -802,6 +804,16 @@ void WebDAVHandler::clearEpubCacheIfNeeded(const String& path) const {
if (FsHelpers::hasEpubExtension(path)) {
Epub(path.c_str(), "/.crosspoint").clearCache();
LOG_DBG("DAV", "Cleared epub cache for: %s", path.c_str());
} else if (FsHelpers::hasXtcExtension(path)) {
Xtc(path.c_str(), "/.crosspoint").clearCache();
LOG_DBG("DAV", "Cleared xtc cache for: %s", path.c_str());
} else if (FsHelpers::hasTxtExtension(path) || FsHelpers::hasMarkdownExtension(path)) {
const Txt txt(path.c_str(), "/.crosspoint");
const String cachePath = txt.getCachePath().c_str();
if (Storage.exists(cachePath.c_str())) {
Storage.removeDir(cachePath.c_str());
LOG_DBG("DAV", "Cleared txt cache for: %s", path.c_str());
}
}
}