mirror of
https://github.com/bookorbit/bookorbit.git
synced 2026-11-03 00:11:37 -06:00
fix(metadata): preserve ISBN fields across metadata workflows
Carry ISBN-10 and ISBN-13 independently from accepted providers through Auto-fill previews, saves and background refreshes. Preserve existing identifiers and field locks, validate provider checksums and avoid combining different editions. Classify embedded ISBNs correctly and support ISBN-10 fallback in search, Book Dock confidence and file naming.
This commit is contained in:
@@ -248,6 +248,7 @@ export async function setBookMetadata(
|
||||
seriesName?: string | null;
|
||||
seriesIndex?: string | null;
|
||||
language?: string | null;
|
||||
isbn10?: string | null;
|
||||
isbn13?: string | null;
|
||||
authors?: string[];
|
||||
},
|
||||
@@ -264,6 +265,7 @@ export async function setBookMetadata(
|
||||
...(metadata.seriesName !== undefined && { seriesName: metadata.seriesName }),
|
||||
...(metadata.seriesIndex !== undefined && { seriesIndex: metadata.seriesIndex }),
|
||||
...(metadata.language !== undefined && { language: metadata.language }),
|
||||
...(metadata.isbn10 !== undefined && { isbn10: metadata.isbn10 }),
|
||||
...(metadata.isbn13 !== undefined && { isbn13: metadata.isbn13 }),
|
||||
...(metadata.authors !== undefined && { authors: metadata.authors }),
|
||||
},
|
||||
|
||||
@@ -94,6 +94,8 @@ export interface BookMutationState {
|
||||
publishedYear: number | null;
|
||||
language: string | null;
|
||||
pageCount: number | null;
|
||||
isbn10: string | null;
|
||||
isbn13: string | null;
|
||||
coverSource: string | null;
|
||||
goodreadsId: string | null;
|
||||
openLibraryId: string | null;
|
||||
@@ -277,6 +279,8 @@ export async function readBookMutationState(ctx: MetadataLockE2EContext, bookId:
|
||||
publishedYear: bookMetadata.publishedYear,
|
||||
language: bookMetadata.language,
|
||||
pageCount: bookMetadata.pageCount,
|
||||
isbn10: bookMetadata.isbn10,
|
||||
isbn13: bookMetadata.isbn13,
|
||||
coverSource: bookMetadata.coverSource,
|
||||
goodreadsId: bookMetadata.goodreadsId,
|
||||
openLibraryId: bookMetadata.openLibraryId,
|
||||
@@ -311,6 +315,8 @@ export async function readBookMutationState(ctx: MetadataLockE2EContext, bookId:
|
||||
publishedYear: meta.publishedYear,
|
||||
language: meta.language,
|
||||
pageCount: meta.pageCount,
|
||||
isbn10: meta.isbn10,
|
||||
isbn13: meta.isbn13,
|
||||
coverSource: meta.coverSource,
|
||||
goodreadsId: meta.goodreadsId,
|
||||
openLibraryId: meta.openLibraryId,
|
||||
|
||||
@@ -50,6 +50,8 @@ export function buildMetadataLockCandidates(
|
||||
authors: ['Refreshed Author'],
|
||||
description: 'Refreshed description from fake Goodreads',
|
||||
publisher: 'Refreshed Publisher',
|
||||
isbn10: '0306406152',
|
||||
isbn13: '9780306406157',
|
||||
publishedYear: 2024,
|
||||
language: 'fr',
|
||||
pageCount: 321,
|
||||
|
||||
@@ -150,6 +150,31 @@ describe('Bulk file rename (e2e)', { timeout: SUITE_TIMEOUT_MS }, () => {
|
||||
}
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ isbn10: '0306406152', isbn13: null, expected: '0306406152' },
|
||||
{ isbn10: '0306406152', isbn13: '9780306406157', expected: '9780306406157' },
|
||||
])('renames using the available ISBN ($expected) without changing its field', async ({ isbn10, isbn13, expected }) => {
|
||||
const lib = await createLibrary(ctx, { mode: 'book_per_file', fileRenameEnabled: true, fileNamingPattern: '{isbn}' });
|
||||
await createEpubFile(join(lib.folderPath, 'isbn-source.epub'));
|
||||
await triggerAndWaitForScan(ctx, lib.libraryId);
|
||||
const [book] = await findAllBooksInLibrary(ctx, lib.libraryId);
|
||||
await setBookMetadata(ctx, book.bookId, { isbn10, isbn13 });
|
||||
|
||||
const preview = await getBulkRenamePreview(ctx, lib.libraryId);
|
||||
expect(preview.items).toHaveLength(1);
|
||||
expect(preview.items[0].status).toBe('will_rename');
|
||||
|
||||
const result = await executeBulkRename(ctx, lib.libraryId);
|
||||
expect(getDoneEvent(result.events)).toMatchObject({ succeeded: 1, failed: 0 });
|
||||
await expect(pathExists(join(lib.folderPath, `${expected}.epub`))).resolves.toBe(true);
|
||||
expect((await findAllBooksInLibrary(ctx, lib.libraryId))[0].relPath).toBe(`${expected}.epub`);
|
||||
const [metadata] = await ctx.db
|
||||
.select({ isbn10: schema.bookMetadata.isbn10, isbn13: schema.bookMetadata.isbn13 })
|
||||
.from(schema.bookMetadata)
|
||||
.where(eq(schema.bookMetadata.bookId, book.bookId));
|
||||
expect(metadata).toEqual({ isbn10, isbn13 });
|
||||
});
|
||||
|
||||
it('reports unchanged for books already at the correct path', async () => {
|
||||
const lib = await createLibrary(ctx, {
|
||||
mode: 'book_per_file',
|
||||
|
||||
@@ -138,6 +138,8 @@ describe('metadata-lock e2e', { timeout: 30_000 }, () => {
|
||||
publisher: expected.goodreads.publisher,
|
||||
language: expected.goodreads.language,
|
||||
pageCount: expected.goodreads.pageCount,
|
||||
isbn10: expected.goodreads.isbn10,
|
||||
isbn13: expected.goodreads.isbn13,
|
||||
goodreadsId: expected.goodreads.providerId,
|
||||
openLibraryId: expected.openLibrary.providerId,
|
||||
coverUrl: expected.goodreads.coverUrl,
|
||||
@@ -191,7 +193,7 @@ describe('metadata-lock e2e', { timeout: 30_000 }, () => {
|
||||
});
|
||||
expect(seed.statusCode).toBe(200);
|
||||
|
||||
const lock = await patchLocks(scanned.bookId, ['subtitle', 'narrators', 'comicIssueNumber', 'cover', 'openLibraryId']);
|
||||
const lock = await patchLocks(scanned.bookId, ['subtitle', 'narrators', 'comicIssueNumber', 'cover', 'openLibraryId', 'isbn13']);
|
||||
expect(lock.statusCode).toBe(200);
|
||||
|
||||
const refresh = await ctx.app.inject({
|
||||
@@ -209,6 +211,8 @@ describe('metadata-lock e2e', { timeout: 30_000 }, () => {
|
||||
publisher: expected.goodreads.publisher,
|
||||
language: expected.goodreads.language,
|
||||
pageCount: expected.goodreads.pageCount,
|
||||
isbn10: expected.goodreads.isbn10,
|
||||
isbn13: null,
|
||||
goodreadsId: expected.goodreads.providerId,
|
||||
openLibraryId: null,
|
||||
durationSeconds: expected.goodreads.durationSeconds,
|
||||
|
||||
Reference in New Issue
Block a user