Skip to content

Commit

Permalink
Support different base urls
Browse files Browse the repository at this point in the history
  • Loading branch information
Gitii committed Feb 11, 2024
1 parent d4b479a commit 664f930
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 11 deletions.
4 changes: 2 additions & 2 deletions src/fetch-books.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
48 changes: 39 additions & 9 deletions src/kindle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -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,
Expand All @@ -72,6 +87,7 @@ export class Kindle {
*/
readonly defaultBooks: KindleBook[];
readonly #client: HttpClient;
readonly #baseUrl: string;

constructor(
private options: KindleOptions,
Expand All @@ -82,9 +98,11 @@ export class Kindle {
) {
this.defaultBooks = prePopulatedBooks ?? [];
this.#client = client;
this.#baseUrl = options.baseUrl;
}

static async fromConfig(config: KindleConfiguration): Promise<Kindle> {
const baseUrl = new URL(config.baseUrl ?? Kindle.BASE_URL).origin;
const cookies =
typeof config.cookies === "string"
? Kindle.deserializeCookies(config.cookies)
Expand All @@ -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(
Expand All @@ -106,26 +128,29 @@ export class Kindle {
cookies,
},
sessionId,
baseUrl,
},
client,
books
);
}

static async deviceToken(
baseUrl: string,
client: HttpClient,
token: string
): Promise<KindleDeviceInfo> {
const params = new URLSearchParams({
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?: {
Expand All @@ -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,
Expand Down Expand Up @@ -178,7 +203,12 @@ export class Kindle {
query?: Query;
filter?: Filter;
}): Promise<KindleBook[]> {
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
Expand Down

0 comments on commit 664f930

Please sign in to comment.