reader: implement Feature Registration Pattern infrastructure
- Add Feature Registry system in reader-shell.ts to track features - Update format detection to use format_group with switch statement - Add manga detection via library_type_name or manga_type fields - Import and register features via init() functions on load - Fix parser imports in parser-manager.ts (add missing function imports) - Simplify epub-parsers and fb2-parser by removing unused imports
This commit is contained in:
@@ -1,13 +1,12 @@
|
||||
// EPUB Parser - Converts EPUB 2/3 to Common Intermediate Format
|
||||
// Procedural style: Functions, not classes
|
||||
|
||||
import JSZip from 'jszip';
|
||||
|
||||
// ============================================================
|
||||
// Main Parse Function
|
||||
// ============================================================
|
||||
|
||||
export async function parseEPUB(epubBlob: Blob): Promise<EbookCIF> {
|
||||
const JSZip = (await import('jszip')).default;
|
||||
const zip = await JSZip.loadAsync(epubBlob);
|
||||
|
||||
// Parse container.xml to find OPF file
|
||||
@@ -18,11 +17,9 @@ export async function parseEPUB(epubBlob: Blob): Promise<EbookCIF> {
|
||||
throw new Error('Invalid EPUB: no OPF file found');
|
||||
}
|
||||
|
||||
// Parse OPF file
|
||||
const opfXml = await getZipFileContent(zip, opfPath);
|
||||
const packageDoc = parseXML(opfXml);
|
||||
|
||||
// Extract all components
|
||||
const metadata = extractMetadata(packageDoc);
|
||||
const spine = parseSpine(packageDoc);
|
||||
const toc = await parseTOC(zip, packageDoc, opfPath);
|
||||
@@ -48,7 +45,7 @@ export async function parseEPUB(epubBlob: Blob): Promise<EbookCIF> {
|
||||
// Helper Functions
|
||||
// ============================================================
|
||||
|
||||
async function getZipFileContent(zip: JSZip, path: string): Promise<string> {
|
||||
async function getZipFileContent(zip: any, path: string): Promise<string> {
|
||||
const file = zip.file(path);
|
||||
if (!file) {
|
||||
throw new Error(`File not found: ${path}`);
|
||||
@@ -113,7 +110,7 @@ function parseSpine(packageDoc: XMLDocument): EbookCIF['spine'] {
|
||||
return result;
|
||||
}
|
||||
|
||||
async function parseTOC(zip: JSZip, packageDoc: XMLDocument, opfPath: string): Promise<EbookCIF['toc']> {
|
||||
async function parseTOC(zip: any, packageDoc: XMLDocument, opfPath: string): Promise<EbookCIF['toc']> {
|
||||
// Try EPUB 3.0 navigation document first
|
||||
const navItem = packageDoc.querySelector('manifest item[properties~="nav"]');
|
||||
if (navItem) {
|
||||
@@ -140,7 +137,7 @@ async function parseTOC(zip: JSZip, packageDoc: XMLDocument, opfPath: string): P
|
||||
return [];
|
||||
}
|
||||
|
||||
async function parseNavTOC(zip: JSZip, navPath: string): Promise<EbookCIF['toc']> {
|
||||
async function parseNavTOC(zip: any, navPath: string): Promise<EbookCIF['toc']> {
|
||||
const navXml = await getZipFileContent(zip, navPath);
|
||||
const navDoc = parseXML(navXml);
|
||||
const nav = navDoc.querySelector('nav');
|
||||
@@ -168,7 +165,7 @@ async function parseNavTOC(zip: JSZip, navPath: string): Promise<EbookCIF['toc']
|
||||
return result;
|
||||
}
|
||||
|
||||
async function parseNCXTOC(zip: JSZip, ncxPath: string): Promise<EbookCIF['toc']> {
|
||||
async function parseNCXTOC(zip: any, ncxPath: string): Promise<EbookCIF['toc']> {
|
||||
const ncxXml = await getZipFileContent(zip, ncxPath);
|
||||
const ncxDoc = parseXML(ncxXml);
|
||||
const navMap = ncxDoc.querySelector('navMap');
|
||||
@@ -198,7 +195,7 @@ function parseNCXNode(node: Element): EbookCIF['toc'] {
|
||||
return result;
|
||||
}
|
||||
|
||||
async function loadResources(zip: JSZip): Promise<Map<string, Blob>> {
|
||||
async function loadResources(zip: any): Promise<Map<string, Blob>> {
|
||||
const resources = new Map<string, Blob>();
|
||||
const files = Object.keys(zip.files);
|
||||
|
||||
@@ -213,7 +210,7 @@ async function loadResources(zip: JSZip): Promise<Map<string, Blob>> {
|
||||
return resources;
|
||||
}
|
||||
|
||||
async function extractCover(zip: JSZip, packageDoc: XMLDocument): Promise<Blob | undefined> {
|
||||
async function extractCover(zip: any, packageDoc: XMLDocument): Promise<Blob | undefined> {
|
||||
// Try cover-id metadata
|
||||
const coverId = packageDoc.querySelector('meta[name="cover"]')?.getAttribute('content');
|
||||
if (coverId) {
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
// FB2 is XML-based, similar to EPUB structure
|
||||
// Procedural style: Functions, not classes
|
||||
|
||||
import JSZip from "jszip";
|
||||
|
||||
// ============================================================
|
||||
// Main Parse Function
|
||||
// ============================================================
|
||||
@@ -16,6 +14,7 @@ export async function parseFB2(fb2Blob: Blob): Promise<EbookCIF> {
|
||||
fb2Blob.type === "application/zip" ||
|
||||
fb2Blob.type === "application/x-zip-compressed"
|
||||
) {
|
||||
const JSZip = (await import("jszip")).default;
|
||||
const zip = await JSZip.loadAsync(fb2Blob);
|
||||
const files = Object.keys(zip.files);
|
||||
|
||||
@@ -231,6 +230,7 @@ export async function getFB2Metadata(
|
||||
fb2Blob.type === "application/zip" ||
|
||||
fb2Blob.type === "application/x-zip-compressed"
|
||||
) {
|
||||
const JSZip = (await import("jszip")).default;
|
||||
const zip = await JSZip.loadAsync(fb2Blob);
|
||||
const files = Object.keys(zip.files);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user