@ikenxuan/amagi 5.6.2 → 5.6.3
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 +228 -104
- package/dist/default/cjs/index.cjs +14 -9
- package/dist/default/esm/index.mjs +13 -8
- package/dist/default/index.d.ts +2 -7
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,156 +1,280 @@
|
|
|
1
|
-
# amagi
|
|
1
|
+
# @ikenxuan/amagi
|
|
2
2
|
|
|
3
|
-
抖音、
|
|
3
|
+
> 抖音、B站、快手、小红书 Web 端相关数据接口的 Node.js 封装与服务。支持“直接调用(SDK)”与“本地 HTTP 服务”两种使用方式,并内置严格的参数校验与统一响应格式。
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
文档站点(API 文档):https://amagi.apifox.cn
|
|
6
|
+
开发者文档(Typedoc):https://ikenxuan.github.io/amagi
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
本项目最初的代码从 [kkkkkk-10086](https://github.com/ikenxuan/kkkkkk-10086) 解耦。主要负责相关数据接口的封装。
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
[@ikenxuan/amagi](https://www.npmjs.com/package/@ikenxuan/amagi) 将作为一个独立的上游模块,提供给下游 [karin-plugin-kkk](https://github.com/ikenxuan/karin-plugin-kkk) 和 [kkkkkk-10086](https://github.com/ikenxuan/kkkkkk-10086) 进行视频解析相关业务使用。这两个项目已完成了几乎所有由 ikenxuan 安排的功能和任务,所以它们如果没有什么新的业务需求,本项目大概再也不会封装新的任何接口。
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
当然,如果你的下游有新的业务需求,欢迎提 issue 或 pr。(作者本人很菜,尤其不会逆向工程,所以 issue 不一定能解决)
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
## 特性
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
- 多平台支持:抖音、B站、快手、小红书的主流数据接口
|
|
17
|
+
- 两种使用姿势:
|
|
18
|
+
- 直接调用:通过 SDK 获取数据,支持绑定 Cookie
|
|
19
|
+
- 本地服务:一键启动 Express 服务,REST 风格路由
|
|
20
|
+
- 参数校验:基于 Zod,按方法类型校验必填与可选参数
|
|
21
|
+
- 统一响应:约定化 `ApiResponse` 返回结构,含 `success`/`code`/`message`/`data`
|
|
22
|
+
- 类型模式:`strict` 与 `loose` 可选,开发友好与容错可控
|
|
23
|
+
- 工具集齐全:签名算法、URL 拼接器、AV/BV 转换等常用工具
|
|
24
|
+
- 双模块输出:同时支持 ESM 与 CJS 引入
|
|
16
25
|
|
|
17
|
-
##
|
|
26
|
+
## 安装
|
|
27
|
+
|
|
28
|
+
使用 npm:
|
|
18
29
|
|
|
19
30
|
```bash
|
|
20
|
-
|
|
31
|
+
npm i @ikenxuan/amagi
|
|
21
32
|
```
|
|
22
33
|
|
|
34
|
+
或使用 pnpm / yarn:
|
|
23
35
|
|
|
24
|
-
|
|
25
|
-
|
|
36
|
+
```bash
|
|
37
|
+
pnpm add @ikenxuan/amagi
|
|
38
|
+
```
|
|
26
39
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
> * 第三个参数是用户的 cookies。
|
|
31
|
-
> * 得益于函数重载,参数二和参数三可以互换位置
|
|
40
|
+
```bash
|
|
41
|
+
yarn add @ikenxuan/amagi
|
|
42
|
+
```
|
|
32
43
|
|
|
33
|
-
|
|
34
|
-
|
|
44
|
+
## 快速上手
|
|
45
|
+
|
|
46
|
+
### 1) 直接调用(推荐)
|
|
47
|
+
|
|
48
|
+
SDK 方式最灵活,适合在 Node.js 程序里按需拉取数据。
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import amagi from '@ikenxuan/amagi'
|
|
52
|
+
|
|
53
|
+
// 绑定各平台 Cookie(可选)与请求配置(可选)
|
|
54
|
+
const client = amagi({
|
|
55
|
+
cookies: {
|
|
56
|
+
bilibili: 'SESSDATA=xxx; bili_jct=yyy; ...',
|
|
57
|
+
douyin: 'ttwid=...; ...',
|
|
58
|
+
kuaishou: 'did=...; ...',
|
|
59
|
+
xiaohongshu: 'a1=...; ...',
|
|
60
|
+
},
|
|
61
|
+
request: {
|
|
62
|
+
// 例如自定义 headers、代理等
|
|
63
|
+
headers: { 'User-Agent': 'Mozilla/5.0 ...' }
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
// B站:获取单个视频信息(bvid)
|
|
68
|
+
const video = await client.getBilibiliData('单个视频作品数据', {
|
|
69
|
+
bvid: 'BV1xx411c7mD',
|
|
70
|
+
typeMode: 'strict' // 严格类型(可选,默认 loose)
|
|
71
|
+
})
|
|
72
|
+
if (video.success) {
|
|
73
|
+
console.log(video.data)
|
|
74
|
+
}
|
|
35
75
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
76
|
+
// 抖音:获取评论数据(aweme_id)
|
|
77
|
+
const comments = await client.getDouyinData('评论数据', {
|
|
78
|
+
aweme_id: '1234567890123456789',
|
|
79
|
+
number: 20
|
|
80
|
+
})
|
|
81
|
+
console.log(comments)
|
|
40
82
|
|
|
41
|
-
//
|
|
42
|
-
const
|
|
43
|
-
const amagi = new Client({...options})
|
|
83
|
+
// 也可使用绑定 Cookie 的 API 对象:不再传 cookie
|
|
84
|
+
const { bilibili, douyin, kuaishou, xiaohongshu } = client
|
|
44
85
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
{ ...opt }
|
|
51
|
-
)
|
|
86
|
+
const bInfo = await bilibili.api.getVideoInfo({ bvid: 'BV1xx411c7mD' })
|
|
87
|
+
const dCom = await douyin.api.getComments({ aweme_id: '1234567890123456789', number: 10 })
|
|
88
|
+
const kInfo = await kuaishou.api.getWorkInfo({ photoId: '3xqxxxxxx' })
|
|
89
|
+
const xNote = await xiaohongshu.api.getNote({ note_id: '64xxxxxxxx', xsec_token: 'xsec_xxx' })
|
|
90
|
+
```
|
|
52
91
|
|
|
53
|
-
|
|
54
|
-
const videoData = await amagi.getBilibiliData(
|
|
55
|
-
'单个视频作品数据',
|
|
56
|
-
{ ...opt }
|
|
57
|
-
)
|
|
92
|
+
也可以不用实例,直接调用静态方法(手动给 cookie):
|
|
58
93
|
|
|
59
|
-
|
|
60
|
-
|
|
94
|
+
```ts
|
|
95
|
+
import amagi from '@ikenxuan/amagi'
|
|
61
96
|
|
|
62
|
-
|
|
97
|
+
const res = await amagi.getBilibiliData('单个视频作品数据', { bvid: 'BV1xx411c7mD' }, 'SESSDATA=...')
|
|
98
|
+
console.log(res)
|
|
63
99
|
```
|
|
64
100
|
|
|
65
|
-
|
|
101
|
+
### 2) 启动本地 HTTP 服务
|
|
102
|
+
|
|
103
|
+
一行代码启动 Express 服务,路由自动注册。
|
|
66
104
|
|
|
67
|
-
|
|
105
|
+
```ts
|
|
106
|
+
import amagi from '@ikenxuan/amagi'
|
|
68
107
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
108
|
+
// 端口默认 4567,可自定义
|
|
109
|
+
amagi({
|
|
110
|
+
cookies: {
|
|
111
|
+
bilibili: 'SESSDATA=xxx; bili_jct=yyy; ...',
|
|
112
|
+
douyin: 'ttwid=...; ...',
|
|
113
|
+
kuaishou: 'did=...; ...',
|
|
114
|
+
xiaohongshu: 'a1=...; ...',
|
|
115
|
+
}
|
|
116
|
+
}).startServer(4567)
|
|
117
|
+
|
|
118
|
+
// 打开 http://localhost:4567/ 或 http://localhost:4567/docs
|
|
119
|
+
// 文档自动重定向至 https://amagi.apifox.cn
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## 服务端路由(HTTP API)
|
|
123
|
+
|
|
124
|
+
- 路由与参数请直接参考 API 文档:https://amagi.apifox.cn
|
|
125
|
+
- 本地启动示例:`amagi({ cookies: {...} }).startServer(4567)`
|
|
126
|
+
|
|
127
|
+
启动服务后,默认挂载在 `/api/<platform>` 下。以下列出主要路由与典型参数(查询串/JSON 皆可):
|
|
128
|
+
|
|
129
|
+
- Douyin `/api/douyin`
|
|
130
|
+
- `GET /fetch_one_work` 聚合解析(`aweme_id`)
|
|
131
|
+
- `GET /fetch_work_comments` 评论数据(`aweme_id`, `number?`, `cursor?`)
|
|
132
|
+
- `GET /fetch_user_info` 用户主页数据(`sec_uid`)
|
|
133
|
+
- `GET /fetch_user_post_videos` 用户主页视频列表(`sec_uid`)
|
|
134
|
+
- `GET /fetch_search_info` 搜索/热点词(`query`, `number?`, `search_id?`)
|
|
135
|
+
- `GET /fetch_suggest_words` 热点词数据(`query`, `number?`)
|
|
136
|
+
- `GET /fetch_music_work` 音乐数据(`music_id`)
|
|
137
|
+
- `GET /fetch_emoji_list` Emoji 列表
|
|
138
|
+
- `GET /fetch_emoji_pro_list` 动态表情列表
|
|
139
|
+
- `GET /fetch_user_live_videos` 直播间信息(`sec_uid`)
|
|
140
|
+
- `GET /fetch_video_comment_replies` 指定评论回复(`aweme_id`, `comment_id`, `number?`, `cursor?`)
|
|
141
|
+
- `GET /fetch_work_danmaku` 弹幕数据(`aweme_id`, `duration`, `start_time?`, `end_time?`)
|
|
142
|
+
|
|
143
|
+
- Bilibili `/api/bilibili`
|
|
144
|
+
- `GET /fetch_one_video` 单个视频作品数据(`bvid`)
|
|
145
|
+
- `GET /fetch_video_playurl` 单个视频下载信息(`avid`, `cid`)
|
|
146
|
+
- `GET /fetch_work_comments` 评论数据(`oid`, `type`, `pn?`, `number?`)
|
|
147
|
+
- `GET /fetch_user_profile` 用户主页数据(`host_mid`)
|
|
148
|
+
- `GET /fetch_user_dynamic` 用户主页动态列表(`host_mid`)
|
|
149
|
+
- `GET /fetch_emoji_list` Emoji 列表
|
|
150
|
+
- `GET /fetch_bangumi_video_info` 番剧基本信息(二选一:`ep_id` 或 `season_id`)
|
|
151
|
+
- `GET /fetch_bangumi_video_playurl` 番剧下载信息(`cid`, `ep_id`)
|
|
152
|
+
- `GET /fetch_dynamic_info` 动态详情(`dynamic_id`)
|
|
153
|
+
- `GET /fetch_dynamic_card` 动态卡片(`dynamic_id`)
|
|
154
|
+
- `GET /fetch_live_room_detail` 直播间信息(`room_id`)
|
|
155
|
+
- `GET /fetch_liveroom_def` 直播间初始化信息(`room_id`)
|
|
156
|
+
- `GET /login_basic_info` 登录基本信息
|
|
157
|
+
- `GET /new_login_qrcode` 申请二维码
|
|
158
|
+
- `GET /check_qrcode` 二维码状态(`qrcode_key`)
|
|
159
|
+
- `GET /fetch_user_full_view` UP 主总播放量(`host_mid`)
|
|
160
|
+
- `GET /av_to_bv` AV 转 BV(`avid`)
|
|
161
|
+
- `GET /bv_to_av` BV 转 AV(`bvid`)
|
|
162
|
+
|
|
163
|
+
- Kuaishou `/api/kuaishou`
|
|
164
|
+
- `GET /fetch_one_work` 单个视频作品数据(`photoId`)
|
|
165
|
+
- `GET /fetch_work_comments` 评论数据(`photoId`)
|
|
166
|
+
- `GET /fetch_emoji_list` Emoji 列表
|
|
167
|
+
|
|
168
|
+
- Xiaohongshu `/api/xiaohongshu`
|
|
169
|
+
- `GET /fetch_home_feed` 首页推荐(`cursor_score?`, `num?`, `refresh_type?`, `note_index?`, `category?`, `search_key?`)
|
|
170
|
+
- `GET /fetch_one_note` 单个笔记数据(`note_id`, `xsec_token`)
|
|
171
|
+
- `GET /fetch_note_comments` 评论数据(`note_id`, `xsec_token`, `cursor?`)
|
|
172
|
+
- `GET /fetch_user_profile` 用户数据(`user_id`)
|
|
173
|
+
- `GET /fetch_user_notes` 用户笔记(`user_id`, `cursor?`, `num?`)
|
|
174
|
+
- `GET /fetch_emoji_list` 表情列表
|
|
175
|
+
- `GET /fetch_search_notes` 搜索笔记(`keyword`, `page?`, `page_size?`, `sort?`, `note_type?`)
|
|
176
|
+
|
|
177
|
+
说明:
|
|
178
|
+
- 路由内部已固定 `methodType`,你只需传具体业务参数即可。
|
|
179
|
+
- 抖音的 `/fetch_one_work` 同路径注册了多个方法(视频 / 图集 / 合辑 / 聚合),推荐使用“聚合解析”。
|
|
180
|
+
|
|
181
|
+
## 统一响应结构
|
|
182
|
+
|
|
183
|
+
所有 API 返回统一结构:
|
|
184
|
+
|
|
185
|
+
```json
|
|
186
|
+
{
|
|
187
|
+
"success": true,
|
|
188
|
+
"code": 200,
|
|
189
|
+
"message": "获取成功",
|
|
190
|
+
"data": { ... },
|
|
191
|
+
"requestPath": "/api/bilibili/fetch_one_video?bvid=BV1xx411c7mD"
|
|
75
192
|
}
|
|
76
193
|
```
|
|
77
194
|
|
|
78
|
-
|
|
195
|
+
错误时:
|
|
79
196
|
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
197
|
+
```json
|
|
198
|
+
{
|
|
199
|
+
"success": false,
|
|
200
|
+
"code": 400,
|
|
201
|
+
"message": "参数错误",
|
|
202
|
+
"error": { "name": "ZodError", "details": [ ... ] },
|
|
203
|
+
"requestPath": "/api/douyin/fetch_work_comments?aweme_id=..."
|
|
87
204
|
}
|
|
88
205
|
```
|
|
89
206
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
> 接口返回数据的类型类型为自动生成,仅供参考,不保证 100% 准确。
|
|
207
|
+
## 类型模式与参数校验
|
|
93
208
|
|
|
94
|
-
|
|
209
|
+
- 类型模式 `typeMode`
|
|
210
|
+
- `strict`:返回严格类型(基于已知响应结构,可能缺少平台新增字段)
|
|
211
|
+
- `loose`:返回 `any`,容错更强(默认)
|
|
212
|
+
- 使用位置:仅 SDK 直接调用可指定 `typeMode`,HTTP 路由默认等价于 `loose`
|
|
95
213
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
'搜索数据',
|
|
100
|
-
{ keyword: '测试', typeMode: 'strict' }
|
|
101
|
-
)
|
|
214
|
+
- 参数校验(Zod)
|
|
215
|
+
- 每个方法类型对应一套完整参数校验规则
|
|
216
|
+
- 请求入参不合法时将返回错误响应(含详细错误信息)
|
|
102
217
|
|
|
103
|
-
|
|
104
|
-
const looseResult = await client.getDouyinData(
|
|
105
|
-
'搜索数据',
|
|
106
|
-
{ keyword: '测试', typeMode: 'loose' }
|
|
107
|
-
)
|
|
108
|
-
```
|
|
218
|
+
## 工具集与常用能力
|
|
109
219
|
|
|
110
|
-
|
|
220
|
+
所有平台工具集从包顶层导出,可直接使用:
|
|
111
221
|
|
|
112
|
-
|
|
222
|
+
```ts
|
|
223
|
+
import { douyinUtils, bilibiliUtils, kuaishouUtils } from '@ikenxuan/amagi'
|
|
224
|
+
import { xiaohongshuUtils } from '@ikenxuan/amagi'
|
|
225
|
+
```
|
|
113
226
|
|
|
114
|
-
|
|
115
|
-
|
|
227
|
+
- 抖音 `douyinUtils`
|
|
228
|
+
- `sign`: `douyinSign.Mstoken(length)`, `douyinSign.AB(url, ua)`, `douyinSign.XB(url, ua)`, `douyinSign.VerifyFpManager()`
|
|
229
|
+
- `douyinApiUrls`: 仅负责拼接基础 URL,需要再次以该 URL 生成反爬参数
|
|
230
|
+
- `api`: 原始 API 调用(需传 cookie);若使用 `client.douyin.api` 则已绑定 cookie
|
|
116
231
|
|
|
117
|
-
|
|
232
|
+
- B站 `bilibiliUtils`
|
|
233
|
+
- `sign`: `wbi_sign(baseUrl, cookie)`, `av2bv(aid: number)`, `bv2av(bvid: string)`
|
|
234
|
+
- `bilibiliApiUrls`: 仅拼接基础 URL
|
|
235
|
+
- `api`: 原始 API 调用(同上)
|
|
118
236
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
// const app = amagi.startClient(8080)
|
|
123
|
-
```
|
|
237
|
+
- 快手 `kuaishouUtils`
|
|
238
|
+
- `kuaishouApiUrls`: 仅拼接基础 URL
|
|
239
|
+
- `api`: 原始 API 调用(同上)
|
|
124
240
|
|
|
125
|
-
|
|
241
|
+
- 小红书 `xiaohongshuUtils`
|
|
242
|
+
- `sign`: `xiaohongshuSign.generateXSGet(path, a1Cookie, clientType?, params?)`,`generateXSPost(...)`,`generateXS(url, body, ua?, method?, a1Cookie?)`,`generateXSCommon(length?)`,`generateXT()`,`generateXB3Traceid()`,`extractA1FromCookie(cookieString)`,`getSearchId()`
|
|
243
|
+
- `xiaohongshuApiUrls`: 拼接 URL + POST 参数
|
|
244
|
+
- `api`: 原始 API 调用(同上)
|
|
126
245
|
|
|
127
|
-
|
|
128
|
-
- bilibili API: `http://localhost:4567/api/bilibili/...` 📺
|
|
246
|
+
### AV/BV 转换示例
|
|
129
247
|
|
|
130
|
-
|
|
248
|
+
```ts
|
|
249
|
+
import { bilibiliUtils } from '@ikenxuan/amagi'
|
|
131
250
|
|
|
132
|
-
|
|
251
|
+
const bv = bilibiliUtils.sign.av2bv(170001)
|
|
252
|
+
const av = bilibiliUtils.sign.bv2av('BV1xx411c7mD')
|
|
253
|
+
```
|
|
133
254
|
|
|
134
|
-
|
|
255
|
+
## 运行建议与注意事项
|
|
135
256
|
|
|
136
|
-
|
|
257
|
+
- Cookie 与 UA:部分接口需要有效 Cookie 与合理的 `User-Agent`;请遵循平台使用政策
|
|
258
|
+
- 频率与限流:避免高频调用,合理设置请求重试与指数退避
|
|
259
|
+
- ESM/CJS:包 `type` 为 `module`,同时提供 ESM/CJS 两类入口
|
|
260
|
+
- ESM:`import amagi from '@ikenxuan/amagi'`
|
|
261
|
+
- CJS:`const amagi = require('@ikenxuan/amagi')`
|
|
137
262
|
|
|
138
|
-
|
|
139
|
-
- 🛡️ 本项目的作者不承担任何责任,包括但不限于因使用本项目而导致的任何损失或损害。
|
|
140
|
-
- 📚 本项目仅供学习和研究使用,不得用于任何商业目的。
|
|
141
|
-
- 🔒 本项目的作者不对因使用本项目而产生的任何后果负责。
|
|
142
|
-
- 📝 本项目的使用者在下载、安装、运行或使用本工具时,即表示已阅读并同意本免责声明。如有异议,请立即停止使用本工具,并删除所有相关文件。
|
|
143
|
-
- 🔄 本项目的作者保留随时修改、更新、删除或终止本工具的权利,无需事先通知或承担任何义务。
|
|
263
|
+
## 许可证
|
|
144
264
|
|
|
145
|
-
|
|
265
|
+
GPL-3.0-only
|
|
146
266
|
|
|
147
|
-
|
|
267
|
+
## 变更日志与反馈
|
|
148
268
|
|
|
149
|
-
|
|
269
|
+
- 变更日志:请查看 GitHub Releases 或提交记录
|
|
270
|
+
- 问题反馈:GitHub Issues https://github.com/ikenxuan/amagi/issues
|
|
271
|
+
- 文档站点:https://amagi.apifox.cn
|
|
150
272
|
|
|
151
|
-
|
|
273
|
+
## 鸣谢
|
|
274
|
+
请求签名部分参考了以下项目的实现:
|
|
152
275
|
|
|
153
|
-
-
|
|
154
|
-
-
|
|
155
|
-
-
|
|
156
|
-
-
|
|
276
|
+
- [SocialSisterYi/bilibili-API-collect](https://github.com/SocialSisterYi/bilibili-API-collect)
|
|
277
|
+
- [NearHuiwen/TiktokDouyinCrawler](https://github.com/NearHuiwen/TiktokDouyinCrawler)
|
|
278
|
+
- [Evil0ctal/Douyin_TikTok_Download_API](https://github.com/Evil0ctal/Douyin_TikTok_Download_API)
|
|
279
|
+
- [Johnserf-Seed/f2](https://github.com/Johnserf-Seed/f2)
|
|
280
|
+
- [ikenxuan/xhshow-ts](https://github.com/ikenxuan/xhshow-ts)
|
|
@@ -29,7 +29,7 @@ var express__default = /*#__PURE__*/_interopDefault(express);
|
|
|
29
29
|
* GPL-3.0 Licensed
|
|
30
30
|
*/
|
|
31
31
|
|
|
32
|
-
// node_modules/.pnpm/tsup@8.5.0_@swc+core@1.13.20_tsx@4.20.6_typescript@5.
|
|
32
|
+
// node_modules/.pnpm/tsup@8.5.0_@swc+core@1.13.20_tsx@4.20.6_typescript@5.8.3_yaml@2.8.1/node_modules/tsup/assets/cjs_shims.js
|
|
33
33
|
var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.src || new URL("main.js", document.baseURI).href;
|
|
34
34
|
var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
35
35
|
var getPackageLogsPath = () => {
|
|
@@ -183,11 +183,7 @@ var fetchData = async (config) => {
|
|
|
183
183
|
if (cleanedConfig.headers && cleanedConfig.headers["User-Agent"]) {
|
|
184
184
|
cleanedConfig.headers["User-Agent"] = cleanUserAgent(cleanedConfig.headers["User-Agent"]);
|
|
185
185
|
}
|
|
186
|
-
const response = await axios__default.default(cleanedConfig);
|
|
187
|
-
if (response.status === 429) {
|
|
188
|
-
logger.error("HTTP \u54CD\u5E94\u72B6\u6001\u7801: 429");
|
|
189
|
-
throw new Error("ratelimit triggered, \u89E6\u53D1\u901F\u7387\u9650\u5236\uFF01");
|
|
190
|
-
}
|
|
186
|
+
const response = await axios__default.default({ ...cleanedConfig, validateStatus: () => true });
|
|
191
187
|
return response.data;
|
|
192
188
|
} catch (error) {
|
|
193
189
|
if (error instanceof axios.AxiosError) {
|
|
@@ -197,13 +193,19 @@ var fetchData = async (config) => {
|
|
|
197
193
|
throw error;
|
|
198
194
|
}
|
|
199
195
|
};
|
|
196
|
+
var normalizeHeaders = (headers) => {
|
|
197
|
+
if (headers && typeof headers.toJSON === "function") {
|
|
198
|
+
return headers.toJSON();
|
|
199
|
+
}
|
|
200
|
+
return headers || {};
|
|
201
|
+
};
|
|
200
202
|
var fetchResponse = async (config) => {
|
|
201
203
|
try {
|
|
202
204
|
const cleanedConfig = { ...config };
|
|
203
205
|
if (cleanedConfig.headers && cleanedConfig.headers["User-Agent"]) {
|
|
204
206
|
cleanedConfig.headers["User-Agent"] = cleanUserAgent(cleanedConfig.headers["User-Agent"]);
|
|
205
207
|
}
|
|
206
|
-
return await axios__default.default(cleanedConfig);
|
|
208
|
+
return await axios__default.default({ ...cleanedConfig, validateStatus: () => true });
|
|
207
209
|
} catch (error) {
|
|
208
210
|
if (error instanceof axios.AxiosError) {
|
|
209
211
|
throw error;
|
|
@@ -215,12 +217,15 @@ var getHeadersAndData = async (config) => {
|
|
|
215
217
|
try {
|
|
216
218
|
const response = await fetchResponse(config);
|
|
217
219
|
return {
|
|
218
|
-
headers: response.headers,
|
|
220
|
+
headers: normalizeHeaders(response.headers),
|
|
219
221
|
data: response.data
|
|
220
222
|
};
|
|
221
223
|
} catch (error) {
|
|
222
224
|
logger.error("\u83B7\u53D6\u54CD\u5E94\u5934\u548C\u6570\u636E\u5931\u8D25:", error);
|
|
223
|
-
return {
|
|
225
|
+
return {
|
|
226
|
+
headers: {},
|
|
227
|
+
data: {}
|
|
228
|
+
};
|
|
224
229
|
}
|
|
225
230
|
};
|
|
226
231
|
|
|
@@ -166,11 +166,7 @@ var fetchData = async (config) => {
|
|
|
166
166
|
if (cleanedConfig.headers && cleanedConfig.headers["User-Agent"]) {
|
|
167
167
|
cleanedConfig.headers["User-Agent"] = cleanUserAgent(cleanedConfig.headers["User-Agent"]);
|
|
168
168
|
}
|
|
169
|
-
const response = await axios(cleanedConfig);
|
|
170
|
-
if (response.status === 429) {
|
|
171
|
-
logger.error("HTTP \u54CD\u5E94\u72B6\u6001\u7801: 429");
|
|
172
|
-
throw new Error("ratelimit triggered, \u89E6\u53D1\u901F\u7387\u9650\u5236\uFF01");
|
|
173
|
-
}
|
|
169
|
+
const response = await axios({ ...cleanedConfig, validateStatus: () => true });
|
|
174
170
|
return response.data;
|
|
175
171
|
} catch (error) {
|
|
176
172
|
if (error instanceof AxiosError) {
|
|
@@ -180,13 +176,19 @@ var fetchData = async (config) => {
|
|
|
180
176
|
throw error;
|
|
181
177
|
}
|
|
182
178
|
};
|
|
179
|
+
var normalizeHeaders = (headers) => {
|
|
180
|
+
if (headers && typeof headers.toJSON === "function") {
|
|
181
|
+
return headers.toJSON();
|
|
182
|
+
}
|
|
183
|
+
return headers || {};
|
|
184
|
+
};
|
|
183
185
|
var fetchResponse = async (config) => {
|
|
184
186
|
try {
|
|
185
187
|
const cleanedConfig = { ...config };
|
|
186
188
|
if (cleanedConfig.headers && cleanedConfig.headers["User-Agent"]) {
|
|
187
189
|
cleanedConfig.headers["User-Agent"] = cleanUserAgent(cleanedConfig.headers["User-Agent"]);
|
|
188
190
|
}
|
|
189
|
-
return await axios(cleanedConfig);
|
|
191
|
+
return await axios({ ...cleanedConfig, validateStatus: () => true });
|
|
190
192
|
} catch (error) {
|
|
191
193
|
if (error instanceof AxiosError) {
|
|
192
194
|
throw error;
|
|
@@ -198,12 +200,15 @@ var getHeadersAndData = async (config) => {
|
|
|
198
200
|
try {
|
|
199
201
|
const response = await fetchResponse(config);
|
|
200
202
|
return {
|
|
201
|
-
headers: response.headers,
|
|
203
|
+
headers: normalizeHeaders(response.headers),
|
|
202
204
|
data: response.data
|
|
203
205
|
};
|
|
204
206
|
} catch (error) {
|
|
205
207
|
logger.error("\u83B7\u53D6\u54CD\u5E94\u5934\u548C\u6570\u636E\u5931\u8D25:", error);
|
|
206
|
-
return {
|
|
208
|
+
return {
|
|
209
|
+
headers: {},
|
|
210
|
+
data: {}
|
|
211
|
+
};
|
|
207
212
|
}
|
|
208
213
|
};
|
|
209
214
|
|
package/dist/default/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AxiosResponse, AxiosRequestConfig } from 'axios';
|
|
1
|
+
import { AxiosResponse, AxiosRequestConfig, RawAxiosResponseHeaders } from 'axios';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import * as express from 'express';
|
|
4
4
|
import express__default, { Router, RequestHandler } from 'express';
|
|
@@ -20565,11 +20565,6 @@ declare const logMiddleware: (pathsToLog?: string[]) => RequestHandler;
|
|
|
20565
20565
|
* @returns 响应数据
|
|
20566
20566
|
*/
|
|
20567
20567
|
declare const fetchData: <T = any>(config: AxiosRequestConfig) => Promise<T>;
|
|
20568
|
-
/**
|
|
20569
|
-
* 执行网络请求并返回完整响应对象
|
|
20570
|
-
* @param config - axios请求配置
|
|
20571
|
-
* @returns axios响应对象
|
|
20572
|
-
*/
|
|
20573
20568
|
declare const fetchResponse: <T = unknown>(config: AxiosRequestConfig) => Promise<AxiosResponse<T>>;
|
|
20574
20569
|
/**
|
|
20575
20570
|
* 获取响应头和数据
|
|
@@ -20577,7 +20572,7 @@ declare const fetchResponse: <T = unknown>(config: AxiosRequestConfig) => Promis
|
|
|
20577
20572
|
* @returns 包含headers和data的对象
|
|
20578
20573
|
*/
|
|
20579
20574
|
declare const getHeadersAndData: <T = any>(config: AxiosRequestConfig) => Promise<{
|
|
20580
|
-
headers:
|
|
20575
|
+
headers: RawAxiosResponseHeaders;
|
|
20581
20576
|
data: T;
|
|
20582
20577
|
}>;
|
|
20583
20578
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ikenxuan/amagi",
|
|
3
|
-
"version": "5.6.
|
|
3
|
+
"version": "5.6.3",
|
|
4
4
|
"description": "抖音、B站的 web 端相关数据接口基于 Node.js 的实现",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"douyin",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
},
|
|
72
72
|
"dependencies": {
|
|
73
73
|
"@ikenxuan/xhshow-ts": "^1.0.0",
|
|
74
|
-
"axios": "
|
|
74
|
+
"axios": "^1.12.2",
|
|
75
75
|
"chalk": "5.6.2",
|
|
76
76
|
"express": "npm:@karinjs/express@^1.0.3",
|
|
77
77
|
"log4js": "npm:@karinjs/log4js@1.1.4",
|