koishi-plugin-video-parser-all 0.2.6 → 0.2.8

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/lib/index.d.ts CHANGED
@@ -19,9 +19,6 @@ export interface Config {
19
19
  messageBufferDelay: number;
20
20
  retryTimes: number;
21
21
  retryInterval: number;
22
- apiMode: 'builtin' | 'custom';
23
- builtinApiUrl: string;
24
- customApiUrl: string;
25
22
  videoSendTimeout: number;
26
23
  autoClearCacheInterval: number;
27
24
  }
package/lib/index.js CHANGED
@@ -14,43 +14,26 @@ const promises_1 = require("stream/promises");
14
14
  const worker_threads_1 = require("worker_threads");
15
15
  exports.name = 'video-parser-all';
16
16
  exports.Config = koishi_1.Schema.object({
17
- enable: koishi_1.Schema.boolean().default(true).description('启用插件'),
18
- showWaitingTip: koishi_1.Schema.boolean().default(true).description('解析时显示等待提示'),
19
- waitingTipText: koishi_1.Schema.string().default('正在解析视频,请稍候...').description('等待提示文本'),
20
- sameLinkInterval: koishi_1.Schema.number().default(0).min(0).description('相同链接解析间隔(秒)'),
21
- imageParseFormat: koishi_1.Schema.string().role('textarea').default('${标题}\n${UP主}').description(`解析结果格式
22
- 支持变量:\${标题} \${UP主} \${简介} \${tab} \${~~~}`),
17
+ enable: koishi_1.Schema.boolean().default(true),
18
+ showWaitingTip: koishi_1.Schema.boolean().default(true),
19
+ waitingTipText: koishi_1.Schema.string().default('正在解析视频,请稍候...'),
20
+ sameLinkInterval: koishi_1.Schema.number().default(180).min(0),
21
+ imageParseFormat: koishi_1.Schema.string().role('textarea').default('${标题}\n${UP主}'),
23
22
  returnContent: koishi_1.Schema.object({
24
- showImageText: koishi_1.Schema.boolean().default(true).description('显示文本与封面'),
25
- showVideoUrl: koishi_1.Schema.boolean().default(false).description('显示无水印链接'),
26
- showVideoFile: koishi_1.Schema.boolean().default(true).description('发送视频消息'),
27
- }).description('返回内容设置'),
28
- maxDescLength: koishi_1.Schema.number().default(200).description('简介最大长度'),
29
- timeout: koishi_1.Schema.number().default(0).min(0).description('API请求超时(毫秒)'),
30
- ignoreSendError: koishi_1.Schema.boolean().default(true).description('忽略消息发送错误'),
31
- enableForward: koishi_1.Schema.boolean().default(false).description('启用合并转发(仅OneBot)'),
32
- downloadVideoBeforeSend: koishi_1.Schema.boolean().default(false).description('发送前先下载视频(仅OneBot)'),
33
- messageBufferDelay: koishi_1.Schema.number().default(0).min(0).description('消息缓冲延迟(秒)'),
34
- retryTimes: koishi_1.Schema.number().default(0).min(0).description('接口重试次数'),
35
- retryInterval: koishi_1.Schema.number().default(0).min(0).description('重试间隔(毫秒)'),
36
- apiMode: koishi_1.Schema.union([
37
- koishi_1.Schema.const('builtin').description('使用内置API'),
38
- koishi_1.Schema.const('custom').description('使用自定义API')
39
- ]).default('builtin').description('API使用模式'),
40
- builtinApiUrl: koishi_1.Schema.string().default('https://api.bugpk.com/api/short_videos').description(`内置API地址
41
- 解析失败可能原因:
42
- 1. 视频为私密/付费/下架/限制
43
- 2. 接口限流或维护
44
- 3. 网络波动请求超时
45
- 4. 平台链接格式更新
46
- 注意事项:
47
- 1. 勿频繁解析相同链接
48
- 2. 仅支持快手/B站/小红书/微博公开视频
49
- 3. 公共接口不保证100%成功
50
- 4. 失败可检查链接或稍后重试`),
51
- customApiUrl: koishi_1.Schema.string().default('').description('自定义API地址(优先使用)'),
52
- videoSendTimeout: koishi_1.Schema.number().default(0).min(0).description('视频发送超时(毫秒)'),
53
- autoClearCacheInterval: koishi_1.Schema.number().default(0).min(0).description('自动清理缓存间隔(分钟),0表示不自动清理'),
23
+ showImageText: koishi_1.Schema.boolean().default(true),
24
+ showVideoUrl: koishi_1.Schema.boolean().default(false),
25
+ showVideoFile: koishi_1.Schema.boolean().default(true),
26
+ }),
27
+ maxDescLength: koishi_1.Schema.number().default(200),
28
+ timeout: koishi_1.Schema.number().default(180000).min(0),
29
+ ignoreSendError: koishi_1.Schema.boolean().default(true),
30
+ enableForward: koishi_1.Schema.boolean().default(false),
31
+ downloadVideoBeforeSend: koishi_1.Schema.boolean().default(false),
32
+ messageBufferDelay: koishi_1.Schema.number().default(0).min(0),
33
+ retryTimes: koishi_1.Schema.number().default(0).min(0),
34
+ retryInterval: koishi_1.Schema.number().default(0).min(0),
35
+ videoSendTimeout: koishi_1.Schema.number().default(0).min(0),
36
+ autoClearCacheInterval: koishi_1.Schema.number().default(60).min(0),
54
37
  });
55
38
  if (!worker_threads_1.isMainThread) {
56
39
  const { url, filePath } = worker_threads_1.workerData;
@@ -83,7 +66,39 @@ const PLATFORM_KEYWORDS = {
83
66
  bilibili: ['bilibili', 'b23', 'B站', 'www.bilibili.com', 'm.bilibili.com'],
84
67
  kuaishou: ['kuaishou', '快手', 'v.kuishou.com', 'www.kuishou.com', 'kwimgs.com'],
85
68
  xiaohongshu: ['xiaohongshu', '小红书', 'xhslink.com', 'xiaohongshu.com', 'xhscdn.com'],
86
- weibo: ['weibo', '微博', 'weibo.com', 'video.weibo.com', 'svproxy.168299.xyz']
69
+ weibo: ['weibo', '微博', 'weibo.com', 'video.weibo.com', 'svproxy.168299.xyz'],
70
+ toutiao: ['toutiao', '今日头条', 'm.toutiao.com', 'toutiao.com', 'ixigua.com'],
71
+ pipigx: ['pipigx', '皮皮搞笑', 'h5.pipigx.com', 'ippzone.com'],
72
+ pipixia: ['pipixia', '皮皮虾', 'h5.pipix.com', 'ppxsign.byteimg.com'],
73
+ douyin: ['douyin', '抖音', 'v.douyin.com', 'douyinpic.com', 'douyinvod.com']
74
+ };
75
+ const API_CONFIG = {
76
+ universal: 'https://api.bugpk.com/api/short_videos',
77
+ platform: {
78
+ bilibili: ['https://api.bugpk.com/api/bilibili'],
79
+ kuaishou: [
80
+ 'https://api.bugpk.com/api/ksjx',
81
+ 'https://api.bugpk.com/api/kuaishou',
82
+ 'https://api.bugpk.com/api/ksimg'
83
+ ],
84
+ xiaohongshu: [
85
+ 'https://api.bugpk.com/api/xhsjx',
86
+ 'https://api.bugpk.com/api/xhsimg',
87
+ 'https://api.bugpk.com/api/xhslive'
88
+ ],
89
+ weibo: [
90
+ 'https://api.bugpk.com/api/weibo',
91
+ 'https://api.bugpk.com/api/weibo_v'
92
+ ],
93
+ toutiao: ['https://api.bugpk.com/api/toutiao'],
94
+ pipigx: ['https://api.bugpk.com/api/pipigx'],
95
+ pipixia: ['https://api.bugpk.com/api/pipixia'],
96
+ douyin: [
97
+ 'https://api.bugpk.com/api/douyin',
98
+ 'https://api.bugpk.com/api/dyjx',
99
+ 'https://api.bugpk.com/api/dylive'
100
+ ]
101
+ }
87
102
  };
88
103
  function extractUrl(content) {
89
104
  const urlMatches = content.match(/https?:\/\/[^\s]+/gi) || [];
@@ -106,6 +121,14 @@ function getPlatformType(url) {
106
121
  return 'xiaohongshu';
107
122
  if (PLATFORM_KEYWORDS.weibo.some(k => lower.includes(k)))
108
123
  return 'weibo';
124
+ if (PLATFORM_KEYWORDS.toutiao.some(k => lower.includes(k)))
125
+ return 'toutiao';
126
+ if (PLATFORM_KEYWORDS.pipigx.some(k => lower.includes(k)))
127
+ return 'pipigx';
128
+ if (PLATFORM_KEYWORDS.pipixia.some(k => lower.includes(k)))
129
+ return 'pipixia';
130
+ if (PLATFORM_KEYWORDS.douyin.some(k => lower.includes(k)))
131
+ return 'douyin';
109
132
  return null;
110
133
  }
111
134
  async function shortUrl(url) {
@@ -129,20 +152,58 @@ async function downloadVideoWithThreads(url, filename) {
129
152
  worker.on('exit', (code) => code !== 0 && reject(new Error('视频下载线程异常')));
130
153
  });
131
154
  }
132
- function parseData(data, maxDescLength) {
155
+ function parseData(data, maxDescLength, platform) {
133
156
  const type = data.type || 'video';
134
157
  const title = data.title || data.desc || '无标题';
135
- const author = data.author?.name || data.author || data.auther || data.user?.name || '未知作者';
158
+ let author = '';
159
+ if (data.author?.name)
160
+ author = data.author.name;
161
+ else if (data.author)
162
+ author = data.author;
163
+ else if (data.auther)
164
+ author = data.auther;
165
+ else if (data.user?.name)
166
+ author = data.user.name;
167
+ else
168
+ author = '未知作者';
136
169
  const desc = (data.desc || data.description || title).slice(0, maxDescLength);
137
170
  const cover = data.cover || data.imgurl || data.pic || '';
138
- const images = data.images || [];
171
+ let images = [];
172
+ if (data.images)
173
+ images = data.images;
174
+ else if (data.imgurl && Array.isArray(data.imgurl))
175
+ images = data.imgurl;
139
176
  let video = '';
140
- if (data.url)
141
- video = data.url;
142
- else if (data.videos?.[0]?.url)
143
- video = data.videos[0].url;
144
- else if (data.video_backup?.[0]?.url)
145
- video = data.video_backup[0].url;
177
+ if (platform === 'douyin') {
178
+ if (typeof data.url === 'string' && data.url.trim() && data.url.startsWith('http')) {
179
+ video = data.url;
180
+ }
181
+ else if (Array.isArray(data.video_backup) && data.video_backup.length > 0) {
182
+ video = data.video_backup[0]?.url || '';
183
+ }
184
+ if (video && (video.endsWith('.m4a') || video.endsWith('.mp3'))) {
185
+ video = '';
186
+ }
187
+ }
188
+ else if (platform === 'bilibili') {
189
+ if (data.videos && Array.isArray(data.videos) && data.videos.length > 0) {
190
+ const hdVideo = data.videos.find(v => v.title.includes('1080') ||
191
+ (v.url && v.url.includes('192')) ||
192
+ v.index === 1);
193
+ video = hdVideo?.url || data.videos[0]?.url || '';
194
+ }
195
+ else if (data.url) {
196
+ video = data.url;
197
+ }
198
+ }
199
+ else {
200
+ if (data.url)
201
+ video = data.url;
202
+ else if (data.videos?.[0]?.url)
203
+ video = data.videos[0].url;
204
+ else if (data.video_backup?.[0]?.url)
205
+ video = data.video_backup[0].url;
206
+ }
146
207
  if (video.endsWith('.m4a') || video.endsWith('.mp3'))
147
208
  video = '';
148
209
  return { type, title, author, desc, cover, images, video };
@@ -164,37 +225,72 @@ function apply(ctx, config) {
164
225
  if (!worker_threads_1.isMainThread)
165
226
  return;
166
227
  clearAllCache();
167
- const getApiUrl = () => {
168
- if (config.apiMode === 'custom' && config.customApiUrl.trim()) {
169
- return config.customApiUrl.trim();
170
- }
171
- return config.builtinApiUrl;
172
- };
173
228
  const http = axios_1.default.create({
174
229
  timeout: config.timeout,
175
230
  headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' }
176
231
  });
177
232
  async function parse(url) {
178
- const p = getPlatformType(url);
179
- if (!p)
233
+ const platform = getPlatformType(url);
234
+ if (!platform)
180
235
  return { data: null, msg: '不支持该平台链接' };
181
- for (let i = 0; i <= config.retryTimes; i++) {
182
- try {
183
- const res = await http.get(getApiUrl(), { params: { url } });
184
- if ((res.data.code === 200 || res.data.code === 0) && res.data.data) {
185
- return { data: parseData(res.data.data, config.maxDescLength), msg: '解析成功' };
236
+ if (platform !== 'toutiao') {
237
+ for (let retry = 0; retry <= config.retryTimes; retry++) {
238
+ try {
239
+ const res = await http.get(API_CONFIG.universal, { params: { url } });
240
+ if ((res.data.code === 200 || res.data.code === 0) && res.data.data) {
241
+ const parseResult = parseData(res.data.data, config.maxDescLength, platform);
242
+ return { data: parseResult, msg: '解析成功' };
243
+ }
244
+ else if (res.data.code === 201) {
245
+ break;
246
+ }
186
247
  }
187
- else {
188
- return { data: null, msg: res.data.msg || '解析失败' };
248
+ catch (e) {
249
+ if (retry === config.retryTimes) {
250
+ break;
251
+ }
252
+ await delay(config.retryInterval);
189
253
  }
190
254
  }
191
- catch (e) {
192
- if (i === config.retryTimes)
193
- return { data: null, msg: '接口请求超时' };
194
- await delay(config.retryInterval);
255
+ }
256
+ const platformApis = API_CONFIG.platform[platform] || [];
257
+ for (let apiIndex = 0; apiIndex < platformApis.length; apiIndex++) {
258
+ const apiUrl = platformApis[apiIndex];
259
+ for (let retry = 0; retry <= config.retryTimes; retry++) {
260
+ try {
261
+ const res = await http.get(apiUrl, { params: { url } });
262
+ if ((res.data.code === 200 || res.data.code === 0) && (res.data.data || (platform === 'kuaishou' && res.data.images))) {
263
+ let parseResult = null;
264
+ if (platform === 'kuaishou' && res.data.images && !res.data.data) {
265
+ parseResult = parseData({
266
+ title: '快手图集',
267
+ author: '未知作者',
268
+ images: res.data.images,
269
+ type: 'image'
270
+ }, config.maxDescLength, platform);
271
+ }
272
+ else {
273
+ parseResult = parseData(res.data.data || res.data, config.maxDescLength, platform);
274
+ }
275
+ return { data: parseResult, msg: '解析成功' };
276
+ }
277
+ else if (retry < config.retryTimes) {
278
+ await delay(config.retryInterval);
279
+ continue;
280
+ }
281
+ else {
282
+ break;
283
+ }
284
+ }
285
+ catch (e) {
286
+ if (retry === config.retryTimes) {
287
+ break;
288
+ }
289
+ await delay(config.retryInterval);
290
+ }
195
291
  }
196
292
  }
197
- return { data: null, msg: '解析失败' };
293
+ return { data: null, msg: '所有接口解析失败,请稍后重试' };
198
294
  }
199
295
  async function processSingleUrl(session, url) {
200
296
  const hash = crypto_1.default.createHash('md5').update(url).digest('hex');
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-video-parser-all",
3
- "description": "Koishi 视频解析插件,支持抖音/快手/B站/小红书/微博视频链接解析",
4
- "version": "0.2.6",
3
+ "description": "Koishi 全平台视频解析插件,支持抖音/快手/B站/小红书/微博/今日头条/皮皮搞笑/皮皮虾视频/图文链接解析",
4
+ "version": "0.2.8",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [
@@ -19,9 +19,15 @@
19
19
  "plugin",
20
20
  "video",
21
21
  "parser",
22
+ "image",
22
23
  "bilibili",
23
24
  "douyin",
24
- "kuaishou"
25
+ "kuaishou",
26
+ "xiaohongshu",
27
+ "weibo",
28
+ "toutiao",
29
+ "pipigx",
30
+ "pipixia"
25
31
  ],
26
32
  "devDependencies": {
27
33
  "@koishijs/client": "^5.30.4",
@@ -30,7 +36,8 @@
30
36
  "typescript": "^5.3.3"
31
37
  },
32
38
  "dependencies": {
33
- "axios": "^1.6.8"
39
+ "axios": "^1.6.8",
40
+ "stream": "^0.0.3"
34
41
  },
35
42
  "peerDependencies": {
36
43
  "@koishijs/plugin-console": "^5.30.4",
@@ -43,5 +50,8 @@
43
50
  "bugs": {
44
51
  "url": "https://github.com/Minecraft-1314/koishi-plugin-video-parser-all/issues"
45
52
  },
46
- "homepage": "https://github.com/Minecraft-1314/koishi-plugin-video-parser-all#readme"
53
+ "homepage": "https://github.com/Minecraft-1314/koishi-plugin-video-parser-all#readme",
54
+ "engines": {
55
+ "node": ">=16.0.0"
56
+ }
47
57
  }
package/readme.md CHANGED
@@ -3,20 +3,22 @@
3
3
  ## 项目介绍 (Project Introduction)
4
4
 
5
5
  ### 中文
6
- 这是一个为 Koishi 机器人框架开发的**多平台视频解析插件**,支持自动识别并解析抖音、快手、B站、小红书、微博等主流短视频平台链接。核心特性:
7
- - 自动识别多平台视频链接,无需手动指定平台
8
- - 自定义解析结果格式、返回内容类型(封面/链接/视频文件)
9
- - 内置防重复解析、接口重试、自动缓存清理等实用功能
10
- - 支持 OneBot 平台消息合并转发,优化展示体验
11
- - 可切换内置 API 或自定义 API,适配不同网络环境
6
+ 这是一个为 Koishi 机器人框架开发的**全平台视频/图文解析插件**,支持自动识别并解析抖音、快手、B站、小红书、微博、今日头条、皮皮搞笑、皮皮虾等主流平台的短视频/图文链接。核心特性:
7
+ - 🚀 自动识别多平台链接,无需手动指定平台
8
+ - 🎨 自定义解析结果格式、返回内容类型(封面/链接/视频文件)
9
+ - 内置防重复解析、接口重试、自动缓存清理等实用功能
10
+ - 📤 支持 OneBot 平台消息合并转发,优化展示体验
11
+ - 🔌 内置多套解析 API,自动降级容错,提升解析成功率
12
+ - 📸 同时支持视频解析和图文/图集解析
12
13
 
13
14
  ### English
14
- A multi-platform video parsing plugin for the Koishi bot framework, supporting automatic recognition and parsing of video links from Douyin, Kuaishou, Bilibili, Xiaohongshu, Weibo and other mainstream platforms. Core features:
15
- - Auto-detect video links across platforms without manual specification
16
- - Customize result formatting and output types (cover/link/video file)
17
- - Built-in duplicate prevention, retry logic, auto cache cleanup
18
- - Support OneBot message forwarding for better display experience
19
- - Switchable between built-in and custom API for different network environments
15
+ A multi-platform video/image parsing plugin for the Koishi bot framework, supporting automatic recognition and parsing of video/image links from Douyin, Kuaishou, Bilibili, Xiaohongshu, Weibo, Toutiao, Pipigx, Pipixia and other mainstream platforms. Core features:
16
+ - 🚀 Auto-detect links across platforms without manual specification
17
+ - 🎨 Customize result formatting and output types (cover/link/video file)
18
+ - Built-in duplicate prevention, retry logic, auto cache cleanup
19
+ - 📤 Support OneBot message forwarding for better display experience
20
+ - 🔌 Multiple built-in parsing APIs with automatic failover
21
+ - 📸 Support both video parsing and image album parsing
20
22
 
21
23
  ## 项目仓库 (Repository)
22
24
  - GitHub: `https://github.com/Minecraft-1314/koishi-plugin-video-parser-all`
@@ -24,16 +26,48 @@ A multi-platform video parsing plugin for the Koishi bot framework, supporting a
24
26
 
25
27
  ## 核心指令 (Core Commands)
26
28
 
27
- parse (手动解析)(Manual parsing)
29
+ | 指令 (Command) | 说明 (Description) | 示例 (Example) |
30
+ |----------------|--------------------|----------------|
31
+ | `parse <url>` | 手动解析指定的视频/图文链接 | `parse https://v.douyin.com/xxxx/` |
32
+ | `clear-cache` | 清理解析缓存和临时下载文件 | `clear-cache` |
28
33
 
29
- clear-cache (清理缓存)(Clear cache)
34
+ ## 配置项说明 (Configuration)
35
+
36
+ | 配置项 (Config Item) | 类型 (Type) | 默认值 (Default) | 说明 (Description) |
37
+ |----------------------|-------------|------------------|--------------------|
38
+ | `enable` | boolean | true | 是否启用插件 |
39
+ | `showWaitingTip` | boolean | true | 解析时是否显示等待提示 |
40
+ | `waitingTipText` | string | 正在解析视频,请稍候... | 等待提示文本 |
41
+ | `sameLinkInterval` | number | 180 | 相同链接解析间隔(秒) |
42
+ | `imageParseFormat` | string | `${标题}\n${UP主}` | 解析结果文本格式 |
43
+ | `returnContent.showImageText` | boolean | true | 是否显示文本与封面 |
44
+ | `returnContent.showVideoUrl` | boolean | false | 是否显示无水印链接 |
45
+ | `returnContent.showVideoFile` | boolean | true | 是否发送视频文件 |
46
+ | `maxDescLength` | number | 200 | 简介最大长度 |
47
+ | `timeout` | number | 180000 | API 请求超时时间(毫秒) |
48
+ | `retryTimes` | number | 0 | 接口重试次数 |
49
+ | `retryInterval` | number | 0 | 重试间隔(毫秒) |
50
+ | `autoClearCacheInterval` | number | 60 | 自动清理缓存间隔(分钟) |
51
+
52
+ ## 支持平台 (Supported Platforms)
53
+
54
+ | 平台 (Platform) | 支持类型 (Supported Type) | 状态 (Status) |
55
+ |-----------------|---------------------------|---------------|
56
+ | 抖音 (Douyin) | 视频/图文 | ✅ 稳定 |
57
+ | 快手 (Kuaishou) | 视频/图集 | ✅ 稳定 |
58
+ | B站 (Bilibili) | 视频 | ✅ 稳定 |
59
+ | 小红书 (Xiaohongshu) | 视频/图文 | ✅ 稳定 |
60
+ | 微博 (Weibo) | 视频/图文 | ✅ 稳定 |
61
+ | 今日头条 (Toutiao) | 视频 | ✅ 稳定 |
62
+ | 皮皮搞笑 (Pipigx) | 视频/图文 | ✅ 稳定 |
63
+ | 皮皮虾 (Pipixia) | 视频/图文 | ✅ 稳定 |
30
64
 
31
65
  ## 项目贡献者 (Contributors)
32
66
 
33
67
  | 贡献者 (Contributor) | 贡献内容 (Contribution) |
34
68
  |----------------------|-------------------------|
35
69
  | Minecraft-1314 | 插件完整开发 (Complete plugin development) |
36
- | JH-Ahua | BugPk-Api |
70
+ | JH-Ahua | BugPk-Api 支持 |
37
71
  | (欢迎提交 PR 加入贡献者列表) | (Welcome to submit PR to join the contributor list) |
38
72
 
39
73
  ## 许可协议 (License)