@mr-quin/dango-manifests 0.2.0 → 0.3.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.
package/README.md CHANGED
@@ -6,42 +6,42 @@ This package is data, not code. There is no `index.ts`: consumers import each ma
6
6
 
7
7
  ## Shipped manifests
8
8
 
9
- | Id | Endpoint | Notes |
10
- | -------------------- | ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
11
- | `builtin:dandanplay` | `baseUrl` (default `api.dandanplay.net`) | Unified DDP source. Default official; optional `appId`/`appSecret` signing, `auth` headers, or none (proxy). `baseUrl` overrides the endpoint (proxy / self-hosted). |
12
- | `builtin:bilibili` | `api.bilibili.com` | Parallel media_bangumi + media_ft search; xml + protobuf danmaku variants. |
13
- | `builtin:tencent` | `pbaccess.video.qq.com` + `dm.video.qq.com` | POST bodies, page-cursor episode pagination via forEach `breakOn`, two-phase danmaku (segment index then per-segment fetch). |
14
- | `builtin:hanjutv` | `hxqapi.hiyun.tv` + `hxqapi.zmdcq.com` | Korean-drama source; search, detail-driven episodes, per-segment danmaku. |
15
- | `builtin:mango` | `*.mgtv.com` + `*.hitv.com` | Mango TV; source=imgo search, two-stage showlist episodes, per-minute CDN danmaku shards. |
16
- | `builtin:migu` | `*.migu.cn` + `*.miguvideo.com` | Migu Video; content-info lookup, episodes list, segmented danmaku. |
17
- | `builtin:youku` | `*.youku.com` + `log.mmstat.com` | Youku; HTML-stripped search, openapi episodes, signed per-minute danmaku POSTs (cna guid + \_m_h5_tk token). |
18
- | `builtin:iqiyi` | `*.iqiyi.com` + `mesh.if.iqiyi.com` | iQIYI; /v\_ watch-page search, signed base_info episodes, zlib-deflated XML bullet shards. |
19
- | `builtin:aiyifan` | `yfsp.tv` + `rankv21.tripdata.app` | Aiyifan; homepage-scraped pub/priv keys sign each JSON request; getBarrage danmaku. |
20
- | `builtin:bahamut` | `api.gamer.com.tw` | Bahamut Anime (Traditional Chinese); single danmu.php fetch, time in tenths of a second. |
21
- | `builtin:maiduidui` | `mob.mddcloud.com.cn` | Maiduidui; nested search, listVodSactions episodes, per-minute vodBarrage shards. |
22
- | `builtin:renren` | `api.gorafie.com` + `static-dm.qwdjapp.com` | Renren (foreign drama); array-rooted danmaku with CSV `p` strings. |
23
- | `builtin:sohu` | `*.sohu.com` | Sohu TV; site=1 search, album episodes, paginated dmListAll danmaku (decimal/hex color handling). |
9
+ | Id | Endpoint | Notes |
10
+ | ------------ | ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
11
+ | `dandanplay` | `baseUrl` (default `api.dandanplay.net`) | Unified DDP source. Default official; optional `appId`/`appSecret` signing, `auth` headers, or none (proxy). `baseUrl` overrides the endpoint (proxy / self-hosted). |
12
+ | `bilibili` | `api.bilibili.com` | Parallel media_bangumi + media_ft search; xml + protobuf danmaku variants. |
13
+ | `tencent` | `pbaccess.video.qq.com` + `dm.video.qq.com` | POST bodies, page-cursor episode pagination via forEach `breakOn`, two-phase danmaku (segment index then per-segment fetch). |
14
+ | `hanjutv` | `hxqapi.hiyun.tv` + `hxqapi.zmdcq.com` | Korean-drama source; search, detail-driven episodes, per-segment danmaku. |
15
+ | `mango` | `*.mgtv.com` + `*.hitv.com` | Mango TV; source=imgo search, two-stage showlist episodes, per-minute CDN danmaku shards. |
16
+ | `migu` | `*.migu.cn` + `*.miguvideo.com` | Migu Video; content-info lookup, episodes list, segmented danmaku. |
17
+ | `youku` | `*.youku.com` + `log.mmstat.com` | Youku; HTML-stripped search, openapi episodes, signed per-minute danmaku POSTs (cna guid + \_m_h5_tk token). |
18
+ | `iqiyi` | `*.iqiyi.com` + `mesh.if.iqiyi.com` | iQIYI; /v\_ watch-page search, signed base_info episodes, zlib-deflated XML bullet shards. |
19
+ | `aiyifan` | `yfsp.tv` + `rankv21.tripdata.app` | Aiyifan; homepage-scraped pub/priv keys sign each JSON request; getBarrage danmaku. |
20
+ | `bahamut` | `api.gamer.com.tw` | Bahamut Anime (Traditional Chinese); single danmu.php fetch, time in tenths of a second. |
21
+ | `maiduidui` | `mob.mddcloud.com.cn` | Maiduidui; nested search, listVodSactions episodes, per-minute vodBarrage shards. |
22
+ | `renren` | `api.gorafie.com` + `static-dm.qwdjapp.com` | Renren (foreign drama); array-rooted danmaku with CSV `p` strings. |
23
+ | `sohu` | `*.sohu.com` | Sohu TV; site=1 search, album episodes, paginated dmListAll danmaku (decimal/hex color handling). |
24
24
 
25
25
  ## Layout
26
26
 
27
27
  ```
28
28
  src/
29
29
  manifests/
30
- builtin-aiyifan.json
31
- builtin-bahamut.json
32
- builtin-bilibili.json
33
- builtin-dandanplay.json
34
- builtin-hanjutv.json
35
- builtin-iqiyi.json
36
- builtin-maiduidui.json
37
- builtin-mango.json
38
- builtin-migu.json
39
- builtin-renren.json
40
- builtin-sohu.json
41
- builtin-tencent.json
42
- builtin-youku.json
30
+ aiyifan.json
31
+ bahamut.json
32
+ bilibili.json
33
+ dandanplay.json
34
+ hanjutv.json
35
+ iqiyi.json
36
+ maiduidui.json
37
+ mango.json
38
+ migu.json
39
+ renren.json
40
+ sohu.json
41
+ tencent.json
42
+ youku.json
43
43
  __tests__/
44
- builtin-*.test.ts # per-manifest pipeline tests
44
+ *.test.ts # per-manifest pipeline tests
45
45
  fixtures/ # captured representative responses
46
46
  mockFetcher.ts # shared test fetcher (string + Uint8Array bodies)
47
47
  scripts/
@@ -52,7 +52,7 @@ scripts/
52
52
 
53
53
  ```ts
54
54
  import { ManifestRunner, zManifest } from '@mr-quin/dango'
55
- import builtinDandanplay from '@mr-quin/dango-manifests/manifests/builtin-dandanplay.json' with { type: 'json' }
55
+ import builtinDandanplay from '@mr-quin/dango-manifests/manifests/dandanplay.json' with { type: 'json' }
56
56
 
57
57
  const manifest = zManifest.parse(builtinDandanplay)
58
58
  const runner = new ManifestRunner(manifest, { fetcher })
@@ -66,9 +66,46 @@ const danmaku = await runner.runDanmaku({
66
66
  })
67
67
  ```
68
68
 
69
+ ## CDN consumption
70
+
71
+ A host can discover and fetch manifests over a CDN without bundling this package at build time. jsDelivr and unpkg serve the published package files, so a host reads `catalog.json` for the index, then fetches each entry's `file` path.
72
+
73
+ ```
74
+ https://cdn.jsdelivr.net/npm/@mr-quin/dango-manifests@<version>/catalog.json
75
+ https://cdn.jsdelivr.net/npm/@mr-quin/dango-manifests@<version>/<file>
76
+ ```
77
+
78
+ `catalog.json` is generated from the manifests on disk and ships with the package:
79
+
80
+ ```jsonc
81
+ {
82
+ "packageVersion": "0.3.0",
83
+ "manifests": [
84
+ {
85
+ "id": "bilibili",
86
+ "name": "Bilibili",
87
+ "version": "0.3.0",
88
+ "apiVersion": 1,
89
+ "file": "src/manifests/bilibili.json",
90
+ },
91
+ // ...
92
+ ],
93
+ }
94
+ ```
95
+
96
+ Each `file` is the in-package path to fetch, e.g.:
97
+
98
+ ```
99
+ https://cdn.jsdelivr.net/npm/@mr-quin/dango-manifests@0.3.0/src/manifests/bilibili.json
100
+ ```
101
+
102
+ unpkg works the same way (`https://unpkg.com/@mr-quin/dango-manifests@<version>/...`). Pin a `<version>` for reproducibility; omit it to track the latest published release.
103
+
104
+ `catalog.json` is regenerated as part of `bun run build`; `bun run check` fails if the committed copy is stale (run `bun run catalog` and commit the result).
105
+
69
106
  ## Routing notes
70
107
 
71
- **DanDanPlay** (`builtin:dandanplay`) is one manifest for every DDP endpoint over the `/api/v2` paths. `baseUrl` (config) defaults to the official `api.dandanplay.net`; point it at a proxy or self-hosted server to override. Auth is picked by config: `appId` + `appSecret` sign each request (`X-Signature: Base64(SHA256(appId + timestamp + path + appSecret))`, secret never sent); else `auth.enabled` + `auth.headers` attach custom headers; else no auth (for a proxy that signs server-side). The official API is just the case with no `baseUrl` and credentials set.
108
+ **DanDanPlay** (`dandanplay`) is one manifest for every DDP endpoint over the `/api/v2` paths. `baseUrl` (config) defaults to the official `api.dandanplay.net`; point it at a proxy or self-hosted server to override. Auth is picked by config: `appId` + `appSecret` sign each request (`X-Signature: Base64(SHA256(appId + timestamp + path + appSecret))`, secret never sent); else `auth.enabled` + `auth.headers` attach custom headers; else no auth (for a proxy that signs server-side). The official API is just the case with no `baseUrl` and credentials set.
72
109
 
73
110
  **Bilibili** and **Tencent** call their public APIs directly. Both rely on `rewriteHeaders` for `Origin` / `Referer`, which a browser host's `FetchLike` applies via a request-header rewrite mechanism such as `chrome.declarativeNetRequest`. The smoke script merges those headers in directly since Node fetch lets you set them.
74
111
 
@@ -86,7 +123,7 @@ The smoke fetcher keeps a per-host cookie jar so bilibili's anti-bot wall lets u
86
123
 
87
124
  ## Adding a new manifest
88
125
 
89
- 1. Drop the JSON file in `src/manifests/`. Use `id: 'builtin:<source>'`.
126
+ 1. Drop the JSON file in `src/manifests/`. Use `id: '<source>'`.
90
127
  2. Capture representative responses from the source's API to `src/__tests__/fixtures/`.
91
128
  3. Write a per-manifest test that:
92
129
  - Parses the manifest against `zManifest` (catches schema drift)
@@ -107,5 +144,5 @@ Manifests in this package are vetted at PR review time and shipped as built-ins.
107
144
  | `bun test` | bun test, fixture-backed pipeline tests |
108
145
  | `bun run smoke <source>` | Live end-to-end test against the real API (manual, **not CI**) |
109
146
  | `bun run type-check` | tsc --noEmit |
110
- | `bun run lint` | oxlint |
111
- | `bun run build` | Compile with tsc |
147
+ | `bun run catalog` | Regenerate `catalog.json` from the manifests on disk |
148
+ | `bun run catalog:check` | Fail if the committed `catalog.json` is stale |
package/catalog.json ADDED
@@ -0,0 +1,96 @@
1
+ {
2
+ "packageVersion": "0.3.0",
3
+ "manifests": [
4
+ {
5
+ "id": "aiyifan",
6
+ "name": "Aiyifan",
7
+ "version": "0.3.0",
8
+ "apiVersion": 1,
9
+ "file": "src/manifests/aiyifan.json"
10
+ },
11
+ {
12
+ "id": "bahamut",
13
+ "name": "Bahamut",
14
+ "version": "0.3.0",
15
+ "apiVersion": 1,
16
+ "file": "src/manifests/bahamut.json"
17
+ },
18
+ {
19
+ "id": "bilibili",
20
+ "name": "Bilibili",
21
+ "version": "0.3.0",
22
+ "apiVersion": 1,
23
+ "file": "src/manifests/bilibili.json"
24
+ },
25
+ {
26
+ "id": "dandanplay",
27
+ "name": "DanDanPlay",
28
+ "version": "0.3.0",
29
+ "apiVersion": 1,
30
+ "file": "src/manifests/dandanplay.json"
31
+ },
32
+ {
33
+ "id": "hanjutv",
34
+ "name": "Hanjutv",
35
+ "version": "0.3.0",
36
+ "apiVersion": 1,
37
+ "file": "src/manifests/hanjutv.json"
38
+ },
39
+ {
40
+ "id": "iqiyi",
41
+ "name": "iQIYI",
42
+ "version": "0.3.0",
43
+ "apiVersion": 1,
44
+ "file": "src/manifests/iqiyi.json"
45
+ },
46
+ {
47
+ "id": "maiduidui",
48
+ "name": "Maiduidui",
49
+ "version": "0.3.0",
50
+ "apiVersion": 1,
51
+ "file": "src/manifests/maiduidui.json"
52
+ },
53
+ {
54
+ "id": "mango",
55
+ "name": "Mango TV",
56
+ "version": "0.3.0",
57
+ "apiVersion": 1,
58
+ "file": "src/manifests/mango.json"
59
+ },
60
+ {
61
+ "id": "migu",
62
+ "name": "Migu Video",
63
+ "version": "0.3.0",
64
+ "apiVersion": 1,
65
+ "file": "src/manifests/migu.json"
66
+ },
67
+ {
68
+ "id": "renren",
69
+ "name": "Renren",
70
+ "version": "0.3.0",
71
+ "apiVersion": 1,
72
+ "file": "src/manifests/renren.json"
73
+ },
74
+ {
75
+ "id": "sohu",
76
+ "name": "Sohu",
77
+ "version": "0.3.0",
78
+ "apiVersion": 1,
79
+ "file": "src/manifests/sohu.json"
80
+ },
81
+ {
82
+ "id": "tencent",
83
+ "name": "Tencent Video",
84
+ "version": "0.3.0",
85
+ "apiVersion": 1,
86
+ "file": "src/manifests/tencent.json"
87
+ },
88
+ {
89
+ "id": "youku",
90
+ "name": "Youku",
91
+ "version": "0.3.0",
92
+ "apiVersion": 1,
93
+ "file": "src/manifests/youku.json"
94
+ }
95
+ ]
96
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mr-quin/dango-manifests",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Built-in dango manifests for danmaku sources",
5
5
  "author": "Mr-Quin",
6
6
  "license": "MIT",
@@ -17,11 +17,14 @@
17
17
  "./manifests/*.json": "./src/manifests/*.json"
18
18
  },
19
19
  "files": [
20
- "src/manifests"
20
+ "src/manifests",
21
+ "catalog.json"
21
22
  ],
22
23
  "scripts": {
23
24
  "type-check": "tsc -p tsconfig.json",
24
25
  "test": "bun test",
26
+ "catalog": "bun run scripts/gen-catalog.ts",
27
+ "catalog:check": "bun run scripts/gen-catalog.ts --check",
25
28
  "smoke": "bun run scripts/smoke-test.ts"
26
29
  },
27
30
  "devDependencies": {
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "apiVersion": 1,
3
- "id": "builtin:aiyifan",
3
+ "id": "aiyifan",
4
4
  "name": "Aiyifan",
5
- "version": "0.2.0",
5
+ "version": "0.3.0",
6
6
  "hosts": ["www.yfsp.tv", "rankv21.tripdata.app", "m10.yfsp.tv"],
7
7
  "search": {
8
8
  "inputs": ["q"],
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "apiVersion": 1,
3
- "id": "builtin:bahamut",
3
+ "id": "bahamut",
4
4
  "name": "Bahamut",
5
- "version": "0.2.0",
5
+ "version": "0.3.0",
6
6
  "hosts": ["api.gamer.com.tw"],
7
7
  "search": {
8
8
  "inputs": ["q"],
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "apiVersion": 1,
3
- "id": "builtin:bilibili",
3
+ "id": "bilibili",
4
4
  "name": "Bilibili",
5
- "version": "0.2.0",
5
+ "version": "0.3.0",
6
6
  "hosts": ["api.bilibili.com"],
7
7
  "urlMatch": [
8
8
  {
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "apiVersion": 1,
3
- "id": "builtin:dandanplay",
3
+ "id": "dandanplay",
4
4
  "name": "DanDanPlay",
5
- "version": "0.2.0",
5
+ "version": "0.3.0",
6
6
  "hosts": ["*"],
7
7
  "configSchema": {
8
8
  "type": "object",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "apiVersion": 1,
3
- "id": "builtin:hanjutv",
3
+ "id": "hanjutv",
4
4
  "name": "Hanjutv",
5
- "version": "0.2.0",
5
+ "version": "0.3.0",
6
6
  "hosts": ["hxqapi.hiyun.tv", "hxqapi.zmdcq.com"],
7
7
  "search": {
8
8
  "inputs": ["q"],
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "apiVersion": 1,
3
- "id": "builtin:iqiyi",
3
+ "id": "iqiyi",
4
4
  "name": "iQIYI",
5
- "version": "0.2.0",
5
+ "version": "0.3.0",
6
6
  "hosts": [
7
7
  "mesh.if.iqiyi.com",
8
8
  "pcw-api.iq.com",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "apiVersion": 1,
3
- "id": "builtin:maiduidui",
3
+ "id": "maiduidui",
4
4
  "name": "Maiduidui",
5
- "version": "0.2.0",
5
+ "version": "0.3.0",
6
6
  "hosts": ["mob.mddcloud.com.cn"],
7
7
  "search": {
8
8
  "inputs": ["q"],
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "apiVersion": 1,
3
- "id": "builtin:mango",
3
+ "id": "mango",
4
4
  "name": "Mango TV",
5
- "version": "0.2.0",
5
+ "version": "0.3.0",
6
6
  "hosts": [
7
7
  "mobileso.bz.mgtv.com",
8
8
  "pcweb.api.mgtv.com",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "apiVersion": 1,
3
- "id": "builtin:migu",
3
+ "id": "migu",
4
4
  "name": "Migu Video",
5
- "version": "0.2.0",
5
+ "version": "0.3.0",
6
6
  "hosts": ["jadeite.migu.cn", "v3-sc.miguvideo.com", "webapi.miguvideo.com"],
7
7
  "search": {
8
8
  "inputs": ["q"],
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "apiVersion": 1,
3
- "id": "builtin:renren",
3
+ "id": "renren",
4
4
  "name": "Renren",
5
- "version": "0.2.0",
5
+ "version": "0.3.0",
6
6
  "hosts": ["api.gorafie.com", "static-dm.qwdjapp.com"],
7
7
  "search": {
8
8
  "inputs": ["q"],
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "apiVersion": 1,
3
- "id": "builtin:sohu",
3
+ "id": "sohu",
4
4
  "name": "Sohu",
5
- "version": "0.2.0",
5
+ "version": "0.3.0",
6
6
  "hosts": ["m.so.tv.sohu.com", "api.tv.sohu.com", "api.danmu.tv.sohu.com"],
7
7
  "search": {
8
8
  "inputs": ["q"],
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "apiVersion": 1,
3
- "id": "builtin:tencent",
3
+ "id": "tencent",
4
4
  "name": "Tencent Video",
5
- "version": "0.2.0",
5
+ "version": "0.3.0",
6
6
  "hosts": ["pbaccess.video.qq.com", "dm.video.qq.com"],
7
7
  "urlMatch": [
8
8
  {
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "apiVersion": 1,
3
- "id": "builtin:youku",
3
+ "id": "youku",
4
4
  "name": "Youku",
5
- "version": "0.2.0",
5
+ "version": "0.3.0",
6
6
  "hosts": [
7
7
  "search.youku.com",
8
8
  "openapi.youku.com",
@@ -29,16 +29,21 @@
29
29
  "inputs": ["showId"],
30
30
  "steps": [
31
31
  {
32
- "type": "http",
33
- "id": "page",
32
+ "type": "forEach",
33
+ "id": "pages",
34
+ "in": "$range(1, 51)",
35
+ "as": "page",
34
36
  "request": {
35
37
  "method": "GET",
36
38
  "url": "'https://openapi.youku.com/v2/shows/videos.json'",
37
- "query": "{ 'client_id': '53e6cc67237fc59a', 'package': 'com.huawei.hwvplayer.youku', 'ext': 'show', 'show_id': showId, 'page': '1', 'count': '100' }"
38
- }
39
+ "query": "{ 'client_id': '53e6cc67237fc59a', 'package': 'com.huawei.hwvplayer.youku', 'ext': 'show', 'show_id': showId, 'page': $string(page), 'count': '100' }"
40
+ },
41
+ "collect": "videos",
42
+ "breakOn": "$count($) < 100",
43
+ "throttleMs": 100
39
44
  }
40
45
  ],
41
- "output": "[page.videos.{ 'providerIds': { 'vid': id }, 'indexedId': id, 'title': title, 'episodeNumber': $number(stage), 'imageUrl': thumbnail_v2 }]"
46
+ "output": "[pages.{ 'providerIds': { 'vid': id }, 'indexedId': id, 'title': title, 'episodeNumber': $number(stage), 'imageUrl': thumbnail_v2 }]"
42
47
  },
43
48
  "danmaku": {
44
49
  "inputs": ["vid"],