mirror of
https://github.com/kikootwo/ReadMeABook.git
synced 2026-06-03 04:40:09 +00:00
cb9f1b81bc
Introduce full support for Audible series exploration: API routes, frontend pages, components, hooks, and integrations. Key changes: - Prisma: add Audiobook.seriesAsin for linking audiobooks to series detail pages. - Backend: add /api/series/search and /api/series/[asin] routes that require auth; scrape Audible series data and enrich books with library availability. - Integrations/services: add audible-series integration and update request/HTTP services to support the workflow. - Frontend: add /series and /series/[asin] pages, new components (SeriesCard, SeriesGrid, SeriesDetailCard, SimilarSeriesRow) and wire them to a new useSeries hook; update AudiobookDetailsModal to show/link series; add Series link to Header. - Misc: extend audiobook types with series fields and add seriesLabels to language-config for scraping. These changes enable users to search for series, view series metadata and books, and navigate between audiobook and series detail pages.
58 lines
1.6 KiB
TypeScript
58 lines
1.6 KiB
TypeScript
/**
|
|
* Component: Series Search API Route
|
|
* Documentation: documentation/integrations/audible.md
|
|
*/
|
|
|
|
import { NextRequest, NextResponse } from 'next/server';
|
|
import { getCurrentUser } from '@/lib/middleware/auth';
|
|
import { RMABLogger } from '@/lib/utils/logger';
|
|
import { searchForSeries } from '@/lib/integrations/audible-series';
|
|
|
|
const logger = RMABLogger.create('API.Series.Search');
|
|
|
|
/**
|
|
* GET /api/series/search?q=game+of+thrones
|
|
* Search for audiobook series on Audible, de-duplicate, and return enriched summaries
|
|
*/
|
|
export async function GET(request: NextRequest) {
|
|
try {
|
|
// Require authentication
|
|
const currentUser = getCurrentUser(request);
|
|
if (!currentUser) {
|
|
return NextResponse.json(
|
|
{ error: 'Unauthorized', message: 'Authentication required' },
|
|
{ status: 401 }
|
|
);
|
|
}
|
|
|
|
const query = request.nextUrl.searchParams.get('q');
|
|
|
|
if (!query || query.trim().length === 0) {
|
|
return NextResponse.json(
|
|
{ error: 'ValidationError', message: 'Search query is required' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
logger.info(`Searching series: "${query}"`);
|
|
|
|
const series = await searchForSeries(query.trim());
|
|
|
|
logger.info(`Series search complete: "${query}" -> ${series.length} results`);
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
series,
|
|
query: query.trim(),
|
|
});
|
|
} catch (error) {
|
|
logger.error('Failed to search series', {
|
|
error: error instanceof Error ? error.message : String(error),
|
|
});
|
|
return NextResponse.json(
|
|
{ error: 'SearchError', message: 'Failed to search series' },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|