From 137e2b56073d7dcddf19f000703c9697b02c8dc4 Mon Sep 17 00:00:00 2001 From: kikootwo Date: Thu, 5 Mar 2026 17:14:26 -0500 Subject: [PATCH] Propagate and use customSearchTerms for ebooks Persist and apply customSearchTerms across ebook workflows and searches. Updated admin search-terms PATCH to enqueue addSearchEbookJob for ebook requests. Included customSearchTerms when creating ebook request records in audiobooks/[asin]/fetch-ebook, audiobooks/[asin]/select-ebook and requests/[id]/fetch-ebook. Reworked requests/[id]/select-ebook to handle being passed either an audiobook or ebook request (resolve parent audiobook, reuse existing ebook request if present) and to propagate parent.customSearchTerms when creating new ebook requests. Modified search-ebook.processor to read customSearchTerms from the request record, use it as the effective search title (with logging), and pass the modified audiobook title into Anna's Archive and indexer searches so custom terms are honored. --- .../admin/requests/[id]/search-terms/route.ts | 12 ++++-- .../audiobooks/[asin]/fetch-ebook/route.ts | 2 + .../audiobooks/[asin]/select-ebook/route.ts | 2 + .../api/requests/[id]/fetch-ebook/route.ts | 1 + .../api/requests/[id]/select-ebook/route.ts | 43 +++++++++++++------ src/lib/processors/search-ebook.processor.ts | 17 ++++++-- 6 files changed, 58 insertions(+), 19 deletions(-) diff --git a/src/app/api/admin/requests/[id]/search-terms/route.ts b/src/app/api/admin/requests/[id]/search-terms/route.ts index 65032b1..7faf222 100644 --- a/src/app/api/admin/requests/[id]/search-terms/route.ts +++ b/src/app/api/admin/requests/[id]/search-terms/route.ts @@ -100,15 +100,21 @@ export async function PATCH( }, }); - // Queue search job + // Queue search job based on request type const { getJobQueueService } = await import('@/lib/services/job-queue.service'); const jobQueue = getJobQueueService(); - await jobQueue.addSearchJob(id, { + const audiobookData = { id: existingRequest.audiobook.id, title: existingRequest.audiobook.title, author: existingRequest.audiobook.author, asin: existingRequest.audiobook.audibleAsin || undefined, - }); + }; + + if (existingRequest.type === 'ebook') { + await jobQueue.addSearchEbookJob(id, audiobookData); + } else { + await jobQueue.addSearchJob(id, audiobookData); + } searchTriggered = true; logger.info(`Search triggered for request ${id} with custom terms`, { requestId: id }); diff --git a/src/app/api/audiobooks/[asin]/fetch-ebook/route.ts b/src/app/api/audiobooks/[asin]/fetch-ebook/route.ts index 5d0ddf8..224cc0e 100644 --- a/src/app/api/audiobooks/[asin]/fetch-ebook/route.ts +++ b/src/app/api/audiobooks/[asin]/fetch-ebook/route.ts @@ -260,6 +260,7 @@ export async function POST( parentRequestId: availableRequest?.id || null, // Link to parent if exists status: 'awaiting_approval', progress: 0, + customSearchTerms: availableRequest?.customSearchTerms || null, }, }); @@ -292,6 +293,7 @@ export async function POST( parentRequestId: availableRequest?.id || null, status: 'pending', progress: 0, + customSearchTerms: availableRequest?.customSearchTerms || null, }, }); diff --git a/src/app/api/audiobooks/[asin]/select-ebook/route.ts b/src/app/api/audiobooks/[asin]/select-ebook/route.ts index 8b5bbf6..0847ffb 100644 --- a/src/app/api/audiobooks/[asin]/select-ebook/route.ts +++ b/src/app/api/audiobooks/[asin]/select-ebook/route.ts @@ -252,6 +252,7 @@ export async function POST( status: 'awaiting_approval', progress: 0, selectedTorrent: selectedEbook as any, + customSearchTerms: availableRequest?.customSearchTerms || null, }, }); logger.info(`Created ebook request ${ebookRequest.id}, awaiting approval`); @@ -296,6 +297,7 @@ export async function POST( parentRequestId: availableRequest?.id || null, status: 'searching', progress: 0, + customSearchTerms: availableRequest?.customSearchTerms || null, }, }); logger.info(`Created new ebook request ${ebookRequest.id}`); diff --git a/src/app/api/requests/[id]/fetch-ebook/route.ts b/src/app/api/requests/[id]/fetch-ebook/route.ts index d3b731b..0a056d0 100644 --- a/src/app/api/requests/[id]/fetch-ebook/route.ts +++ b/src/app/api/requests/[id]/fetch-ebook/route.ts @@ -123,6 +123,7 @@ export async function POST( parentRequestId, status: 'pending', progress: 0, + customSearchTerms: parentRequest.customSearchTerms, }, }); diff --git a/src/app/api/requests/[id]/select-ebook/route.ts b/src/app/api/requests/[id]/select-ebook/route.ts index 7c38f22..f7a47f7 100644 --- a/src/app/api/requests/[id]/select-ebook/route.ts +++ b/src/app/api/requests/[id]/select-ebook/route.ts @@ -52,17 +52,32 @@ export async function POST( return NextResponse.json({ error: 'Ebook source not specified' }, { status: 400 }); } - // Get the parent audiobook request - const parentRequest = await prisma.request.findUnique({ + // Get the request - could be an audiobook request or an existing ebook request + const foundRequest = await prisma.request.findUnique({ where: { id: parentRequestId }, include: { audiobook: true }, }); - if (!parentRequest) { + if (!foundRequest) { return NextResponse.json({ error: 'Request not found' }, { status: 404 }); } - if (parentRequest.type !== 'audiobook') { + // If this is an ebook request, find the parent audiobook request + let parentRequest; + if (foundRequest.type === 'ebook') { + if (!foundRequest.parentRequestId) { + return NextResponse.json({ error: 'Ebook request has no parent audiobook request' }, { status: 400 }); + } + parentRequest = await prisma.request.findUnique({ + where: { id: foundRequest.parentRequestId }, + include: { audiobook: true }, + }); + if (!parentRequest) { + return NextResponse.json({ error: 'Parent audiobook request not found' }, { status: 404 }); + } + } else if (foundRequest.type === 'audiobook') { + parentRequest = foundRequest; + } else { return NextResponse.json({ error: 'Can only select ebooks for audiobook requests' }, { status: 400 }); } @@ -74,13 +89,16 @@ export async function POST( } // Check for existing ebook request - let ebookRequest = await prisma.request.findFirst({ - where: { - parentRequestId, - type: 'ebook', - deletedAt: null, - }, - }); + // If we were given an ebook request ID directly, use that; otherwise search by parent + let ebookRequest = foundRequest.type === 'ebook' + ? foundRequest + : await prisma.request.findFirst({ + where: { + parentRequestId: parentRequest.id, + type: 'ebook', + deletedAt: null, + }, + }); if (ebookRequest && !['failed', 'awaiting_search', 'pending'].includes(ebookRequest.status)) { return NextResponse.json({ @@ -109,9 +127,10 @@ export async function POST( userId: parentRequest.userId, audiobookId: parentRequest.audiobookId, type: 'ebook', - parentRequestId, + parentRequestId: parentRequest.id, status: 'searching', progress: 0, + customSearchTerms: parentRequest.customSearchTerms, }, }); logger.info(`Created new ebook request ${ebookRequest.id}`); diff --git a/src/lib/processors/search-ebook.processor.ts b/src/lib/processors/search-ebook.processor.ts index cd9e3ab..f7798d0 100644 --- a/src/lib/processors/search-ebook.processor.ts +++ b/src/lib/processors/search-ebook.processor.ts @@ -36,16 +36,25 @@ export async function processSearchEbook(payload: SearchEbookPayload): Promise