Files
bookhoard/web/src/reader/comic/image-parser.ts
T
john-okeefe 7eee7c8e39 fix: Rename image-parter.ts to image-parser.ts
Reader shell expects image-parser module. Renamed file to match
import expectations.
2026-04-04 14:21:04 -04:00

82 lines
2.5 KiB
TypeScript

// 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<ComicReader> {
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<MangaReader> {
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<Blob[]> {
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;
}