mirror of
https://github.com/bookorbit/bookorbit.git
synced 2026-11-03 00:11:37 -06:00
fix(book-dock): use file path as folderPath when finalizing into a book_per_file library
In book_per_file mode the scanner identifies books by the file's absolute path (not its parent directory). The finalize service was always using dirname(destPath) regardless of organization mode, so the scanner would see the freshly-moved file as unknown, create a second book record, and then steal the book_file from the first record via the onConflictDoUpdate upsert - leaving the original book as a ghost with a stale primary_file_id. Fix: check library.organizationMode after fetching the library and pass destPath directly as bookFolderPath when the mode is book_per_file. Add a dedicated test case that asserts createBookRecord receives the file path (not its parent dir) in book_per_file mode.
This commit is contained in:
@@ -440,6 +440,30 @@ describe('BookDockFinalizeService', () => {
|
||||
success: true,
|
||||
bookId: 555,
|
||||
});
|
||||
expect(processor.createBookRecord).toHaveBeenCalledWith(5, 9, '/library/new', '/library/new/book.epub', 'new/book.epub', 'epub', 100);
|
||||
});
|
||||
|
||||
it('uses the file path as bookFolderPath in book_per_file mode', async () => {
|
||||
const { service, processor } = makeService();
|
||||
vi.spyOn(service as never, 'findLibraryOrFail').mockResolvedValue({
|
||||
id: 5,
|
||||
allowedFormats: ['epub'],
|
||||
fileNamingPattern: null,
|
||||
organizationMode: 'book_per_file',
|
||||
} as never);
|
||||
vi.spyOn(service as never, 'findFolderOrFail').mockResolvedValue({ id: 9, libraryId: 5, path: '/library' } as never);
|
||||
vi.spyOn(service as never, 'resolveDestination').mockResolvedValue('/library/new/book.epub' as never);
|
||||
vi.spyOn(service as never, 'findDuplicate').mockResolvedValue(null as never);
|
||||
vi.spyOn(service as never, 'applyMetadata').mockResolvedValue(undefined as never);
|
||||
vi.spyOn(service as never, 'cleanupBookDockRecord').mockResolvedValue(undefined as never);
|
||||
mockAccess.mockRejectedValueOnce(new Error('missing'));
|
||||
mockStat.mockResolvedValueOnce({ size: 100 } as never);
|
||||
processor.createBookRecord.mockResolvedValueOnce({ bookId: 556 });
|
||||
|
||||
await expect(
|
||||
(service as any).finalizeFile(makeRow({ targetLibraryId: 5, targetFolderId: 9 }), undefined, undefined, new Map(), 1, true),
|
||||
).resolves.toMatchObject({ success: true, bookId: 556 });
|
||||
expect(processor.createBookRecord).toHaveBeenCalledWith(5, 9, '/library/new/book.epub', '/library/new/book.epub', 'new/book.epub', 'epub', 100);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -209,7 +209,7 @@ export class BookDockFinalizeService implements OnModuleInit {
|
||||
let bookId: number;
|
||||
try {
|
||||
const { size } = await stat(destPath);
|
||||
const bookFolderPath = dirname(destPath);
|
||||
const bookFolderPath = library.organizationMode === 'book_per_file' ? destPath : dirname(destPath);
|
||||
({ bookId } = await this.processor.createBookRecord(
|
||||
libraryId,
|
||||
folder.id,
|
||||
|
||||
Reference in New Issue
Block a user