import { marked } from "marked"; import { mediaUrl } from "@/app/lib/media"; export interface EbookChunk { html: string; estimatedHeight: number; } export interface EbookData { chunks: EbookChunk[]; headers: string[]; } function rewriteMediaTag(tag: string): string { return tag.replace(/\s(src|poster)=(["'])(.*?)\2/gi, (match, attr: string, quote: string, value: string) => { const rewritten = mediaUrl(value); return rewritten ? ` ${attr}=${quote}${rewritten}${quote}` : match; }); } function rewriteEmbeddedMedia(html: string): string { return html.replace(/<(img|video|audio|source)\b[^>]*>/gi, rewriteMediaTag); } export function parseEbookMarkdown(source: string): EbookData { const html = marked.parse(source, { breaks: true, gfm: true }) as string; const mediaHtml = rewriteEmbeddedMedia(html); const lazyHtml = mediaHtml.replace(/])/i).filter((c) => c.trim()); if (rawChunks.length <= 1) { rawChunks = lazyHtml.split(/(?=])/i).filter((c) => c.trim()); } const headers: string[] = []; const headerRegex = /]*>(.*?)<\/h1>/gi; let m; while ((m = headerRegex.exec(lazyHtml)) !== null) { headers.push(m[1].replace(/<[^>]+>/g, "")); } if (headers.length === 0) { const h2Regex = /]*>(.*?)<\/h2>/gi; while ((m = h2Regex.exec(lazyHtml)) !== null) { headers.push(m[1].replace(/<[^>]+>/g, "")); } } const chunks: EbookChunk[] = rawChunks.map((chunkHtml) => { const imgCount = (chunkHtml.match(/]+>/g, "").length; const estimatedHeight = Math.max(300, Math.round(textLen * 0.6 + imgCount * 350)); return { html: chunkHtml, estimatedHeight }; }); return { chunks, headers }; }