// Comic/Manga Reader - Image-based pages // Handles CBZ, comic archives, image directories interface ReaderMetadata { media_item_id: string; title: string; author: string; cover_image_path: string; library_type: "ebook" | "comic" | "manga" | "pdf"; mime_type: string; file_path: string; total_pages?: number; } interface ComicReader { type: "comic"; images: Blob[]; currentPage: number; } interface MangaReader { type: "manga"; images: Blob[]; currentPage: number; readingDirection: "rtl" | "vertical"; } // ============================================================ // Comic Reader Initialization // ============================================================ export async function initializeComicReader( metadata: ReaderMetadata, ): Promise { const response = await fetch(metadata.file_path); const archiveBlob = await response.blob(); // Parse comic archive (CBZ) or image directory const images = await parseComicArchive(archiveBlob); return { type: "comic", images, currentPage: 1, }; } // ============================================================ // Manga Reader Initialization // ============================================================ export async function initializeMangaReader( metadata: ReaderMetadata, ): Promise { const response = await fetch(metadata.file_path); const archiveBlob = await response.blob(); const images = await parseComicArchive(archiveBlob); return { type: "manga", images, currentPage: 1, readingDirection: "rtl", // Default for manga }; } // ============================================================ // Comic Archive Parser // ============================================================ async function parseComicArchive(archiveBlob: Blob): Promise { const JSZip = (await import("jszip")).default; const zip = await JSZip.loadAsync(archiveBlob); const images: Blob[] = []; // Get all image files from archive const files = Object.keys(zip.files).filter((filename) => filename.match(/\.(jpg|jpeg|png|gif|webp)$/i), ); // Sort files naturally (page-01.jpg, page-02.jpg, etc.) files.sort((a, b) => { const aName = a.split("/").pop() || a; const bName = b.split("/").pop() || b; return aName.localeCompare(bName, undefined, { numeric: true }); }); // Extract images for (const file of files) { const fileData = await zip.file(file)?.async("blob"); if (fileData) { images.push(fileData); } } return images; }