Skip to content

Commit 664f930

Browse files
committed
Support different base urls
1 parent d4b479a commit 664f930

File tree

2 files changed

+41
-11
lines changed

2 files changed

+41
-11
lines changed

src/fetch-books.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ export async function fetchBooks(
2929
};
3030
}
3131

32-
export function toUrl(query: Query, filter: Filter): string {
33-
const url = new URL(Kindle.BOOKS_URL);
32+
export function toUrl(baseUrl: string, query: Query, filter: Filter): string {
33+
const url = new URL(`${baseUrl}/${Kindle.BOOKS_PATH}`);
3434
const searchParams = {
3535
...query,
3636
...filter,

src/kindle.ts

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,21 @@ export type KindleConfiguration = {
3838
cookies: KindleRequiredCookies,
3939
clientOptions: TlsClientConfig
4040
) => HttpClient;
41+
42+
/**
43+
* Base url of the kindle service.
44+
* Amazon has different regional service endpoints, for example https://lesen.amazon.de/kindle-library (DACH region) or https://read.amazon.com/kindle-library (worldwide).
45+
* Path and query parameters will be ignored.
46+
*
47+
* @default "https://read.amazon.com"
48+
*/
49+
baseUrl?: string;
4150
};
4251

4352
export type KindleOptions = {
4453
config: KindleConfiguration;
4554
sessionId: string;
55+
baseUrl: string;
4656
};
4757

4858
export type KindleFromCookieOptions = {
@@ -51,13 +61,18 @@ export type KindleFromCookieOptions = {
5161
};
5262

5363
export class Kindle {
54-
public static DEVICE_TOKEN_URL =
55-
"https://read.amazon.com/service/web/register/getDeviceToken";
56-
public static readonly BOOKS_URL =
57-
"https://read.amazon.com/kindle-library/search?query=&libraryType=BOOKS&sortType=recency&querySize=50";
64+
public static readonly BASE_URL = "https://read.amazon.com";
65+
66+
public static readonly DEVICE_TOKEN_PATH =
67+
"service/web/register/getDeviceToken";
68+
69+
public static readonly BOOKS_PATH =
70+
"kindle-library/search?query=&libraryType=BOOKS&sortType=recency&querySize=50";
71+
5872
public static readonly DEFAULT_QUERY = Object.freeze({
5973
sortType: "acquisition_desc",
6074
} satisfies Query);
75+
6176
public static readonly DEFAULT_FILTER = Object.freeze({
6277
querySize: 50,
6378
fetchAllPages: false,
@@ -72,6 +87,7 @@ export class Kindle {
7287
*/
7388
readonly defaultBooks: KindleBook[];
7489
readonly #client: HttpClient;
90+
readonly #baseUrl: string;
7591

7692
constructor(
7793
private options: KindleOptions,
@@ -82,9 +98,11 @@ export class Kindle {
8298
) {
8399
this.defaultBooks = prePopulatedBooks ?? [];
84100
this.#client = client;
101+
this.#baseUrl = options.baseUrl;
85102
}
86103

87104
static async fromConfig(config: KindleConfiguration): Promise<Kindle> {
105+
const baseUrl = new URL(config.baseUrl ?? Kindle.BASE_URL).origin;
88106
const cookies =
89107
typeof config.cookies === "string"
90108
? Kindle.deserializeCookies(config.cookies)
@@ -93,10 +111,14 @@ export class Kindle {
93111
config.clientFactory?.(cookies, config.tlsServer) ??
94112
new HttpClient(cookies, config.tlsServer);
95113

96-
const { sessionId, books } = await Kindle.baseRequest(client);
114+
const { sessionId, books } = await Kindle.baseRequest(baseUrl, client);
97115
client.updateSession(sessionId);
98116

99-
const deviceInfo = await Kindle.deviceToken(client, config.deviceToken);
117+
const deviceInfo = await Kindle.deviceToken(
118+
baseUrl,
119+
client,
120+
config.deviceToken
121+
);
100122
client.updateAdpSession(deviceInfo.deviceSessionToken);
101123

102124
return new this(
@@ -106,26 +128,29 @@ export class Kindle {
106128
cookies,
107129
},
108130
sessionId,
131+
baseUrl,
109132
},
110133
client,
111134
books
112135
);
113136
}
114137

115138
static async deviceToken(
139+
baseUrl: string,
116140
client: HttpClient,
117141
token: string
118142
): Promise<KindleDeviceInfo> {
119143
const params = new URLSearchParams({
120144
serialNumber: token,
121145
deviceType: token,
122146
});
123-
const url = `${Kindle.DEVICE_TOKEN_URL}?${params.toString()}`;
147+
const url = `${baseUrl}/${Kindle.DEVICE_TOKEN_PATH}?${params.toString()}`;
124148
const response = await client.request(url);
125149
return JSON.parse(response.body) as KindleDeviceInfo;
126150
}
127151

128152
static async baseRequest(
153+
baseUrl: string,
129154
client: HttpClient,
130155
version?: string,
131156
args?: {
@@ -150,7 +175,7 @@ export class Kindle {
150175

151176
// loop until we get less than the requested amount of books or hit the limit
152177
do {
153-
const url = toUrl(query, filter);
178+
const url = toUrl(baseUrl, query, filter);
154179
const { books, sessionId, paginationToken } = await fetchBooks(
155180
client,
156181
url,
@@ -178,7 +203,12 @@ export class Kindle {
178203
query?: Query;
179204
filter?: Filter;
180205
}): Promise<KindleBook[]> {
181-
const result = await Kindle.baseRequest(this.#client, undefined, args);
206+
const result = await Kindle.baseRequest(
207+
this.#baseUrl,
208+
this.#client,
209+
undefined,
210+
args
211+
);
182212
// refreshing the internal session every time books is called.
183213
// This doesn't prevent us from calling the books endpoint but
184214
// it does prevent requesting the metadata of individual books

0 commit comments

Comments
 (0)