mirror of
https://github.com/kikootwo/ReadMeABook.git
synced 2026-06-02 20:30:10 +00:00
Support language/format/publisher for Audible
Expose language, formatType, and publisherName from the Audible catalog. Update audible.service to map format_type and publisher_name (and language) into the AudibleAudiobook model, update AudiobookDetailsModal to display language and format using the CSS "capitalize" class, and update documentation to list the new fields. Add unit tests to verify the mappings, details propagation, and behavior when fields are omitted.
This commit is contained in:
@@ -30,7 +30,7 @@ src/components/
|
||||
**Audiobooks**
|
||||
- **AudiobookCard** ✅ - Cover, title, author, narrator, duration, request button, clickable to open details modal. Shows "Requested by [username]" when someone else has requested the book, "Requested" when current user has requested it
|
||||
- **AudiobookGrid** - Responsive grid (1/2/3/4 cols)
|
||||
- **AudiobookDetailsModal** ✅ - Full-screen modal with comprehensive metadata (description, genres, rating, release date, narrator, request functionality). Shows requesting user's name when applicable
|
||||
- **AudiobookDetailsModal** ✅ - Full-screen modal with comprehensive metadata (description, genres, rating, release date, narrator, language, format, publisher, request functionality). Shows requesting user's name when applicable
|
||||
|
||||
**Requests**
|
||||
- **RequestCard** ✅ - Cover, title, author, status badge, progress bar, timestamps, action buttons (cancel, manual search, interactive search)
|
||||
|
||||
@@ -250,6 +250,9 @@ interface AudibleAudiobook {
|
||||
series?: string;
|
||||
seriesPart?: string;
|
||||
seriesAsin?: string;
|
||||
language?: string;
|
||||
formatType?: string;
|
||||
publisherName?: string;
|
||||
}
|
||||
|
||||
interface EnrichedAudibleAudiobook extends AudibleAudiobook {
|
||||
|
||||
@@ -552,7 +552,7 @@ export function AudiobookDetailsModal({
|
||||
{audiobook.language && (
|
||||
<div>
|
||||
<p className="text-gray-500 dark:text-gray-400">Language</p>
|
||||
<p className="text-gray-900 dark:text-gray-100">{audiobook.language.charAt(0).toUpperCase() + audiobook.language.slice(1)}</p>
|
||||
<p className="text-gray-900 dark:text-gray-100 capitalize">{audiobook.language}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -560,7 +560,7 @@ export function AudiobookDetailsModal({
|
||||
{audiobook.formatType && (
|
||||
<div>
|
||||
<p className="text-gray-500 dark:text-gray-400">Format</p>
|
||||
<p className="text-gray-900 dark:text-gray-100">{audiobook.formatType.charAt(0).toUpperCase() + audiobook.formatType.slice(1)}</p>
|
||||
<p className="text-gray-900 dark:text-gray-100 capitalize">{audiobook.formatType}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -108,6 +108,8 @@ interface CatalogProduct {
|
||||
runtime_length_min?: number;
|
||||
release_date?: string;
|
||||
language?: string;
|
||||
format_type?: string;
|
||||
publisher_name?: string;
|
||||
rating?: {
|
||||
overall_distribution?: {
|
||||
display_stars?: number;
|
||||
@@ -198,6 +200,9 @@ function mapCatalogProduct(product: CatalogProduct): AudibleAudiobook {
|
||||
series,
|
||||
seriesPart,
|
||||
seriesAsin,
|
||||
language: product.language ?? undefined,
|
||||
formatType: product.format_type ?? undefined,
|
||||
publisherName: product.publisher_name ?? undefined,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,8 @@ interface ProductOverrides {
|
||||
runtime_length_min?: number;
|
||||
release_date?: string;
|
||||
language?: string;
|
||||
format_type?: string;
|
||||
publisher_name?: string;
|
||||
rating?: { overall_distribution?: { display_stars?: number } };
|
||||
category_ladders?: Array<{ ladder: Array<{ name: string }> }>;
|
||||
series?: Array<{ asin?: string; title?: string; sequence?: string }>;
|
||||
@@ -615,6 +617,47 @@ describe('AudibleService', () => {
|
||||
const genreSet = new Set(results[0].genres);
|
||||
expect(genreSet.size).toBe(5);
|
||||
});
|
||||
|
||||
it('maps language from catalog product', async () => {
|
||||
const products = [makeProduct({ language: 'english' })];
|
||||
apiClientMock.get.mockResolvedValue(apiResponse(makeProductsResponse(products)));
|
||||
|
||||
const service = new AudibleService();
|
||||
const { results } = await service.search('test', 1);
|
||||
|
||||
expect(results[0].language).toBe('english');
|
||||
});
|
||||
|
||||
it('maps format_type to formatType from catalog product', async () => {
|
||||
const products = [makeProduct({ format_type: 'unabridged' })];
|
||||
apiClientMock.get.mockResolvedValue(apiResponse(makeProductsResponse(products)));
|
||||
|
||||
const service = new AudibleService();
|
||||
const { results } = await service.search('test', 1);
|
||||
|
||||
expect(results[0].formatType).toBe('unabridged');
|
||||
});
|
||||
|
||||
it('maps publisher_name to publisherName from catalog product', async () => {
|
||||
const products = [makeProduct({ publisher_name: 'Penguin Random House Audio' })];
|
||||
apiClientMock.get.mockResolvedValue(apiResponse(makeProductsResponse(products)));
|
||||
|
||||
const service = new AudibleService();
|
||||
const { results } = await service.search('test', 1);
|
||||
|
||||
expect(results[0].publisherName).toBe('Penguin Random House Audio');
|
||||
});
|
||||
|
||||
it('leaves formatType and publisherName undefined when catalog product omits them', async () => {
|
||||
const products = [makeProduct()];
|
||||
apiClientMock.get.mockResolvedValue(apiResponse(makeProductsResponse(products)));
|
||||
|
||||
const service = new AudibleService();
|
||||
const { results } = await service.search('test', 1);
|
||||
|
||||
expect(results[0].formatType).toBeUndefined();
|
||||
expect(results[0].publisherName).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@@ -1262,6 +1305,9 @@ describe('AudibleService', () => {
|
||||
runtimeLengthMin: '300',
|
||||
genres: ['Fiction'],
|
||||
rating: '4.7',
|
||||
language: 'english',
|
||||
formatType: 'unabridged',
|
||||
publisherName: 'Test Publisher',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1271,6 +1317,9 @@ describe('AudibleService', () => {
|
||||
expect(details?.title).toBe('Audnexus Book');
|
||||
expect(details?.author).toBe('Author A');
|
||||
expect(details?.durationMinutes).toBe(300);
|
||||
expect(details?.language).toBe('english');
|
||||
expect(details?.formatType).toBe('unabridged');
|
||||
expect(details?.publisherName).toBe('Test Publisher');
|
||||
// Catalog API should NOT be called when Audnexus succeeds.
|
||||
expect(apiClientMock.get).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user