kaizoku-core 0.1.0

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.
Files changed (54) hide show
  1. package/LICENSE +0 -0
  2. package/README.md +105 -0
  3. package/dist/extractors/kwik.d.ts +6 -0
  4. package/dist/extractors/kwik.js +33 -0
  5. package/dist/extractors/megaplay.d.ts +2 -0
  6. package/dist/extractors/megaplay.js +32 -0
  7. package/dist/extractors/streamwish.d.ts +4 -0
  8. package/dist/extractors/streamwish.js +86 -0
  9. package/dist/extractors/vidtube.d.ts +2 -0
  10. package/dist/extractors/vidtube.js +24 -0
  11. package/dist/extractors/vidwish.d.ts +2 -0
  12. package/dist/extractors/vidwish.js +32 -0
  13. package/dist/index.d.ts +18 -0
  14. package/dist/index.js +18 -0
  15. package/dist/lib/config.d.ts +22 -0
  16. package/dist/lib/config.js +21 -0
  17. package/dist/providers/anime/anidb.d.ts +6 -0
  18. package/dist/providers/anime/anidb.js +88 -0
  19. package/dist/providers/anime/anikoto.d.ts +41 -0
  20. package/dist/providers/anime/anikoto.js +259 -0
  21. package/dist/providers/anime/animegg.d.ts +6 -0
  22. package/dist/providers/anime/animegg.js +107 -0
  23. package/dist/providers/anime/animeonsen.d.ts +6 -0
  24. package/dist/providers/anime/animeonsen.js +95 -0
  25. package/dist/providers/anime/animepahe.d.ts +6 -0
  26. package/dist/providers/anime/animepahe.js +102 -0
  27. package/dist/providers/anime/animesaturn.d.ts +8 -0
  28. package/dist/providers/anime/animesaturn.js +160 -0
  29. package/dist/providers/anime/animeunity.d.ts +4 -0
  30. package/dist/providers/anime/animeunity.js +108 -0
  31. package/dist/providers/anime/anizone.d.ts +12 -0
  32. package/dist/providers/anime/anizone.js +146 -0
  33. package/dist/providers/anime/gojo.d.ts +6 -0
  34. package/dist/providers/anime/gojo.js +83 -0
  35. package/dist/providers/meta/anilist/anilist.d.ts +28 -0
  36. package/dist/providers/meta/anilist/anilist.js +263 -0
  37. package/dist/providers/meta/anilist/queries.d.ts +22 -0
  38. package/dist/providers/meta/anilist/queries.js +405 -0
  39. package/dist/providers/meta/anilist/types.d.ts +213 -0
  40. package/dist/providers/meta/anilist/types.js +21 -0
  41. package/dist/providers/meta/anilist.d.ts +15 -0
  42. package/dist/providers/meta/anilist.js +94 -0
  43. package/dist/types/types.d.ts +88 -0
  44. package/dist/types/types.js +4 -0
  45. package/dist/utils/http.d.ts +13 -0
  46. package/dist/utils/http.js +39 -0
  47. package/dist/utils/proxy.d.ts +9 -0
  48. package/dist/utils/proxy.js +43 -0
  49. package/dist/utils/shared.d.ts +15 -0
  50. package/dist/utils/shared.js +64 -0
  51. package/dist/utils/unpack.d.ts +9 -0
  52. package/dist/utils/unpack.js +59 -0
  53. package/package.json +34 -0
  54. package/vitest.config.ts +7 -0
package/LICENSE ADDED
File without changes
package/README.md ADDED
@@ -0,0 +1,105 @@
1
+ # @kaizoku/core
2
+
3
+ The core TypeScript engine powering [kaizoku.site](https://kaizoku.site). It contains modular, tree-shakeable scrapers for popular anime providers, video extractors, AniList integration, and metadata mapping.
4
+
5
+ ## Features
6
+
7
+ - **9+ Anime Providers**: Anikoto, Anizone, AnimeOnsen, AnimeUnity, Gojo, AnimeGG, AniDB, AnimeSaturn, and AnimePahe.
8
+ - **AniList Meta API**: Search, fetch details, get popular/trending titles directly from AniList.
9
+ - **AniZip Mapping**: Seamless AniList-to-Provider mapping with TVDB metadata enrichment (descriptions, screencaps, air dates, episode length).
10
+ - **AniSkip Skip-Times**: Automatic integration with AniSkip API to fetch and inject intro/outro skip times into video streams.
11
+ - **Auto Proxying**: Built-in Anikuro proxy.
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm install @kaizoku/core
17
+ # or
18
+ pnpm add @kaizoku/core
19
+ # or
20
+ yarn add @kaizoku/core
21
+ ```
22
+
23
+ ## Quick Start
24
+
25
+ ### 1. Configuration
26
+ Set up global settings such as custom AniList proxy URLs or custom proxy options:
27
+
28
+ ```typescript
29
+ import { configure } from '@kaizoku/core';
30
+
31
+ configure({
32
+ proxy: {
33
+ enabled: true,
34
+ generateSourceProxies: true // automatically appends anikuro proxy links
35
+ }
36
+ });
37
+ ```
38
+
39
+ ### 2. Querying AniList
40
+ Get trending anime, popular items this season, or details:
41
+
42
+ ```typescript
43
+ import { anilist } from '@kaizoku/core';
44
+
45
+ // Get trending anime (paginated)
46
+ const trending = await anilist.getTrending(1, 10);
47
+
48
+ // Get detailed metadata
49
+ const details = await anilist.getAnimeDetail(166240);
50
+ ```
51
+
52
+ ### 3. Fetching Episodes by Provider (with AniZip Enrichment)
53
+ Map an AniList ID directly to a provider and retrieve fully enriched episode data:
54
+
55
+ ```typescript
56
+ import { anilist } from '@kaizoku/core';
57
+
58
+ // Map AniList ID to 'anizone' and get AniZip enriched episodes
59
+ const episodes = await anilist.fetchEpisodesByProvider('166240', 'anizone');
60
+ console.log(episodes[0]);
61
+ /*
62
+ {
63
+ id: "...",
64
+ number: 1,
65
+ title: "...",
66
+ image: "...", // TVDB Screencap
67
+ airDate: "...",
68
+ duration: 1500, // Enriched in seconds
69
+ description: "...",
70
+ providerName: "anizone"
71
+ }
72
+ */
73
+ ```
74
+
75
+ ### 4. Fetching Episode Streams (with AniSkip)
76
+ Retrieve video stream URLs for a provider's episode. The response is automatically normalized to a unified `VideoStream` interface:
77
+
78
+ ```typescript
79
+ import { animesaturn } from '@kaizoku/core/providers/animesaturn'; // or from index/exports
80
+
81
+ // Fetch sources and apply AniSkip skip times
82
+ const malId = 55701;
83
+ const episodeNumber = 1;
84
+ const stream = await animesaturn.fetchSources('episode-id', 'sub', 'server-name');
85
+
86
+ // If you want AniSkip boundary ranges, they will be attached as:
87
+ // stream.intro -> { start: 2633, end: 2723 }
88
+ // stream.outro -> { start: 1328, end: 1418 }
89
+ ```
90
+
91
+ ## Build & Test
92
+
93
+ To build the project:
94
+ ```bash
95
+ pnpm run build
96
+ ```
97
+
98
+ To run the Vitest test suite:
99
+ ```bash
100
+ pnpm run test
101
+ ```
102
+
103
+ ## Documentation
104
+
105
+ Full documentation, guides, and API references are available on [docs.kaizoku.site](https://docs.kaizoku.site) (coming soon!).
@@ -0,0 +1,6 @@
1
+ import { VideoStream } from "../types/types.js";
2
+ /**
3
+ * Kwik.cx — AnimePahe's video host. The stream URL is hidden behind a packed-JS
4
+ * script; once unpacked, it's a `const source = '....m3u8'` literal.
5
+ */
6
+ export declare function extractKwik(streamUrl: string): Promise<VideoStream>;
@@ -0,0 +1,33 @@
1
+ import { load } from "cheerio";
2
+ import { getText } from "../utils/http.js";
3
+ import { unpackJs } from "../utils/unpack.js";
4
+ import { encodeAnikuro } from "../utils/proxy.js";
5
+ import { getVideoType } from "../utils/shared.js";
6
+ const REFERER = "https://animepahe.pw/";
7
+ /**
8
+ * Kwik.cx — AnimePahe's video host. The stream URL is hidden behind a packed-JS
9
+ * script; once unpacked, it's a `const source = '....m3u8'` literal.
10
+ */
11
+ export async function extractKwik(streamUrl) {
12
+ const html = await getText(streamUrl, { Referer: REFERER });
13
+ const $ = load(html);
14
+ let streamLink;
15
+ // Matches the original's behavior exactly: every <script> is checked, and if more
16
+ // than one happens to match the packed-eval pattern, the last one wins (no early exit).
17
+ $("script").each((_, el) => {
18
+ const scriptText = $(el).html() ?? "";
19
+ if (!/eval\(function\(p,a,c,k,e,d\)/.test(scriptText))
20
+ return;
21
+ const unpacked = unpackJs(scriptText);
22
+ const match = unpacked.match(/const\s+source\s*=\s*'([^']+\.m3u8)'/);
23
+ streamLink = match?.[1]?.replace(/\{|\}|"|file:/g, "");
24
+ });
25
+ if (!streamLink)
26
+ throw new Error("Kwik: unable to extract stream URL");
27
+ const proxiedUrl = encodeAnikuro(streamLink, "https://kwik.cx/");
28
+ return {
29
+ sources: [{ url: streamLink, isM3U8: true, quality: "single", proxiedUrl, type: getVideoType(streamLink) }],
30
+ subtitles: [],
31
+ headers: { Referer: "https://kwik.cx/" },
32
+ };
33
+ }
@@ -0,0 +1,2 @@
1
+ import type { VideoStream } from "../types/types.js";
2
+ export declare function extractMegaPlay(videoUrl: URL, referer: string): Promise<VideoStream>;
@@ -0,0 +1,32 @@
1
+ import { getJson, getText } from "../utils/http.js";
2
+ import { getVideoType, parseEmbedMediaId } from "../utils/shared.js";
3
+ import { encodeAnikuro } from "../utils/proxy.js";
4
+ const BASE_URL = "https://megaplay.buzz/stream";
5
+ export async function extractMegaPlay(videoUrl, referer) {
6
+ const initialHtml = await getText(videoUrl.href, {
7
+ Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
8
+ "X-Requested-With": "XMLHttpRequest",
9
+ Referer: `${referer}/`,
10
+ });
11
+ const id = parseEmbedMediaId(initialHtml);
12
+ if (!id)
13
+ throw new Error("MegaPlay: could not resolve media id");
14
+ const result = await getJson(`${BASE_URL}/getSources?id=${id}&id=${id}`, {
15
+ Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
16
+ "X-Requested-With": "XMLHttpRequest",
17
+ Referer: videoUrl.href,
18
+ });
19
+ const fileUrl = result.sources.file;
20
+ const proxiedUrl = encodeAnikuro(fileUrl, `${videoUrl.origin}/`);
21
+ return {
22
+ sources: [{ url: fileUrl, isM3U8: fileUrl.includes("m3u8"), quality: "default", proxiedUrl, type: getVideoType(fileUrl) }],
23
+ subtitles: (result.tracks ?? []).map((t) => ({
24
+ url: t.file,
25
+ lang: t.label,
26
+ isDefault: t.default,
27
+ })),
28
+ intro: result.intro,
29
+ outro: result.outro,
30
+ headers: { Referer: `${videoUrl.origin}/` },
31
+ };
32
+ }
@@ -0,0 +1,4 @@
1
+ import type { VideoStream } from "../types/types.js";
2
+ export declare function extractStreamWish(streamUrl: string, options?: {
3
+ headersOverride?: Record<string, string>;
4
+ }): Promise<VideoStream>;
@@ -0,0 +1,86 @@
1
+ import { load } from "cheerio";
2
+ import { getText } from "../utils/http.js";
3
+ import { unpackJs } from "../utils/unpack.js";
4
+ import { encodeAnikuro } from "../utils/proxy.js";
5
+ function extractEnglishSubtitleLink(tracksBlock) {
6
+ const match = tracksBlock.match(/\{[^}]*file\s*:\s*"([^"]+)"[^}]*label\s*:\s*"English"[^}]*kind\s*:\s*"captions"/i);
7
+ return match?.[1];
8
+ }
9
+ /** Some StreamWish variants store the real URL in a separate `var links = {...}` object
10
+ * and reference it symbolically (e.g. "links.auto") instead of a literal URL. */
11
+ function extractLinksObject(source) {
12
+ const match = source.match(/var\s+links\s*=\s*\{([\s\S]*?)\};/);
13
+ if (!match)
14
+ return {};
15
+ const body = match[1];
16
+ const entries = {};
17
+ const entryPattern = /"?(\w+)"?\s*:\s*"((?:\\.|[^"\\])*)"/g;
18
+ let entryMatch;
19
+ while ((entryMatch = entryPattern.exec(body)) !== null) {
20
+ entries[entryMatch[1]] = entryMatch[2];
21
+ }
22
+ return entries;
23
+ }
24
+ function hasScheme(value) {
25
+ try {
26
+ return Boolean(new URL(value).protocol);
27
+ }
28
+ catch {
29
+ return false; // a bare expression like "links.auto" won't parse as an absolute URL
30
+ }
31
+ }
32
+ export async function extractStreamWish(streamUrl, options = {}) {
33
+ if (!streamUrl)
34
+ throw new Error("StreamWish: invalid stream link");
35
+ const html = await getText(streamUrl);
36
+ const $ = load(html);
37
+ let streamLink = "";
38
+ let unpackedData = "";
39
+ let subtitleUrl;
40
+ $("script").each((_, el) => {
41
+ if (streamLink)
42
+ return; // already resolved on an earlier script tag
43
+ const scriptText = $(el).html() ?? "";
44
+ // Strategy 1: unobfuscated `file: "..."` sitting right in the script.
45
+ const directFileMatch = scriptText.match(/file:\s*"(.*?)"/);
46
+ if (directFileMatch) {
47
+ unpackedData = scriptText;
48
+ streamLink = directFileMatch[1];
49
+ }
50
+ else if (/eval\(function\(p,a,c,k,e,d\)/.test(scriptText)) {
51
+ // Strategy 2: packed-eval obfuscation — unpack, then pull the sources[] block.
52
+ const unpacked = unpackJs(scriptText);
53
+ unpackedData = unpacked;
54
+ const sourcesMatch = unpacked.match(/sources:\s*\[([\s\S]*?)\]/);
55
+ streamLink = (sourcesMatch?.[1] ?? "").replace(/\{|\}|"|file:/g, "");
56
+ }
57
+ // Regardless of which strategy fired: pull subtitles, and resolve a symbolic
58
+ // "links.xxx" reference if streamLink isn't a real URL.
59
+ const tracksMatch = unpackedData.match(/tracks:\[([\s\S]*?)\]/);
60
+ if (tracksMatch) {
61
+ subtitleUrl = extractEnglishSubtitleLink(tracksMatch[1]);
62
+ }
63
+ if (streamLink && !hasScheme(streamLink)) {
64
+ const linksTable = extractLinksObject(unpackedData);
65
+ for (const variable of streamLink.split("||")) {
66
+ const parts = variable.split(".");
67
+ if (parts.length === 2 && parts[0].trim() === "links") {
68
+ streamLink = linksTable[parts[1].trim()] ?? "";
69
+ }
70
+ }
71
+ }
72
+ });
73
+ if (!streamLink)
74
+ throw new Error("StreamWish: couldn't get any streams");
75
+ const subtitles = subtitleUrl ? [{ url: subtitleUrl, lang: "English", isDefault: true }] : [];
76
+ const headers = options.headersOverride ?? {
77
+ Referer: streamUrl,
78
+ Origin: `https://${new URL(streamUrl).host}`,
79
+ };
80
+ const proxiedUrl = encodeAnikuro(streamLink, streamUrl);
81
+ return {
82
+ sources: [{ url: streamLink, isM3U8: streamLink.includes("m3u8"), quality: "multi-quality", proxiedUrl }],
83
+ subtitles,
84
+ headers,
85
+ };
86
+ }
@@ -0,0 +1,2 @@
1
+ import type { VideoStream } from "../types/types.js";
2
+ export declare function extractVidtube(streamUrl: string): Promise<VideoStream>;
@@ -0,0 +1,24 @@
1
+ import { load } from "cheerio";
2
+ import { getJson, getText } from "../utils/http.js";
3
+ import { encodeAnikuro } from "../utils/proxy.js";
4
+ export async function extractVidtube(streamUrl) {
5
+ const html = await getText(streamUrl);
6
+ const $ = load(html);
7
+ const id = $("#megaplay-player").attr("data-id");
8
+ if (!id)
9
+ throw new Error("Vidtube: failed to extract video id from the page");
10
+ // The original reads the last URL path segment as a sub/dub "type" flag for the API call.
11
+ const type = new URL(streamUrl).pathname.split("/").filter(Boolean).pop();
12
+ const data = await getJson(`https://vidtube.site/stream/getSourcesNew?id=${id}&type=${type}`, { "X-Requested-With": "XMLHttpRequest" });
13
+ const playlist = data.sources?.file;
14
+ if (!playlist)
15
+ throw new Error("Vidtube: no video sources found");
16
+ const captions = (data.tracks ?? []).filter((t) => t.kind === "captions");
17
+ const subtitleUrl = captions.find((t) => t.lang?.toLowerCase() === "english")?.file ?? captions.find((t) => t.default)?.file;
18
+ const proxiedUrl = encodeAnikuro(playlist, "https://vidtube.site/");
19
+ return {
20
+ sources: [{ url: playlist, isM3U8: playlist.includes("m3u8"), quality: "multi-quality", proxiedUrl }],
21
+ subtitles: subtitleUrl ? [{ url: subtitleUrl, lang: "English", isDefault: true }] : [],
22
+ headers: { Referer: "https://vidtube.site/", Origin: "https://vidtube.site" },
23
+ };
24
+ }
@@ -0,0 +1,2 @@
1
+ import type { VideoStream } from "../types/types.js";
2
+ export declare function extractVidWish(videoUrl: URL, referer: string): Promise<VideoStream>;
@@ -0,0 +1,32 @@
1
+ import { getJson, getText } from "../utils/http.js";
2
+ import { getVideoType, parseEmbedMediaId } from "../utils/shared.js";
3
+ import { encodeAnikuro } from "../utils/proxy.js";
4
+ const BASE_URL = "https://vidwish.live/stream";
5
+ export async function extractVidWish(videoUrl, referer) {
6
+ const initialUrl = `${videoUrl.href}?autostart=true`;
7
+ const initialHtml = await getText(initialUrl, {
8
+ Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
9
+ Referer: `${referer}/`,
10
+ });
11
+ const id = parseEmbedMediaId(initialHtml);
12
+ if (!id)
13
+ throw new Error("VidWish: could not resolve media id");
14
+ const result = await getJson(`${BASE_URL}/getSources?id=${id}&id=${id}`, {
15
+ Accept: "application/json, text/javascript, */*; q=0.01",
16
+ "X-Requested-With": "XMLHttpRequest",
17
+ Referer: initialUrl,
18
+ });
19
+ const fileUrl = result.sources.file;
20
+ const proxiedUrl = encodeAnikuro(fileUrl, `${videoUrl.origin}/`);
21
+ return {
22
+ sources: [{ url: fileUrl, isM3U8: fileUrl.includes("m3u8"), quality: "default", proxiedUrl, type: getVideoType(fileUrl) }],
23
+ subtitles: (result.tracks ?? []).map((t) => ({
24
+ url: t.file,
25
+ lang: t.label,
26
+ isDefault: t.default,
27
+ })),
28
+ intro: result.intro,
29
+ outro: result.outro,
30
+ headers: { Referer: `${videoUrl.origin}/` },
31
+ };
32
+ }
@@ -0,0 +1,18 @@
1
+ export * from "./types/types.js";
2
+ export { extractKwik } from "./extractors/kwik.js";
3
+ export { extractStreamWish } from "./extractors/streamwish.js";
4
+ export { extractVidtube } from "./extractors/vidtube.js";
5
+ export { extractMegaPlay } from "./extractors/megaplay.js";
6
+ export { extractVidWish } from "./extractors/vidwish.js";
7
+ export { configure } from "./lib/config.js";
8
+ export * as anilist from "./providers/meta/anilist/anilist.js";
9
+ export * from "./providers/meta/anilist/types.js";
10
+ export * as anikoto from './providers/anime/anikoto.js';
11
+ export * as anizone from './providers/anime/anizone.js';
12
+ export * as animeonsen from './providers/anime/animeonsen.js';
13
+ export * as animeunity from './providers/anime/animeunity.js';
14
+ export * as gojo from './providers/anime/gojo.js';
15
+ export * as animegg from './providers/anime/animegg.js';
16
+ export * as anidb from './providers/anime/anidb.js';
17
+ export * as animesaturn from './providers/anime/animesaturn.js';
18
+ export * as animepahe from './providers/anime/animepahe.js';
package/dist/index.js ADDED
@@ -0,0 +1,18 @@
1
+ export * from "./types/types.js";
2
+ export { extractKwik } from "./extractors/kwik.js";
3
+ export { extractStreamWish } from "./extractors/streamwish.js";
4
+ export { extractVidtube } from "./extractors/vidtube.js";
5
+ export { extractMegaPlay } from "./extractors/megaplay.js";
6
+ export { extractVidWish } from "./extractors/vidwish.js";
7
+ export { configure } from "./lib/config.js";
8
+ export * as anilist from "./providers/meta/anilist/anilist.js";
9
+ export * from "./providers/meta/anilist/types.js";
10
+ export * as anikoto from './providers/anime/anikoto.js';
11
+ export * as anizone from './providers/anime/anizone.js';
12
+ export * as animeonsen from './providers/anime/animeonsen.js';
13
+ export * as animeunity from './providers/anime/animeunity.js';
14
+ export * as gojo from './providers/anime/gojo.js';
15
+ export * as animegg from './providers/anime/animegg.js';
16
+ export * as anidb from './providers/anime/anidb.js';
17
+ export * as animesaturn from './providers/anime/animesaturn.js';
18
+ export * as animepahe from './providers/anime/animepahe.js';
@@ -0,0 +1,22 @@
1
+ export interface ProxyConfig {
2
+ enabled?: boolean;
3
+ /** Custom proxy URL for the package to route standard requests through */
4
+ customUrl?: string;
5
+ /** Whether to append the hardcoded anikuro. proxies to the video sources */
6
+ generateSourceProxies?: boolean;
7
+ }
8
+ export interface ConfigOptions {
9
+ /** Proxy Configuration */
10
+ proxy?: ProxyConfig;
11
+ /** Optional custom AniList URL */
12
+ anilistProxyUrl?: string;
13
+ }
14
+ declare class KaizokuConfig {
15
+ proxy: ProxyConfig;
16
+ anilistUrl: string;
17
+ configure(options: ConfigOptions): void;
18
+ }
19
+ export declare const config: KaizokuConfig;
20
+ /** Global configure function for the Kaizoku package */
21
+ export declare function configure(options: ConfigOptions): void;
22
+ export {};
@@ -0,0 +1,21 @@
1
+ // Global state
2
+ class KaizokuConfig {
3
+ proxy = {
4
+ enabled: true,
5
+ generateSourceProxies: true
6
+ };
7
+ anilistUrl = "https://graphql.anilist.co";
8
+ configure(options) {
9
+ if (options.anilistProxyUrl) {
10
+ this.anilistUrl = options.anilistProxyUrl;
11
+ }
12
+ if (options.proxy) {
13
+ this.proxy = { ...this.proxy, ...options.proxy };
14
+ }
15
+ }
16
+ }
17
+ export const config = new KaizokuConfig();
18
+ /** Global configure function for the Kaizoku package */
19
+ export function configure(options) {
20
+ config.configure(options);
21
+ }
@@ -0,0 +1,6 @@
1
+ import { AnimeResult, AnimeInfo, VideoStream } from '../../types/types.js';
2
+ export declare function search(query: string): Promise<{
3
+ results: AnimeResult[];
4
+ }>;
5
+ export declare function fetchAnimeInfo(aliasId: string): Promise<AnimeInfo>;
6
+ export declare function fetchEpisodeSources(episodeId: string, malId?: number, episodeNumber?: number): Promise<VideoStream>;
@@ -0,0 +1,88 @@
1
+ import { load } from 'cheerio';
2
+ import { httpGet, getText } from '../../utils/http.js';
3
+ import { encodeAnikuro } from '../../utils/proxy.js';
4
+ import { applyAniSkip, getVideoType } from '../../utils/shared.js';
5
+ const BASE_URL = 'https://anidb.app';
6
+ const HEADERS = {
7
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
8
+ };
9
+ export async function search(query) {
10
+ const html = await getText(`${BASE_URL}/browse?q=${encodeURIComponent(query)}`, HEADERS);
11
+ const $ = load(html);
12
+ const results = [];
13
+ $('.anime-grid a').each((_, elem) => {
14
+ const id = $(elem).attr('href')?.split('/').pop();
15
+ const title = $(elem).find('p').text().trim();
16
+ const image = $(elem).find('img').attr('src');
17
+ if (id) {
18
+ results.push({
19
+ id,
20
+ title,
21
+ image: image || '',
22
+ });
23
+ }
24
+ });
25
+ return { results };
26
+ }
27
+ export async function fetchAnimeInfo(aliasId) {
28
+ const id = aliasId.split('-').pop();
29
+ const res = await httpGet(`${BASE_URL}/api/frontend/anime/${id}/episodes`, HEADERS);
30
+ const json = await res.json();
31
+ const info = {
32
+ id: aliasId,
33
+ title: '', // Not returned by this endpoint natively
34
+ episodes: [],
35
+ };
36
+ if (!json.episodes)
37
+ return info;
38
+ info.episodes = json.episodes.map(ep => ({
39
+ id: ep.id.toString(),
40
+ number: parseInt(ep.number, 10),
41
+ filler: ep.filler ?? false,
42
+ }));
43
+ info.totalEpisodes = info.episodes.length;
44
+ return info;
45
+ }
46
+ export async function fetchEpisodeSources(episodeId, malId, episodeNumber) {
47
+ const res = await httpGet(`${BASE_URL}/api/frontend/episode/${episodeId}/languages`, HEADERS);
48
+ const json = await res.json();
49
+ const stream = { sources: [], subtitles: [] };
50
+ if (!json.languages)
51
+ return stream;
52
+ const promises = json.languages.map(async (lang) => {
53
+ const embedUrl = lang.embed_url;
54
+ if (!embedUrl)
55
+ return;
56
+ try {
57
+ const embedHtml = await getText(embedUrl, HEADERS);
58
+ const $ = load(embedHtml);
59
+ let foundMatch = false;
60
+ $('body script').each((_, script) => {
61
+ if (foundMatch)
62
+ return;
63
+ const scriptContent = $(script).text();
64
+ const match = scriptContent.match(/file:\s*'([^']+\.m3u8[^']*)'/);
65
+ if (match && match[1]) {
66
+ const quality = lang.name?.toString() || 'default';
67
+ const proxiedUrl = encodeAnikuro(match[1], embedUrl);
68
+ stream.sources.push({
69
+ url: match[1],
70
+ quality,
71
+ type: getVideoType(match[1]),
72
+ isM3U8: true,
73
+ proxiedUrl,
74
+ });
75
+ foundMatch = true;
76
+ }
77
+ });
78
+ }
79
+ catch (e) {
80
+ // ignore failing embeds
81
+ console.log("error in anidb", e);
82
+ }
83
+ });
84
+ await Promise.all(promises);
85
+ if (stream.sources.length === 0)
86
+ throw new Error('No video sources found');
87
+ return applyAniSkip(stream, malId, episodeNumber);
88
+ }
@@ -0,0 +1,41 @@
1
+ import { AnimeResult, AnimeInfo, PageResult, VideoStream } from '../../types/types.js';
2
+ type AnikotoServers = 'vidstream-2' | 'vidcloud-1';
3
+ type SubOrDub = 'sub' | 'dub' | 'raw';
4
+ export declare function fetchHome(): Promise<{
5
+ spotlight: AnimeResult[];
6
+ recentlyUpdated: AnimeResult[];
7
+ upcoming: AnimeResult[];
8
+ sections: {
9
+ recentlyAdded: AnimeResult[];
10
+ recentlyReleased: AnimeResult[];
11
+ recentlyCompleted: AnimeResult[];
12
+ };
13
+ topAnime: {
14
+ day: AnimeResult[];
15
+ week: AnimeResult[];
16
+ month: AnimeResult[];
17
+ };
18
+ }>;
19
+ export declare function fetchSchedule(timezone: number): Promise<{
20
+ data: any[];
21
+ }>;
22
+ export declare function search(query: string, page?: number): Promise<PageResult<AnimeResult>>;
23
+ export declare function searchSuggestions(query: string): Promise<{
24
+ data: AnimeResult[];
25
+ }>;
26
+ export declare function fetchAnimeInfo(id: string): Promise<AnimeInfo>;
27
+ export declare function fetchMostPopular(page?: number): Promise<PageResult<AnimeResult>>;
28
+ export declare function fetchRecentlyUpdated(page?: number): Promise<PageResult<AnimeResult>>;
29
+ export declare function fetchRecentlyAdded(page?: number): Promise<PageResult<AnimeResult>>;
30
+ export declare function fetchUpcoming(page?: number): Promise<PageResult<AnimeResult>>;
31
+ export declare function fetchReleasing(page?: number): Promise<PageResult<AnimeResult>>;
32
+ export declare function fetchRecentlyCompleted(page?: number): Promise<PageResult<AnimeResult>>;
33
+ export declare function fetchAtoZList(sort?: string, page?: number): Promise<PageResult<AnimeResult>>;
34
+ export declare function fetchServers(episodeId: string): Promise<{
35
+ sub: any[];
36
+ dub: any[];
37
+ raw: any[];
38
+ episodeNumber: number;
39
+ }>;
40
+ export declare function fetchSources(episodeId: string, version?: SubOrDub, serverName?: AnikotoServers): Promise<VideoStream>;
41
+ export {};