Fix initial horrible performance for books without a cover

This commit is contained in:
jpirnay
2026-05-04 23:59:51 +02:00
parent cd78679973
commit 2a443da291
+62 -23
View File
@@ -76,6 +76,7 @@ bool Epub::findContentOpfFile(std::string* contentOpfFile) const {
} }
bool Epub::parseContentOpf(BookMetadataCache::BookMetadata& bookMetadata, OpfCacheMode cacheMode) { bool Epub::parseContentOpf(BookMetadataCache::BookMetadata& bookMetadata, OpfCacheMode cacheMode) {
const unsigned long opfParseStart = millis();
std::string contentOpfFilePath; std::string contentOpfFilePath;
if (!findContentOpfFile(&contentOpfFilePath)) { if (!findContentOpfFile(&contentOpfFilePath)) {
LOG_ERR("EBP", "Could not find content.opf in zip"); LOG_ERR("EBP", "Could not find content.opf in zip");
@@ -91,6 +92,7 @@ bool Epub::parseContentOpf(BookMetadataCache::BookMetadata& bookMetadata, OpfCac
LOG_ERR("EBP", "Could not get size of content.opf"); LOG_ERR("EBP", "Could not get size of content.opf");
return false; return false;
} }
LOG_DBG("EBP", "content.opf size=%zu bytes", contentOpfSize);
ContentOpfParser opfParser(getCachePath(), getBasePath(), contentOpfSize, ContentOpfParser opfParser(getCachePath(), getBasePath(), contentOpfSize,
cacheMode == OpfCacheMode::Enabled ? bookMetadataCache.get() : nullptr); cacheMode == OpfCacheMode::Enabled ? bookMetadataCache.get() : nullptr);
@@ -99,10 +101,18 @@ bool Epub::parseContentOpf(BookMetadataCache::BookMetadata& bookMetadata, OpfCac
return false; return false;
} }
const unsigned long streamStart = millis();
if (!readItemContentsToStream(contentOpfFilePath, opfParser, 1024)) { if (!readItemContentsToStream(contentOpfFilePath, opfParser, 1024)) {
LOG_ERR("EBP", "Could not read content.opf"); LOG_ERR("EBP", "Could not read content.opf");
return false; return false;
} }
const unsigned long streamMs = millis() - streamStart;
LOG_DBG("EBP",
"content.opf stream=%lu ms, parser.write_calls=%zu, bytes=%zu, parse_buffer=%lu ms, manifest_open=%lu ms, "
"spine_open=%lu ms, guide_open=%lu ms, itemrefs=%zu, itemref_lookup=%lu ms, create_spine=%lu ms",
streamMs, opfParser.stats.writeCalls, opfParser.stats.bytesParsed, opfParser.stats.parseBufferMs,
opfParser.stats.manifestOpenMs, opfParser.stats.spineOpenMs, opfParser.stats.guideOpenMs,
opfParser.stats.itemRefCount, opfParser.stats.itemRefLookupMs, opfParser.stats.createSpineEntryMs);
// Grab data from opfParser into epub // Grab data from opfParser into epub
bookMetadata.title = opfParser.title; bookMetadata.title = opfParser.title;
@@ -241,34 +251,62 @@ bool Epub::parseContentOpf(BookMetadataCache::BookMetadata& bookMetadata, OpfCac
return value; return value;
}; };
for (const auto& baseDir : baseDirs) { const unsigned long coverBatchStart = millis();
for (const char* subDir : kCoverSubdirs) { ZipFile zip(filepath);
for (const char* baseName : kCoverBaseNames) { const bool zipIndexLoaded = zip.loadAllFileStatSlims();
const std::string lowerBase = baseName; LOG_DBG("EBP", "Common cover fallback indexed ZIP in %lu ms (ok=%d)", millis() - coverBatchStart,
const std::string upperBase = toUpper(lowerBase); zipIndexLoaded ? 1 : 0);
for (const std::string* baseVariant : {&lowerBase, &upperBase}) {
for (const char* ext : kCoverExtensions) { if (zipIndexLoaded) {
const std::string lowerExt = ext; int checkedCandidates = 0;
const std::string upperExt = toUpper(lowerExt); const auto tryCandidate = [&](const std::string& candidate) {
for (const std::string* extVariant : {&lowerExt, &upperExt}) { checkedCandidates++;
const std::string candidate = size_t coverSize = 0;
FsHelpers::normalisePath(baseDir + subDir + *baseVariant + "." + *extVariant); if (zip.getInflatedFileSize(candidate.c_str(), &coverSize) && coverSize > 0) {
if (hasReadableSupportedCover(candidate)) { bookMetadata.coverItemHref = candidate;
bookMetadata.coverItemHref = candidate; LOG_DBG("EBP", "Found cover image via common candidate fallback after %d checks in %lu ms: %s",
LOG_DBG("EBP", "Found cover image via common candidate fallback: %s", checkedCandidates, millis() - coverBatchStart, bookMetadata.coverItemHref.c_str());
bookMetadata.coverItemHref.c_str()); return true;
goto cover_fallback_done;
}
}
}
}
} }
return false;
};
bool foundCoverCandidate = false;
for (const auto& baseDir : baseDirs) {
for (const char* subDir : kCoverSubdirs) {
for (const char* baseName : kCoverBaseNames) {
const std::string lowerBase = baseName;
const std::string upperBase = toUpper(lowerBase);
for (const std::string* baseVariant : {&lowerBase, &upperBase}) {
for (const char* ext : kCoverExtensions) {
const std::string lowerExt = ext;
const std::string upperExt = toUpper(lowerExt);
for (const std::string* extVariant : {&lowerExt, &upperExt}) {
const std::string candidate =
FsHelpers::normalisePath(baseDir + subDir + *baseVariant + "." + *extVariant);
if (tryCandidate(candidate)) {
foundCoverCandidate = true;
break;
}
}
if (foundCoverCandidate) break;
}
if (foundCoverCandidate) break;
}
if (foundCoverCandidate) break;
}
if (foundCoverCandidate) break;
}
if (foundCoverCandidate) break;
}
if (!foundCoverCandidate) {
LOG_DBG("EBP", "Common cover fallback checked %d cached candidates in %lu ms with no match", checkedCandidates,
millis() - coverBatchStart);
} }
} }
} }
cover_fallback_done:
bookMetadata.textReferenceHref = opfParser.textReferenceHref; bookMetadata.textReferenceHref = opfParser.textReferenceHref;
if (!opfParser.tocNcxPath.empty()) { if (!opfParser.tocNcxPath.empty()) {
@@ -283,6 +321,7 @@ cover_fallback_done:
cssFiles = opfParser.cssFiles; cssFiles = opfParser.cssFiles;
} }
LOG_DBG("EBP", "parseContentOpf total=%lu ms", millis() - opfParseStart);
LOG_DBG("EBP", "Successfully parsed content.opf"); LOG_DBG("EBP", "Successfully parsed content.opf");
return true; return true;
} }