mirror of
https://github.com/kikootwo/ReadMeABook.git
synced 2026-06-03 12:50:09 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4c1d1c89e8 | |||
| d25d93680e | |||
| d2be3f558f | |||
| 312421a96b | |||
| 1140ffc8eb |
@@ -10,7 +10,7 @@
|
||||
[](https://github.com/sponsors/kikootwo)
|
||||
[](https://github.com/kikootwo/readmeabook/actions/workflows/build-unified-image.yml)
|
||||
[](https://github.com/kikootwo/readmeabook/actions/workflows/run-tests.yml)
|
||||
[](https://github.com/kikootwo/readmeabook/pkgs/container/readmeabook)
|
||||
[](https://github.com/kikootwo/readmeabook/pkgs/container/readmeabook)
|
||||
[](https://www.gnu.org/licenses/agpl-3.0)
|
||||
[](https://github.com/kikootwo/readmeabook/stargazers)
|
||||
[](https://discord.gg/kaw6jKbKts)
|
||||
|
||||
@@ -26,11 +26,18 @@ Audiobook metadata from Audnexus API (primary) and Audible.com scraping (fallbac
|
||||
Configurable Audible region for accurate metadata matching across different international Audible stores.
|
||||
|
||||
**Supported Regions:**
|
||||
- United States (`us`) - `audible.com` (default)
|
||||
- Canada (`ca`) - `audible.ca`
|
||||
- United Kingdom (`uk`) - `audible.co.uk`
|
||||
- Australia (`au`) - `audible.com.au`
|
||||
- India (`in`) - `audible.in`
|
||||
- United States (`us`) - `audible.com` (default, English)
|
||||
- Canada (`ca`) - `audible.ca` (English)
|
||||
- United Kingdom (`uk`) - `audible.co.uk` (English)
|
||||
- Australia (`au`) - `audible.com.au` (English)
|
||||
- India (`in`) - `audible.in` (English)
|
||||
- Germany (`de`) - `audible.de` (non-English)
|
||||
|
||||
**`isEnglish` Flag:**
|
||||
- Each region has `isEnglish: boolean` in `AudibleRegionConfig`
|
||||
- Non-English regions (`isEnglish: false`) display an amber warning in all region dropdowns (setup wizard + admin settings)
|
||||
- Warning text: "Many features such as search, discovery, and metadata matching are not yet fully supported for non-English regions."
|
||||
- Dropdown options for non-English regions show `*` suffix (e.g., "Germany *")
|
||||
|
||||
**Why Regions Matter:**
|
||||
- Each Audible region uses different ASINs for the same audiobook
|
||||
@@ -47,7 +54,8 @@ Configurable Audible region for accurate metadata matching across different inte
|
||||
- `AudibleService` loads region from config on initialization
|
||||
- Dynamically builds base URL: `AUDIBLE_REGIONS[region].baseUrl`
|
||||
- Audnexus API calls include region parameter: `?region={code}`
|
||||
- IP redirect prevention: `?ipRedirectOverride=true` on all Audible requests
|
||||
- IP redirect prevention: `?ipRedirectOverride=true` on all Audible requests (region only)
|
||||
- **Locale enforcement:** `?language=english` query parameter on all Audible requests (forces English content regardless of server IP geolocation)
|
||||
- Configuration service helper: `getAudibleRegion()` returns configured region
|
||||
- **Auto-detection of region changes**: Service checks config before each request and re-initializes if region changed
|
||||
- **Cache clearing**: When region changes, ConfigService cache and AudibleService initialization are cleared
|
||||
@@ -225,3 +233,10 @@ interface EnrichedAudibleAudiobook extends AudibleAudiobook {
|
||||
- **Fix:** Added `mapRegionToABSProvider()` to convert RMAB region codes to AudiobookShelf provider values. US → `'audible'`, others → `'audible.{region}'` (e.g., `'audible.ca'`, `'audible.uk'`)
|
||||
- **Location:** `src/lib/services/audiobookshelf/api.ts:14, 147`
|
||||
- **Affects:** All Audiobookshelf metadata matching operations
|
||||
|
||||
**Non-English locale pages served to users outside US (2026-02-05)**
|
||||
- **Problem:** Audible uses IP geolocation to serve locale-specific pages (e.g., Spanish content for Dominican Republic IPs). `ipRedirectOverride=true` only prevents region redirects (audible.com → audible.co.uk), NOT language/locale changes.
|
||||
- **Impact:** Users self-hosting from non-English-speaking countries got non-English bestsellers/new releases on their homepage.
|
||||
- **Fix:** Added `language=english` query parameter to all Audible requests via axios default params. Audible respects this parameter and serves English content regardless of IP geolocation. Fails gracefully for regions where English isn't available.
|
||||
- **Location:** `src/lib/integrations/audible.service.ts` — `initialize()` (axios default params)
|
||||
- **Affects:** All Audible scraping: popular, new releases, search, detail pages
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "readmeabook",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.2",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import { Input } from '@/components/ui/Input';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { Settings, ABSLibrary } from '../../lib/types';
|
||||
import { AUDIBLE_REGIONS } from '@/lib/types/audible';
|
||||
|
||||
interface AudiobookshelfSectionProps {
|
||||
settings: Settings;
|
||||
@@ -161,12 +162,39 @@ export function AudiobookshelfSection({
|
||||
onChange={(e) => handleAudibleRegionChange(e.target.value)}
|
||||
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<option value="us">United States</option>
|
||||
<option value="ca">Canada</option>
|
||||
<option value="uk">United Kingdom</option>
|
||||
<option value="au">Australia</option>
|
||||
<option value="in">India</option>
|
||||
{Object.values(AUDIBLE_REGIONS).map((region) => (
|
||||
<option key={region.code} value={region.code}>
|
||||
{region.name}{!region.isEnglish ? ' *' : ''}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
{AUDIBLE_REGIONS[settings.audibleRegion as keyof typeof AUDIBLE_REGIONS]?.isEnglish === false && (
|
||||
<div className="bg-amber-50 dark:bg-amber-900/20 rounded-lg p-4 border border-amber-200 dark:border-amber-800 mt-2">
|
||||
<div className="flex gap-3">
|
||||
<svg
|
||||
className="w-5 h-5 text-amber-600 dark:text-amber-400 flex-shrink-0 mt-0.5"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-amber-900 dark:text-amber-100">
|
||||
Non-English Region
|
||||
</p>
|
||||
<p className="text-sm text-amber-700 dark:text-amber-300 mt-1">
|
||||
Many features such as search, discovery, and metadata matching are not yet fully
|
||||
supported for non-English regions. You may still proceed, but expect limited
|
||||
functionality.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Select the Audible region that matches your metadata engine (Audnexus/Audible Agent)
|
||||
configuration in Audiobookshelf. This ensures accurate book matching and metadata.
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import { Input } from '@/components/ui/Input';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { Settings, PlexLibrary } from '../../lib/types';
|
||||
import { AUDIBLE_REGIONS } from '@/lib/types/audible';
|
||||
|
||||
interface PlexSectionProps {
|
||||
settings: Settings;
|
||||
@@ -161,12 +162,39 @@ export function PlexSection({
|
||||
onChange={(e) => handleAudibleRegionChange(e.target.value)}
|
||||
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<option value="us">United States</option>
|
||||
<option value="ca">Canada</option>
|
||||
<option value="uk">United Kingdom</option>
|
||||
<option value="au">Australia</option>
|
||||
<option value="in">India</option>
|
||||
{Object.values(AUDIBLE_REGIONS).map((region) => (
|
||||
<option key={region.code} value={region.code}>
|
||||
{region.name}{!region.isEnglish ? ' *' : ''}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
{AUDIBLE_REGIONS[settings.audibleRegion as keyof typeof AUDIBLE_REGIONS]?.isEnglish === false && (
|
||||
<div className="bg-amber-50 dark:bg-amber-900/20 rounded-lg p-4 border border-amber-200 dark:border-amber-800 mt-2">
|
||||
<div className="flex gap-3">
|
||||
<svg
|
||||
className="w-5 h-5 text-amber-600 dark:text-amber-400 flex-shrink-0 mt-0.5"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-amber-900 dark:text-amber-100">
|
||||
Non-English Region
|
||||
</p>
|
||||
<p className="text-sm text-amber-700 dark:text-amber-300 mt-1">
|
||||
Many features such as search, discovery, and metadata matching are not yet fully
|
||||
supported for non-English regions. You may still proceed, but expect limited
|
||||
functionality.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Select the Audible region that matches your metadata engine (Audnexus/Audible Agent)
|
||||
configuration in Plex. This ensures accurate book matching and metadata.
|
||||
|
||||
@@ -8,11 +8,12 @@ import { requireAuth, requireAdmin, AuthenticatedRequest } from '@/lib/middlewar
|
||||
import { getConfigService } from '@/lib/services/config.service';
|
||||
import { getAudibleService } from '@/lib/integrations/audible.service';
|
||||
import { getJobQueueService } from '@/lib/services/job-queue.service';
|
||||
import { AUDIBLE_REGIONS } from '@/lib/types/audible';
|
||||
import { RMABLogger } from '@/lib/utils/logger';
|
||||
|
||||
const logger = RMABLogger.create('API.Admin.Settings.Audible');
|
||||
|
||||
const VALID_REGIONS = ['us', 'ca', 'uk', 'au', 'in'];
|
||||
const VALID_REGIONS = Object.keys(AUDIBLE_REGIONS);
|
||||
|
||||
export async function PUT(request: NextRequest) {
|
||||
return requireAuth(request, async (req: AuthenticatedRequest) => {
|
||||
@@ -24,7 +25,7 @@ export async function PUT(request: NextRequest) {
|
||||
if (!region || !VALID_REGIONS.includes(region)) {
|
||||
logger.warn('Invalid region provided', { region });
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Invalid Audible region. Must be one of: us, ca, uk, au, in' },
|
||||
{ success: false, error: `Invalid Audible region. Must be one of: ${VALID_REGIONS.join(', ')}` },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
'use client';
|
||||
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { AudibleRegion } from '@/lib/types/audible';
|
||||
import { AudibleRegion, AUDIBLE_REGIONS } from '@/lib/types/audible';
|
||||
|
||||
interface BackendSelectionStepProps {
|
||||
value: 'plex' | 'audiobookshelf';
|
||||
@@ -113,12 +113,39 @@ export function BackendSelectionStep({
|
||||
onChange={(e) => onAudibleRegionChange(e.target.value as AudibleRegion)}
|
||||
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<option value="us">United States</option>
|
||||
<option value="ca">Canada</option>
|
||||
<option value="uk">United Kingdom</option>
|
||||
<option value="au">Australia</option>
|
||||
<option value="in">India</option>
|
||||
{Object.values(AUDIBLE_REGIONS).map((region) => (
|
||||
<option key={region.code} value={region.code}>
|
||||
{region.name}{!region.isEnglish ? ' *' : ''}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
{AUDIBLE_REGIONS[audibleRegion]?.isEnglish === false && (
|
||||
<div className="bg-amber-50 dark:bg-amber-900/20 rounded-lg p-4 border border-amber-200 dark:border-amber-800 mt-2">
|
||||
<div className="flex gap-3">
|
||||
<svg
|
||||
className="w-5 h-5 text-amber-600 dark:text-amber-400 flex-shrink-0 mt-0.5"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-amber-900 dark:text-amber-100">
|
||||
Non-English Region
|
||||
</p>
|
||||
<p className="text-sm text-amber-700 dark:text-amber-300 mt-1">
|
||||
Many features such as search, discovery, and metadata matching are not yet fully
|
||||
supported for non-English regions. You may still proceed, but expect limited
|
||||
functionality.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
||||
Select the Audible region that matches your metadata engine (Audnexus/Audible Agent)
|
||||
configuration in {value === 'plex' ? 'Plex' : 'Audiobookshelf'}. This ensures accurate book matching and metadata.
|
||||
|
||||
@@ -91,6 +91,7 @@ export class AudibleService {
|
||||
},
|
||||
params: {
|
||||
ipRedirectOverride: 'true', // Prevent IP-based region redirects
|
||||
language: 'english', // Force English locale (prevents IP-based language serving for non-English IPs)
|
||||
},
|
||||
});
|
||||
|
||||
@@ -110,6 +111,7 @@ export class AudibleService {
|
||||
},
|
||||
params: {
|
||||
ipRedirectOverride: 'true',
|
||||
language: 'english',
|
||||
},
|
||||
});
|
||||
this.initialized = true;
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
* Documentation: documentation/integrations/audible.md
|
||||
*/
|
||||
|
||||
export type AudibleRegion = 'us' | 'ca' | 'uk' | 'au' | 'in';
|
||||
export type AudibleRegion = 'us' | 'ca' | 'uk' | 'au' | 'in' | 'de';
|
||||
|
||||
export interface AudibleRegionConfig {
|
||||
code: AudibleRegion;
|
||||
name: string;
|
||||
baseUrl: string;
|
||||
audnexusParam: string;
|
||||
isEnglish: boolean;
|
||||
}
|
||||
|
||||
export const AUDIBLE_REGIONS: Record<AudibleRegion, AudibleRegionConfig> = {
|
||||
@@ -18,30 +19,42 @@ export const AUDIBLE_REGIONS: Record<AudibleRegion, AudibleRegionConfig> = {
|
||||
name: 'United States',
|
||||
baseUrl: 'https://www.audible.com',
|
||||
audnexusParam: 'us',
|
||||
isEnglish: true,
|
||||
},
|
||||
ca: {
|
||||
code: 'ca',
|
||||
name: 'Canada',
|
||||
baseUrl: 'https://www.audible.ca',
|
||||
audnexusParam: 'ca',
|
||||
isEnglish: true,
|
||||
},
|
||||
uk: {
|
||||
code: 'uk',
|
||||
name: 'United Kingdom',
|
||||
baseUrl: 'https://www.audible.co.uk',
|
||||
audnexusParam: 'uk',
|
||||
isEnglish: true,
|
||||
},
|
||||
au: {
|
||||
code: 'au',
|
||||
name: 'Australia',
|
||||
baseUrl: 'https://www.audible.com.au',
|
||||
audnexusParam: 'au',
|
||||
isEnglish: true,
|
||||
},
|
||||
in: {
|
||||
code: 'in',
|
||||
name: 'India',
|
||||
baseUrl: 'https://www.audible.in',
|
||||
audnexusParam: 'in',
|
||||
isEnglish: true,
|
||||
},
|
||||
de: {
|
||||
code: 'de',
|
||||
name: 'Germany',
|
||||
baseUrl: 'https://www.audible.de',
|
||||
audnexusParam: 'de',
|
||||
isEnglish: false,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user