diff --git a/apps/readest-app/src/__tests__/document/series-metadata.test.ts b/apps/readest-app/src/__tests__/document/series-metadata.test.ts new file mode 100644 index 00000000..7d024882 --- /dev/null +++ b/apps/readest-app/src/__tests__/document/series-metadata.test.ts @@ -0,0 +1,77 @@ +import { describe, it, expect, beforeAll } from 'vitest'; +import { readFileSync } from 'fs'; +import { resolve, join } from 'path'; + +import { DocumentLoader } from '@/libs/document'; +import type { BookDoc } from '@/libs/document'; +import type { Collection } from '@/utils/book'; + +const vendorDir = join(process.cwd(), 'public/vendor'); + +const loadFixture = async ( + filename: string, + mimeType: string, + expectedFormat: string, +): Promise => { + const filePath = resolve(__dirname, '../fixtures/data', filename); + const buffer = readFileSync(filePath); + const file = new File([buffer], filename, { type: mimeType }); + const loader = new DocumentLoader(file); + const result = await loader.open(); + expect(result.format).toBe(expectedFormat); + return result.book; +}; + +const getSeries = (book: BookDoc): Collection | undefined => { + const belongsTo = book.metadata.belongsTo?.series; + if (!belongsTo) return undefined; + return Array.isArray(belongsTo) ? belongsTo[0] : belongsTo; +}; + +describe('Calibre series metadata', () => { + describe('PDF (XMP calibre:series)', () => { + let book: BookDoc; + + beforeAll(async () => { + await import('foliate-js/pdf.js'); + const pdfjsLib = (globalThis as Record)['pdfjsLib'] as { + GlobalWorkerOptions: { workerSrc: string }; + }; + pdfjsLib.GlobalWorkerOptions.workerSrc = new URL( + `file://${join(vendorDir, 'pdfjs/pdf.worker.min.mjs')}`, + ).href; + + book = await loadFixture('sample-metadata.pdf', 'application/pdf', 'PDF'); + }, 30_000); + + it('extracts series name and position from XMP', () => { + const series = getSeries(book); + expect(series).toBeTruthy(); + expect(series!.name).toBe('Metadata Series'); + expect(series!.position).toBe('1.00'); + }); + + it('preserves the title', () => { + expect(book.metadata.title).toBe('PDF Metadata'); + }); + }); + + describe('CBZ (ComicInfo.xml + ComicBookInfo)', () => { + let book: BookDoc; + + beforeAll(async () => { + book = await loadFixture('sample-metadata.cbz', 'application/vnd.comicbook+zip', 'CBZ'); + }); + + it('extracts series name and position', () => { + const series = getSeries(book); + expect(series).toBeTruthy(); + expect(series!.name).toBe('Metadata Series'); + expect(series!.position).toBe('2.0'); + }); + + it('preserves the title', () => { + expect(book.metadata.title).toBe('CBZ Metadata'); + }); + }); +}); diff --git a/apps/readest-app/src/__tests__/fixtures/data/sample-metadata.cbz b/apps/readest-app/src/__tests__/fixtures/data/sample-metadata.cbz new file mode 100644 index 00000000..c4ecdf10 Binary files /dev/null and b/apps/readest-app/src/__tests__/fixtures/data/sample-metadata.cbz differ diff --git a/apps/readest-app/src/__tests__/fixtures/data/sample-metadata.pdf b/apps/readest-app/src/__tests__/fixtures/data/sample-metadata.pdf new file mode 100644 index 00000000..74524552 Binary files /dev/null and b/apps/readest-app/src/__tests__/fixtures/data/sample-metadata.pdf differ diff --git a/packages/foliate-js b/packages/foliate-js index 0a0cedab..96dac681 160000 --- a/packages/foliate-js +++ b/packages/foliate-js @@ -1 +1 @@ -Subproject commit 0a0cedabb69fe93524b53a93f74adb0876fd6be8 +Subproject commit 96dac68155e4171136a99ab41b4e65fa83ef115a