koishi-plugin-video-parser-all 0.0.7 → 0.0.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
@@ -8,10 +8,10 @@ export interface Config {
8
8
  imageParseFormat: string;
9
9
  showVideoUrl: boolean;
10
10
  maxDescLength: number;
11
- douyinApi: string;
12
- bilibiliApi: string;
13
- kuaishouApi: string;
14
- backupApi: string;
11
+ bugpkDouyinMainApi: string;
12
+ bugpkDouyinBackupApi: string;
13
+ bugpkKuaishouApi: string;
14
+ bugpkBilibiliApi: string;
15
15
  timeout: number;
16
16
  }
17
17
  export declare const Config: Schema<Config>;
package/lib/index.js CHANGED
@@ -10,10 +10,10 @@ const axios_1 = __importDefault(require("axios"));
10
10
  const crypto_1 = __importDefault(require("crypto"));
11
11
  exports.name = 'video-parser-all';
12
12
  exports.Config = koishi_1.Schema.object({
13
- enable: koishi_1.Schema.boolean().default(true).description('开启解析功能'),
14
- showWaitingTip: koishi_1.Schema.boolean().default(true).description('显示等待提示'),
15
- waitingTipText: koishi_1.Schema.string().default('正在解析视频…').description('等待提示文字'),
16
- sameLinkInterval: koishi_1.Schema.number().default(180).description('相同链接去重间隔(秒)'),
13
+ enable: koishi_1.Schema.boolean().default(true),
14
+ showWaitingTip: koishi_1.Schema.boolean().default(true),
15
+ waitingTipText: koishi_1.Schema.string().default('正在解析视频…'),
16
+ sameLinkInterval: koishi_1.Schema.number().default(180),
17
17
  imageParseFormat: koishi_1.Schema.string()
18
18
  .role('textarea')
19
19
  .default(`\${标题} \${tab} \${UP主}
@@ -22,126 +22,100 @@ exports.Config = koishi_1.Schema.object({
22
22
  收藏:\${收藏} \${tab} 转发:\${转发}
23
23
  观看:\${观看} \${tab} 弹幕:\${弹幕}
24
24
  \${~~~}
25
- \${封面}`)
26
- .description('解析输出格式(请勿修改占位符)'),
27
- showVideoUrl: koishi_1.Schema.boolean().default(false).description('额外显示无水印视频链接'),
28
- maxDescLength: koishi_1.Schema.number().default(200).description('简介最大长度'),
29
- douyinApi: koishi_1.Schema.string().default('https://api.douyin.wtf/api/hybrid/video_data').description('抖音主API'),
30
- bilibiliApi: koishi_1.Schema.string().default('https://api.douyin.wtf/api/bilibili/web/fetch_one_video').description('B站主API'),
31
- kuaishouApi: koishi_1.Schema.string().default('https://api.douyin.wtf/api/hybrid/video_data').description('快手主API'),
32
- backupApi: koishi_1.Schema.string().default('https://www.alapi.cn/api/video/jh').description('聚合备用API'),
33
- timeout: koishi_1.Schema.number().default(15000).description('API请求超时(毫秒)'),
25
+ \${封面}`),
26
+ showVideoUrl: koishi_1.Schema.boolean().default(false),
27
+ maxDescLength: koishi_1.Schema.number().default(200),
28
+ bugpkDouyinMainApi: koishi_1.Schema.string().default('https://api.bugpk.com/api/douyin'),
29
+ bugpkDouyinBackupApi: koishi_1.Schema.string().default('https://api.bugpk.com/api/dyjx'),
30
+ bugpkKuaishouApi: koishi_1.Schema.string().default('https://api.bugpk.com/api/ksjx'),
31
+ bugpkBilibiliApi: koishi_1.Schema.string().default('https://api.bugpk.com/api/bilibili'),
32
+ timeout: koishi_1.Schema.number().default(15000),
34
33
  });
35
- // 去重缓存
36
34
  const processed = new Map();
37
35
  function apply(ctx, config) {
38
- // 创建请求实例
39
36
  const request = axios_1.default.create({
40
37
  timeout: config.timeout,
41
38
  headers: {
42
39
  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
43
40
  }
44
41
  });
45
- // 核心解析函数(按平台自动匹配API)
42
+ // 抖音解析
43
+ function parseDouyin(data) {
44
+ const videoUrl = data.url || (data.live_photo?.[0]?.video || '');
45
+ return {
46
+ title: data.title || '无标题',
47
+ author: data.author?.name || '未知作者',
48
+ desc: data.desc || data.title || '无简介',
49
+ digg: 0, coin: 0, collect: 0, share: 0, play: 0, danmaku: 0,
50
+ cover: data.cover || '',
51
+ video: videoUrl
52
+ };
53
+ }
54
+ // 快手解析
55
+ function parseKuaishou(data) {
56
+ return {
57
+ title: data.title || '无标题',
58
+ author: '未知作者',
59
+ desc: data.title || '无简介',
60
+ digg: 0, coin: 0, collect: 0, share: 0, play: 0, danmaku: 0,
61
+ cover: data.cover || '',
62
+ video: data.url || ''
63
+ };
64
+ }
65
+ // B站解析
66
+ function parseBilibili(data) {
67
+ const videoUrl = data.url || (data.videos?.[0]?.url || '');
68
+ return {
69
+ title: data.title || '无标题',
70
+ author: data.user?.name || '未知UP主',
71
+ desc: data.description || data.title || '无简介',
72
+ digg: 0, coin: 0, collect: 0, share: 0, play: 0, danmaku: 0,
73
+ cover: data.cover || '',
74
+ video: videoUrl
75
+ };
76
+ }
77
+ // 核心:只走你给的四个接口,无任何通用API
46
78
  async function parseVideo(url) {
47
- // 1. B站解析(优先主API)
48
- if (url.includes('bilibili.com') || url.includes('b23.tv')) {
79
+ // 抖音
80
+ if (url.includes('douyin.com') || url.includes('v.douyin.com')) {
49
81
  try {
50
- const res = await request.get(config.bilibiliApi, { params: { url } });
51
- const d = res.data.data;
52
- return {
53
- title: d.title || '无标题',
54
- author: d.owner?.name || '未知UP主',
55
- desc: d.desc || '无简介',
56
- digg: d.stat?.like || 0,
57
- coin: d.stat?.coin || 0,
58
- collect: d.stat?.favorite || 0,
59
- share: d.stat?.share || 0,
60
- play: d.stat?.view || 0,
61
- danmaku: d.stat?.danmaku || 0,
62
- cover: d.pic || '',
63
- video: d.video_url || ''
64
- };
82
+ const res = await request.get(config.bugpkDouyinMainApi, { params: { url } });
83
+ if (res.data.code === 200 && res.data.data)
84
+ return parseDouyin(res.data.data);
65
85
  }
66
- catch (e) {
67
- ctx.logger.warn(`B站主API解析失败,尝试备用API: ${e.message}`);
86
+ catch { }
87
+ try {
88
+ const res = await request.get(config.bugpkDouyinBackupApi, { params: { url } });
89
+ if (res.data.code === 200 && res.data.data)
90
+ return parseDouyin(res.data.data);
68
91
  }
92
+ catch { }
69
93
  }
70
- // 2. 快手解析(优先主API)
71
- if (url.includes('kuaishou.com') || url.includes('ksweb')) {
94
+ // 快手
95
+ if (url.includes('kuaishou.com')) {
72
96
  try {
73
- const res = await request.get(config.kuaishouApi, { params: { url } });
74
- const d = res.data.data?.aweme_detail || res.data.data;
75
- return {
76
- title: d.desc || '无标题',
77
- author: d.author?.nickname || '未知作者',
78
- desc: d.desc || '无简介',
79
- digg: d.statistics?.digg_count || 0,
80
- coin: 0, // 快手无投币
81
- collect: d.statistics?.collect_count || 0,
82
- share: d.statistics?.share_count || 0,
83
- play: d.statistics?.play_count || 0,
84
- danmaku: d.statistics?.comment_count || 0, // 用评论数替代弹幕
85
- cover: d.video?.cover || '',
86
- video: d.video?.play_addr?.url_list?.[0] || ''
87
- };
88
- }
89
- catch (e) {
90
- ctx.logger.warn(`快手主API解析失败,尝试备用API: ${e.message}`);
97
+ const res = await request.get(config.bugpkKuaishouApi, { params: { url } });
98
+ if (res.data.code === 200 && res.data.data)
99
+ return parseKuaishou(res.data.data);
91
100
  }
101
+ catch { }
92
102
  }
93
- // 3. 抖音解析(优先主API)
94
- if (url.includes('douyin.com') || url.includes('dy')) {
103
+ // B站
104
+ if (url.includes('bilibili.com') || url.includes('b23.tv')) {
95
105
  try {
96
- const res = await request.get(config.douyinApi, { params: { url } });
97
- const d = res.data.data?.aweme_detail || res.data.data;
98
- return {
99
- title: d.desc || '无标题',
100
- author: d.author?.nickname || '未知作者',
101
- desc: d.desc || '无简介',
102
- digg: d.statistics?.digg_count || 0,
103
- coin: 0, // 抖音无投币
104
- collect: d.statistics?.collect_count || 0,
105
- share: d.statistics?.share_count || 0,
106
- play: d.statistics?.play_count || 0,
107
- danmaku: d.statistics?.comment_count || 0, // 用评论数替代弹幕
108
- cover: d.video?.cover || '',
109
- video: d.video?.play_addr?.url_list?.[0] || ''
110
- };
111
- }
112
- catch (e) {
113
- ctx.logger.warn(`抖音主API解析失败,尝试备用API: ${e.message}`);
106
+ const res = await request.get(config.bugpkBilibiliApi, { params: { url } });
107
+ if (res.data.code === 200 && res.data.data)
108
+ return parseBilibili(res.data.data);
114
109
  }
110
+ catch { }
115
111
  }
116
- // 4. 备用聚合API(所有主API失败时)
117
- try {
118
- const res = await request.get(config.backupApi, { params: { url } });
119
- const d = res.data.data;
120
- return {
121
- title: d.title || '无标题',
122
- author: '未知作者',
123
- desc: d.title || '无简介',
124
- digg: 0,
125
- coin: 0,
126
- collect: 0,
127
- share: 0,
128
- play: 0,
129
- danmaku: 0,
130
- cover: d.cover_url || '',
131
- video: d.video_url || ''
132
- };
133
- }
134
- catch (e) {
135
- ctx.logger.error(`备用API解析失败: ${e.message}`);
136
- }
137
- // 兜底返回
112
+ // 所有接口都失败 → 直接返回失败
138
113
  return {
139
- title: '解析失败', author: '', desc: '无法获取视频信息',
114
+ title: '解析失败', author: '', desc: '不支持该链接或接口异常',
140
115
  digg: 0, coin: 0, collect: 0, share: 0, play: 0, danmaku: 0,
141
116
  cover: '', video: ''
142
117
  };
143
118
  }
144
- // 生成并发送解析结果(严格匹配你的格式)
145
119
  async function sendResult(session, data) {
146
120
  let text = config.imageParseFormat
147
121
  .replace(/\${标题}/g, data.title)
@@ -155,7 +129,6 @@ function apply(ctx, config) {
155
129
  .replace(/\${弹幕}/g, data.danmaku.toString())
156
130
  .replace(/\${tab}/g, '\t')
157
131
  .replace(/\${~~~}/g, '——————————————');
158
- // 拆分封面占位符,避免类型错误
159
132
  const [beforeCover, afterCover] = text.split('\${封面}');
160
133
  if (beforeCover)
161
134
  await session.send(beforeCover.trim());
@@ -163,37 +136,36 @@ function apply(ctx, config) {
163
136
  await session.send(koishi_1.h.image(data.cover));
164
137
  if (afterCover)
165
138
  await session.send(afterCover.trim());
166
- // 发送无水印视频
167
139
  if (data.video) {
168
140
  try {
169
141
  await session.send(koishi_1.h.video(data.video));
170
142
  if (config.showVideoUrl)
171
- await session.send(`🔗 无水印链接:${data.video}`);
143
+ await session.send(`🔗 ${data.video}`);
172
144
  }
173
- catch (e) {
174
- await session.send(`📥 无水印视频:${data.video}`);
145
+ catch {
146
+ await session.send(`📥 ${data.video}`);
175
147
  }
176
148
  }
177
149
  }
178
- // 消息监听与处理
179
150
  ctx.on('message', async (session) => {
180
151
  if (!config.enable)
181
152
  return;
182
153
  const url = session.content.trim();
183
154
  if (!url.startsWith('http'))
184
155
  return;
185
- // 去重逻辑
186
156
  const hash = crypto_1.default.createHash('md5').update(url).digest('hex');
187
157
  const now = Date.now();
188
158
  if (processed.get(hash) && now - processed.get(hash) < config.sameLinkInterval * 1000)
189
159
  return;
190
160
  processed.set(hash, now);
191
- // 发送等待提示
192
161
  if (config.showWaitingTip)
193
162
  await session.send(config.waitingTipText);
194
- // 解析并发送结果
195
163
  const data = await parseVideo(url);
196
164
  await sendResult(session, data);
197
165
  });
198
- ctx.logger.info('✅ 抖音+B站+快手 三平台解析插件加载完成');
166
+ setInterval(() => {
167
+ const now = Date.now();
168
+ processed.forEach((t, k) => now - t > 86400000 && processed.delete(k));
169
+ }, 3600000);
170
+ ctx.logger.info('✅ 抖音+快手+B站 纯专属接口解析(无通用API)加载完成');
199
171
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-video-parser-all",
3
3
  "description": "Koishi 视频解析插件,支持抖音/快手/B站链接解析,可自定义API和解析规则",
4
- "version": "0.0.7",
4
+ "version": "0.0.8",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [