diff --git a/documentation/phase3/prowlarr.md b/documentation/phase3/prowlarr.md index 3380b1e..725b33b 100644 --- a/documentation/phase3/prowlarr.md +++ b/documentation/phase3/prowlarr.md @@ -9,7 +9,7 @@ Indexer aggregator for searching multiple torrent/usenet indexers simultaneously **Base:** `http://prowlarr:9696/api/v1` **Auth:** `X-Api-Key` header -**GET /search?query={q}&categories=3030** - Search all indexers (3030 = audiobooks) +**GET /search?query={q}&categories=3030&indexerIds={ids}** - Search indexers (3030 = audiobooks, ids = comma-separated indexer IDs) **GET /indexer** - List configured indexers **GET /indexerstats** - Indexer statistics **GET /feed/{indexerId}/api?t=search&cat=3030&limit=100** - RSS feed for specific indexer @@ -49,13 +49,17 @@ interface TorrentResult { ## Manual & Interactive Search +**Indexer Filtering:** All searches (manual and interactive) only query indexers enabled in settings (`prowlarr_indexers` config). If no indexers are enabled, search will fail with error. + **Manual Search** (`POST /api/requests/{id}/manual-search`) - Triggers automatic search job for requests with status: pending, failed, awaiting_search +- Searches only enabled indexers - Uses ranking algorithm to select best torrent - Updates request status to 'pending' **Interactive Search** (`POST /api/requests/{id}/interactive-search`) - Returns ranked torrent results for user selection +- Searches only enabled indexers - Shows table with: rank, title, size, quality score, seeders, indexer, publish date - Available for same statuses as manual search - User clicks "Download" button to select specific torrent diff --git a/src/app/api/requests/[id]/interactive-search/route.ts b/src/app/api/requests/[id]/interactive-search/route.ts index 29eb033..96cf96c 100644 --- a/src/app/api/requests/[id]/interactive-search/route.ts +++ b/src/app/api/requests/[id]/interactive-search/route.ts @@ -50,13 +50,37 @@ export async function POST( ); } - // Search Prowlarr for torrents + // Get enabled indexers from configuration + const { getConfigService } = await import('@/lib/services/config.service'); + const configService = getConfigService(); + const indexersConfigStr = await configService.get('prowlarr_indexers'); + + if (!indexersConfigStr) { + return NextResponse.json( + { error: 'ConfigError', message: 'No indexers configured. Please configure indexers in settings.' }, + { status: 400 } + ); + } + + const indexersConfig = JSON.parse(indexersConfigStr); + const enabledIndexerIds = indexersConfig.map((indexer: any) => indexer.id); + + if (enabledIndexerIds.length === 0) { + return NextResponse.json( + { error: 'ConfigError', message: 'No indexers enabled. Please enable at least one indexer in settings.' }, + { status: 400 } + ); + } + + // Search Prowlarr for torrents - ONLY enabled indexers const prowlarr = await getProwlarrService(); const searchQuery = `${requestRecord.audiobook.title} ${requestRecord.audiobook.author}`; - console.log(`[InteractiveSearch] Searching for: ${searchQuery}`); + console.log(`[InteractiveSearch] Searching ${enabledIndexerIds.length} enabled indexers for: ${searchQuery}`); - const results = await prowlarr.search(searchQuery); + const results = await prowlarr.search(searchQuery, { + indexerIds: enabledIndexerIds, + }); if (results.length === 0) { return NextResponse.json({ diff --git a/src/lib/integrations/prowlarr.service.ts b/src/lib/integrations/prowlarr.service.ts index c61e248..0a0a836 100644 --- a/src/lib/integrations/prowlarr.service.ts +++ b/src/lib/integrations/prowlarr.service.ts @@ -11,6 +11,7 @@ export interface SearchFilters { category?: number; minSeeders?: number; maxResults?: number; + indexerIds?: number[]; } export interface Indexer { @@ -72,7 +73,8 @@ export class ProwlarrService { } /** - * Search for audiobooks across all configured indexers + * Search for audiobooks across configured indexers + * If indexerIds is provided, only searches those indexers */ async search( query: string, @@ -86,6 +88,11 @@ export class ProwlarrService { extended: 1, // Enable searching in tags, labels, and metadata }; + // Filter by specific indexers if provided + if (filters?.indexerIds && filters.indexerIds.length > 0) { + params.indexerIds = filters.indexerIds.join(','); + } + const response = await this.client.get('/search', { params }); // Transform Prowlarr results to our format diff --git a/src/lib/processors/search-indexers.processor.ts b/src/lib/processors/search-indexers.processor.ts index 5fc2257..8a587c5 100644 --- a/src/lib/processors/search-indexers.processor.ts +++ b/src/lib/processors/search-indexers.processor.ts @@ -31,6 +31,24 @@ export async function processSearchIndexers(payload: SearchIndexersPayload): Pro }, }); + // Get enabled indexers from configuration + const { getConfigService } = await import('../services/config.service'); + const configService = getConfigService(); + const indexersConfigStr = await configService.get('prowlarr_indexers'); + + if (!indexersConfigStr) { + throw new Error('No indexers configured. Please configure indexers in settings.'); + } + + const indexersConfig = JSON.parse(indexersConfigStr); + const enabledIndexerIds = indexersConfig.map((indexer: any) => indexer.id); + + if (enabledIndexerIds.length === 0) { + throw new Error('No indexers enabled. Please enable at least one indexer in settings.'); + } + + await logger?.info(`Searching ${enabledIndexerIds.length} enabled indexers`); + // Get Prowlarr service const prowlarr = await getProwlarrService(); @@ -39,11 +57,12 @@ export async function processSearchIndexers(payload: SearchIndexersPayload): Pro await logger?.info(`Searching for: "${searchQuery}"`); - // Search indexers + // Search indexers - ONLY enabled ones const searchResults = await prowlarr.search(searchQuery, { category: 3030, // Audiobooks minSeeders: 1, // Only torrents with at least 1 seeder maxResults: 50, // Limit results + indexerIds: enabledIndexerIds, // Filter by enabled indexers }); await logger?.info(`Found ${searchResults.length} results`);