nhentai-client 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1 @@
1
+ # nhentai-client
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "nhentai-client",
3
+ "version": "0.0.1",
4
+ "description": "A TypeScript client for the nhentai API",
5
+ "author": "Krettsy",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "main": "./src/index.ts",
9
+ "module": "./src/index.ts",
10
+ "types": "./src/index.ts",
11
+ "exports": {
12
+ ".": {
13
+ "import": "./src/index.ts",
14
+ "types": "./src/index.ts"
15
+ }
16
+ },
17
+ "files": [
18
+ "src"
19
+ ],
20
+ "keywords": [
21
+ "nhentai",
22
+ "api",
23
+ "client"
24
+ ],
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "https://github.com/Walter-Sparrow/nhentai-client"
28
+ },
29
+ "devDependencies": {
30
+ "@types/bun": "latest"
31
+ },
32
+ "peerDependencies": {
33
+ "typescript": "^5"
34
+ }
35
+ }
package/src/client.ts ADDED
@@ -0,0 +1,47 @@
1
+ import { findWorkingUrl } from "./utils/findWorkingUrl";
2
+ import {
3
+ API_URL,
4
+ MAP_IMAGE_TYPE_TO_EXTENSION,
5
+ T_IMAGE_URL,
6
+ type Book,
7
+ type Page,
8
+ type Sort,
9
+ } from "./client.types";
10
+
11
+ export const getCoverImageUrl = async (book: Book) => {
12
+ const baseUrl = `${T_IMAGE_URL}/galleries/${book.media_id}/cover`;
13
+ const ext = MAP_IMAGE_TYPE_TO_EXTENSION[book.images.cover.t];
14
+
15
+ if (ext !== "webp") {
16
+ return `${baseUrl}.${ext}`;
17
+ }
18
+
19
+ const candidates = Object.values(MAP_IMAGE_TYPE_TO_EXTENSION).map(
20
+ (ext) => `${baseUrl}.${ext}.webp`
21
+ );
22
+ return await findWorkingUrl(candidates);
23
+ };
24
+
25
+ export const getBookUrl = (id: number) => {
26
+ return `${API_URL}/gallery/${id}`;
27
+ };
28
+
29
+ export const getSearchUrl = (query: string, page: number, sort: Sort) => {
30
+ return `${API_URL}/galleries/search?query=${query}&page=${page}&sort=${sort}`;
31
+ };
32
+
33
+ export const getBook = async (id: number) => {
34
+ const response = await fetch(getBookUrl(id));
35
+ const data = await response.json();
36
+ return data as Book;
37
+ };
38
+
39
+ export const getSearch = async (
40
+ query: string,
41
+ page: number,
42
+ sort: Sort = "recent"
43
+ ) => {
44
+ const response = await fetch(getSearchUrl(query, page, sort));
45
+ const data = await response.json();
46
+ return data as Page<Book>;
47
+ };
@@ -0,0 +1,68 @@
1
+ export type ImageType = "j" | "p" | "g" | "w";
2
+
3
+ export type Image = {
4
+ t: ImageType;
5
+ w: number;
6
+ h: number;
7
+ };
8
+
9
+ export type Tag = {
10
+ id: number;
11
+ type:
12
+ | "artist"
13
+ | "circle"
14
+ | "language"
15
+ | "category"
16
+ | "parody"
17
+ | "character"
18
+ | "group"
19
+ | "tag";
20
+ name: string;
21
+ url: string;
22
+ count: number;
23
+ };
24
+
25
+ export type Book = {
26
+ id: number;
27
+ media_id: string;
28
+ title: {
29
+ english: string;
30
+ japanese: string;
31
+ pretty: string;
32
+ };
33
+ images: {
34
+ pages: Image[];
35
+ cover: Image;
36
+ thumbnail: Image;
37
+ };
38
+ scanlator: string;
39
+ upload_date: number;
40
+ tags: Tag[];
41
+ num_pages: number;
42
+ num_favorites: number;
43
+ };
44
+
45
+ export type Sort =
46
+ | "recent"
47
+ | "popular"
48
+ | "popular-today"
49
+ | "popular-week"
50
+ | "popular-month";
51
+
52
+ export type Page<T> = {
53
+ result: T[];
54
+ num_pages: number;
55
+ per_page: number;
56
+ };
57
+
58
+ export const API_URL = "https://nhentai.net/api";
59
+
60
+ export const I_IMAGE_URL = "https://i1.nhentai.net";
61
+ export const T_IMAGE_URL = "https://t1.nhentai.net";
62
+
63
+ export const MAP_IMAGE_TYPE_TO_EXTENSION = {
64
+ j: "jpg",
65
+ p: "png",
66
+ g: "gif",
67
+ w: "webp",
68
+ } as const;
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from "./client";
2
+ export * from "./client.types";
@@ -0,0 +1,11 @@
1
+ export const findWorkingUrl = async (urls: string[]): Promise<string> => {
2
+ const results = await Promise.allSettled(
3
+ urls.map(async (url) => {
4
+ const res = await fetch(url, { method: "HEAD" });
5
+ if (!res.ok) throw new Error("not found");
6
+ return url;
7
+ })
8
+ );
9
+ const found = results.find((r) => r.status === "fulfilled");
10
+ return found?.value ?? urls[0]!;
11
+ };