Fix infinite recursion bug where exported function called itself. Renamed to getTXTMetadata to avoid duplicate declaration.
122 lines
2.9 KiB
TypeScript
122 lines
2.9 KiB
TypeScript
// TXT Parser - Wraps plain text in HTML structure
|
|
// Procedural style: Functions, not classes
|
|
|
|
// ============================================================
|
|
// Main Parse Function
|
|
// ============================================================
|
|
|
|
export async function parseTXT(txtBlob: Blob): Promise<EbookCIF> {
|
|
const textContent = await txtBlob.text();
|
|
|
|
const metadata = extractTXTMetadata(txtBlob);
|
|
const toc = createTXTTOC(textContent);
|
|
const spine = createTXTSpine(textContent);
|
|
const resources = new Map(); // No external resources for plain text
|
|
|
|
const totalCharacters = textContent.length;
|
|
|
|
return {
|
|
metadata,
|
|
toc,
|
|
spine,
|
|
resources,
|
|
locations: {
|
|
totalCharacters,
|
|
estimatedPages: Math.ceil(totalCharacters / 1500),
|
|
},
|
|
};
|
|
}
|
|
|
|
// ============================================================
|
|
// Helper Functions
|
|
// ============================================================
|
|
|
|
function extractTXTMetadata(txtBlob: Blob): EbookCIF["metadata"] {
|
|
const filename = txtBlob.name || "Unknown";
|
|
|
|
return {
|
|
title: filename.replace(/\.(txt|text)$/i, ""),
|
|
author: "Unknown",
|
|
language: "en",
|
|
};
|
|
}
|
|
|
|
function createTXTTOC(textContent: string): EbookCIF["toc"] {
|
|
// Try to detect chapters (simple heuristic)
|
|
const toc: EbookCIF["toc"] = [];
|
|
const lines = textContent.split("\n");
|
|
|
|
let chapterIndex = 0;
|
|
|
|
lines.forEach((line, index) => {
|
|
// Common chapter patterns
|
|
const chapterPattern = /^(chapter|part|section)\s+\d+/i;
|
|
if (chapterPattern.test(line.trim())) {
|
|
toc.push({
|
|
id: `chapter-${chapterIndex}`,
|
|
title: line.trim(),
|
|
href: `#chapter-${chapterIndex}`,
|
|
children: [],
|
|
});
|
|
|
|
chapterIndex++;
|
|
}
|
|
});
|
|
|
|
// If no chapters found, create single entry
|
|
if (toc.length === 0) {
|
|
toc.push({
|
|
id: "full-text",
|
|
title: "Full Text",
|
|
href: "#full-text",
|
|
children: [],
|
|
});
|
|
}
|
|
|
|
return toc;
|
|
}
|
|
|
|
function createTXTSpine(textContent: string): EbookCIF["spine"] {
|
|
// Convert plain text to HTML paragraphs
|
|
const lines = textContent.split("\n");
|
|
let htmlContent = '<div class="txt-content">';
|
|
|
|
lines.forEach((line) => {
|
|
const trimmed = line.trim();
|
|
if (trimmed) {
|
|
htmlContent += `<p>${escapeHTML(trimmed)}</p>`;
|
|
} else {
|
|
htmlContent += "<br />";
|
|
}
|
|
});
|
|
|
|
htmlContent += "</div>";
|
|
|
|
return [
|
|
{
|
|
id: "full-text",
|
|
type: "html",
|
|
content: htmlContent,
|
|
index: 0,
|
|
},
|
|
];
|
|
}
|
|
|
|
function escapeHTML(text: string): string {
|
|
const div = document.createElement("div");
|
|
div.textContent = text;
|
|
return div.innerHTML;
|
|
}
|
|
|
|
// Removed - backend handles detailed position tracking
|
|
|
|
// ============================================================
|
|
// Metadata Quick Extract
|
|
// ============================================================
|
|
|
|
export async function getTXTMetadata(
|
|
txtBlob: Blob,
|
|
): Promise<Partial<EbookCIF["metadata"]>> {
|
|
return extractTXTMetadata(txtBlob);
|
|
}
|