From 664f930e6aa65e83f9ff86c5600ab7724284f463 Mon Sep 17 00:00:00 2001 From: Gitii Date: Sun, 11 Feb 2024 15:15:06 +0100 Subject: [PATCH] Support different base urls --- src/fetch-books.ts | 4 ++-- src/kindle.ts | 48 +++++++++++++++++++++++++++++++++++++--------- 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/src/fetch-books.ts b/src/fetch-books.ts index 185c6df..d306794 100644 --- a/src/fetch-books.ts +++ b/src/fetch-books.ts @@ -29,8 +29,8 @@ export async function fetchBooks( }; } -export function toUrl(query: Query, filter: Filter): string { - const url = new URL(Kindle.BOOKS_URL); +export function toUrl(baseUrl: string, query: Query, filter: Filter): string { + const url = new URL(`${baseUrl}/${Kindle.BOOKS_PATH}`); const searchParams = { ...query, ...filter, diff --git a/src/kindle.ts b/src/kindle.ts index eb34a01..47b2348 100644 --- a/src/kindle.ts +++ b/src/kindle.ts @@ -38,11 +38,21 @@ export type KindleConfiguration = { cookies: KindleRequiredCookies, clientOptions: TlsClientConfig ) => HttpClient; + + /** + * Base url of the kindle service. + * Amazon has different regional service endpoints, for example https://lesen.amazon.de/kindle-library (DACH region) or https://read.amazon.com/kindle-library (worldwide). + * Path and query parameters will be ignored. + * + * @default "https://read.amazon.com" + */ + baseUrl?: string; }; export type KindleOptions = { config: KindleConfiguration; sessionId: string; + baseUrl: string; }; export type KindleFromCookieOptions = { @@ -51,13 +61,18 @@ export type KindleFromCookieOptions = { }; export class Kindle { - public static DEVICE_TOKEN_URL = - "https://read.amazon.com/service/web/register/getDeviceToken"; - public static readonly BOOKS_URL = - "https://read.amazon.com/kindle-library/search?query=&libraryType=BOOKS&sortType=recency&querySize=50"; + public static readonly BASE_URL = "https://read.amazon.com"; + + public static readonly DEVICE_TOKEN_PATH = + "service/web/register/getDeviceToken"; + + public static readonly BOOKS_PATH = + "kindle-library/search?query=&libraryType=BOOKS&sortType=recency&querySize=50"; + public static readonly DEFAULT_QUERY = Object.freeze({ sortType: "acquisition_desc", } satisfies Query); + public static readonly DEFAULT_FILTER = Object.freeze({ querySize: 50, fetchAllPages: false, @@ -72,6 +87,7 @@ export class Kindle { */ readonly defaultBooks: KindleBook[]; readonly #client: HttpClient; + readonly #baseUrl: string; constructor( private options: KindleOptions, @@ -82,9 +98,11 @@ export class Kindle { ) { this.defaultBooks = prePopulatedBooks ?? []; this.#client = client; + this.#baseUrl = options.baseUrl; } static async fromConfig(config: KindleConfiguration): Promise { + const baseUrl = new URL(config.baseUrl ?? Kindle.BASE_URL).origin; const cookies = typeof config.cookies === "string" ? Kindle.deserializeCookies(config.cookies) @@ -93,10 +111,14 @@ export class Kindle { config.clientFactory?.(cookies, config.tlsServer) ?? new HttpClient(cookies, config.tlsServer); - const { sessionId, books } = await Kindle.baseRequest(client); + const { sessionId, books } = await Kindle.baseRequest(baseUrl, client); client.updateSession(sessionId); - const deviceInfo = await Kindle.deviceToken(client, config.deviceToken); + const deviceInfo = await Kindle.deviceToken( + baseUrl, + client, + config.deviceToken + ); client.updateAdpSession(deviceInfo.deviceSessionToken); return new this( @@ -106,6 +128,7 @@ export class Kindle { cookies, }, sessionId, + baseUrl, }, client, books @@ -113,6 +136,7 @@ export class Kindle { } static async deviceToken( + baseUrl: string, client: HttpClient, token: string ): Promise { @@ -120,12 +144,13 @@ export class Kindle { serialNumber: token, deviceType: token, }); - const url = `${Kindle.DEVICE_TOKEN_URL}?${params.toString()}`; + const url = `${baseUrl}/${Kindle.DEVICE_TOKEN_PATH}?${params.toString()}`; const response = await client.request(url); return JSON.parse(response.body) as KindleDeviceInfo; } static async baseRequest( + baseUrl: string, client: HttpClient, version?: string, args?: { @@ -150,7 +175,7 @@ export class Kindle { // loop until we get less than the requested amount of books or hit the limit do { - const url = toUrl(query, filter); + const url = toUrl(baseUrl, query, filter); const { books, sessionId, paginationToken } = await fetchBooks( client, url, @@ -178,7 +203,12 @@ export class Kindle { query?: Query; filter?: Filter; }): Promise { - const result = await Kindle.baseRequest(this.#client, undefined, args); + const result = await Kindle.baseRequest( + this.#baseUrl, + this.#client, + undefined, + args + ); // refreshing the internal session every time books is called. // This doesn't prevent us from calling the books endpoint but // it does prevent requesting the metadata of individual books