mirror of
https://github.com/kikootwo/ReadMeABook.git
synced 2026-06-04 05:10:11 +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.
76 lines
1.6 KiB
TypeScript
76 lines
1.6 KiB
TypeScript
/**
|
|
* Component: Series Fetching Hooks
|
|
* Documentation: documentation/frontend/components.md
|
|
*/
|
|
|
|
'use client';
|
|
|
|
import useSWR from 'swr';
|
|
import { authenticatedFetcher } from '@/lib/utils/api';
|
|
import { Audiobook } from './useAudiobooks';
|
|
|
|
export interface SeriesSummary {
|
|
asin: string;
|
|
title: string;
|
|
bookCount: number;
|
|
rating?: number;
|
|
ratingCount?: number;
|
|
tags: string[];
|
|
coverArtUrl?: string;
|
|
audibleUrl: string;
|
|
}
|
|
|
|
export interface SimilarSeries {
|
|
asin: string;
|
|
title: string;
|
|
bookCount?: number;
|
|
coverArtUrl?: string;
|
|
}
|
|
|
|
export interface SeriesDetail {
|
|
asin: string;
|
|
title: string;
|
|
bookCount: number;
|
|
rating?: number;
|
|
ratingCount?: number;
|
|
description?: string;
|
|
tags: string[];
|
|
books: Audiobook[];
|
|
similarSeries: SimilarSeries[];
|
|
audibleUrl: string;
|
|
}
|
|
|
|
export function useSeriesSearch(query: string) {
|
|
const shouldFetch = query && query.length > 0;
|
|
const endpoint = shouldFetch
|
|
? `/api/series/search?q=${encodeURIComponent(query)}`
|
|
: null;
|
|
|
|
const { data, error, isLoading } = useSWR(endpoint, authenticatedFetcher, {
|
|
revalidateOnFocus: false,
|
|
dedupingInterval: 30000,
|
|
});
|
|
|
|
return {
|
|
series: (data?.series || []) as SeriesSummary[],
|
|
query: data?.query || '',
|
|
isLoading: shouldFetch && isLoading,
|
|
error,
|
|
};
|
|
}
|
|
|
|
export function useSeriesDetail(asin: string | null) {
|
|
const endpoint = asin ? `/api/series/${asin}` : null;
|
|
|
|
const { data, error, isLoading } = useSWR(endpoint, authenticatedFetcher, {
|
|
revalidateOnFocus: false,
|
|
dedupingInterval: 300000, // Cache for 5 minutes
|
|
});
|
|
|
|
return {
|
|
series: (data?.series || null) as SeriesDetail | null,
|
|
isLoading,
|
|
error,
|
|
};
|
|
}
|