koishi-plugin-video-parser-all 0.0.8 → 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.
- package/lib/index.d.ts +1 -0
- package/lib/index.js +146 -79
- package/package.json +1 -1
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -30,13 +30,17 @@ exports.Config = koishi_1.Schema.object({
|
|
|
30
30
|
bugpkKuaishouApi: koishi_1.Schema.string().default('https://api.bugpk.com/api/ksjx'),
|
|
31
31
|
bugpkBilibiliApi: koishi_1.Schema.string().default('https://api.bugpk.com/api/bilibili'),
|
|
32
32
|
timeout: koishi_1.Schema.number().default(15000),
|
|
33
|
+
ignoreSendError: koishi_1.Schema.boolean().default(true).description('忽略消息发送失败的错误(避免日志刷屏)'),
|
|
33
34
|
});
|
|
34
35
|
const processed = new Map();
|
|
35
36
|
function apply(ctx, config) {
|
|
37
|
+
// 修复:移除axios不支持的retry配置,保留核心请求配置
|
|
36
38
|
const request = axios_1.default.create({
|
|
37
39
|
timeout: config.timeout,
|
|
38
40
|
headers: {
|
|
39
|
-
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
|
|
41
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
|
|
42
|
+
'Referer': 'https://www.bugpk.com/',
|
|
43
|
+
'Accept': 'application/json, text/plain, */*'
|
|
40
44
|
}
|
|
41
45
|
});
|
|
42
46
|
// 抖音解析
|
|
@@ -62,110 +66,173 @@ function apply(ctx, config) {
|
|
|
62
66
|
video: data.url || ''
|
|
63
67
|
};
|
|
64
68
|
}
|
|
65
|
-
// B
|
|
69
|
+
// B站解析(增强:兼容更多返回格式)
|
|
66
70
|
function parseBilibili(data) {
|
|
67
|
-
const
|
|
71
|
+
const title = data.title || data.Title || '';
|
|
72
|
+
const cover = data.cover || data.imgurl || data.cover_url || '';
|
|
73
|
+
const author = data.user?.name || data.author || '未知UP主';
|
|
74
|
+
const desc = data.description || data.desc || title || '无简介';
|
|
75
|
+
const videoUrl = data.url || (data.videos?.length > 0 ? data.videos[0].url : data.video_url || '');
|
|
68
76
|
return {
|
|
69
|
-
title
|
|
70
|
-
author
|
|
71
|
-
desc
|
|
77
|
+
title,
|
|
78
|
+
author,
|
|
79
|
+
desc,
|
|
72
80
|
digg: 0, coin: 0, collect: 0, share: 0, play: 0, danmaku: 0,
|
|
73
|
-
cover
|
|
81
|
+
cover,
|
|
74
82
|
video: videoUrl
|
|
75
83
|
};
|
|
76
84
|
}
|
|
77
|
-
//
|
|
85
|
+
// 核心:只走专属接口,增强异常捕获
|
|
78
86
|
async function parseVideo(url) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
87
|
+
try {
|
|
88
|
+
// 抖音
|
|
89
|
+
if (url.includes('douyin.com') || url.includes('v.douyin.com')) {
|
|
90
|
+
try {
|
|
91
|
+
const res = await request.get(config.bugpkDouyinMainApi, { params: { url } });
|
|
92
|
+
if (res.data.code === 200 && res.data.data)
|
|
93
|
+
return parseDouyin(res.data.data);
|
|
94
|
+
}
|
|
95
|
+
catch (e) {
|
|
96
|
+
ctx.logger.debug(`抖音主接口失败: ${e.message}`);
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
const res = await request.get(config.bugpkDouyinBackupApi, { params: { url } });
|
|
100
|
+
if (res.data.code === 200 && res.data.data)
|
|
101
|
+
return parseDouyin(res.data.data);
|
|
102
|
+
}
|
|
103
|
+
catch (e) {
|
|
104
|
+
ctx.logger.debug(`抖音备用接口失败: ${e.message}`);
|
|
105
|
+
}
|
|
85
106
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
107
|
+
// 快手
|
|
108
|
+
if (url.includes('kuaishou.com')) {
|
|
109
|
+
try {
|
|
110
|
+
const res = await request.get(config.bugpkKuaishouApi, { params: { url } });
|
|
111
|
+
if (res.data.code === 200 && res.data.data)
|
|
112
|
+
return parseKuaishou(res.data.data);
|
|
113
|
+
}
|
|
114
|
+
catch (e) {
|
|
115
|
+
ctx.logger.debug(`快手接口失败: ${e.message}`);
|
|
116
|
+
}
|
|
91
117
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
118
|
+
// B站(增强:兼容b23.tv/ bilibili.com 所有子域名)
|
|
119
|
+
if (url.includes('bilibili.com') || url.includes('b23.tv')) {
|
|
120
|
+
try {
|
|
121
|
+
const res = await request.get(config.bugpkBilibiliApi, { params: { url } });
|
|
122
|
+
if (res.data.code === 200 && res.data.data)
|
|
123
|
+
return parseBilibili(res.data.data);
|
|
124
|
+
}
|
|
125
|
+
catch (e) {
|
|
126
|
+
ctx.logger.debug(`B站接口失败: ${e.message}`);
|
|
127
|
+
}
|
|
100
128
|
}
|
|
101
|
-
catch { }
|
|
102
129
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
try {
|
|
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);
|
|
109
|
-
}
|
|
110
|
-
catch { }
|
|
130
|
+
catch (e) {
|
|
131
|
+
ctx.logger.error(`解析核心逻辑异常: ${e.message}`);
|
|
111
132
|
}
|
|
112
|
-
//
|
|
133
|
+
// 兜底返回(更友好的提示)
|
|
113
134
|
return {
|
|
114
|
-
title: '解析失败',
|
|
135
|
+
title: '解析失败',
|
|
136
|
+
author: '',
|
|
137
|
+
desc: '接口暂不可用或链接格式不支持,请稍后重试',
|
|
115
138
|
digg: 0, coin: 0, collect: 0, share: 0, play: 0, danmaku: 0,
|
|
116
139
|
cover: '', video: ''
|
|
117
140
|
};
|
|
118
141
|
}
|
|
142
|
+
// 发送结果(核心修复:所有send操作加try/catch,避免中断)
|
|
119
143
|
async function sendResult(session, data) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
+
try {
|
|
145
|
+
let text = config.imageParseFormat
|
|
146
|
+
.replace(/\${标题}/g, data.title)
|
|
147
|
+
.replace(/\${UP主}/g, data.author)
|
|
148
|
+
.replace(/\${简介}/g, data.desc.slice(0, config.maxDescLength))
|
|
149
|
+
.replace(/\${点赞}/g, data.digg.toString())
|
|
150
|
+
.replace(/\${投币}/g, data.coin.toString())
|
|
151
|
+
.replace(/\${收藏}/g, data.collect.toString())
|
|
152
|
+
.replace(/\${转发}/g, data.share.toString())
|
|
153
|
+
.replace(/\${观看}/g, data.play.toString())
|
|
154
|
+
.replace(/\${弹幕}/g, data.danmaku.toString())
|
|
155
|
+
.replace(/\${tab}/g, '\t')
|
|
156
|
+
.replace(/\${~~~}/g, '——————————————');
|
|
157
|
+
const [beforeCover, afterCover] = text.split('\${封面}');
|
|
158
|
+
// 分段发送,每一步都加异常捕获
|
|
159
|
+
if (beforeCover && beforeCover.trim()) {
|
|
160
|
+
await session.send(beforeCover.trim()).catch(e => {
|
|
161
|
+
if (!config.ignoreSendError)
|
|
162
|
+
ctx.logger.warn(`发送文本失败: ${e.message}`);
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
if (data.cover) {
|
|
166
|
+
await session.send(koishi_1.h.image(data.cover)).catch(e => {
|
|
167
|
+
if (!config.ignoreSendError)
|
|
168
|
+
ctx.logger.warn(`发送封面失败: ${e.message}`);
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
if (afterCover && afterCover.trim()) {
|
|
172
|
+
await session.send(afterCover.trim()).catch(e => {
|
|
173
|
+
if (!config.ignoreSendError)
|
|
174
|
+
ctx.logger.warn(`发送文本失败: ${e.message}`);
|
|
175
|
+
});
|
|
144
176
|
}
|
|
145
|
-
|
|
146
|
-
|
|
177
|
+
if (data.video) {
|
|
178
|
+
try {
|
|
179
|
+
await session.send(koishi_1.h.video(data.video)).catch(e => {
|
|
180
|
+
// 视频发送失败则回退为文本链接
|
|
181
|
+
session.send(`📥 无水印视频链接:${data.video}`).catch(() => { });
|
|
182
|
+
});
|
|
183
|
+
if (config.showVideoUrl) {
|
|
184
|
+
await session.send(`🔗 无水印链接:${data.video}`).catch(e => {
|
|
185
|
+
if (!config.ignoreSendError)
|
|
186
|
+
ctx.logger.warn(`发送链接失败: ${e.message}`);
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
catch (e) {
|
|
191
|
+
await session.send(`📥 无水印视频:${data.video}`).catch(() => { });
|
|
192
|
+
}
|
|
147
193
|
}
|
|
148
194
|
}
|
|
195
|
+
catch (e) {
|
|
196
|
+
if (!config.ignoreSendError)
|
|
197
|
+
ctx.logger.error(`发送结果异常: ${e.message}`);
|
|
198
|
+
}
|
|
149
199
|
}
|
|
200
|
+
// 消息监听(增强:加全局异常捕获)
|
|
150
201
|
ctx.on('message', async (session) => {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
202
|
+
try {
|
|
203
|
+
if (!config.enable)
|
|
204
|
+
return;
|
|
205
|
+
const content = session.content.trim();
|
|
206
|
+
// 提取纯链接
|
|
207
|
+
const urlMatch = content.match(/https?:\/\/[^\s]+/);
|
|
208
|
+
if (!urlMatch)
|
|
209
|
+
return;
|
|
210
|
+
const url = urlMatch[0];
|
|
211
|
+
// 去重逻辑
|
|
212
|
+
const hash = crypto_1.default.createHash('md5').update(url).digest('hex');
|
|
213
|
+
const now = Date.now();
|
|
214
|
+
if (processed.get(hash) && now - processed.get(hash) < config.sameLinkInterval * 1000)
|
|
215
|
+
return;
|
|
216
|
+
processed.set(hash, now);
|
|
217
|
+
// 发送等待提示(加异常捕获)
|
|
218
|
+
if (config.showWaitingTip) {
|
|
219
|
+
await session.send(config.waitingTipText).catch(e => {
|
|
220
|
+
if (!config.ignoreSendError)
|
|
221
|
+
ctx.logger.warn(`发送等待提示失败: ${e.message}`);
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
// 解析并发送结果
|
|
225
|
+
const data = await parseVideo(url);
|
|
226
|
+
await sendResult(session, data);
|
|
227
|
+
}
|
|
228
|
+
catch (e) {
|
|
229
|
+
ctx.logger.error(`消息处理异常: ${e.message}`);
|
|
230
|
+
}
|
|
165
231
|
});
|
|
232
|
+
// 定时清理缓存
|
|
166
233
|
setInterval(() => {
|
|
167
234
|
const now = Date.now();
|
|
168
235
|
processed.forEach((t, k) => now - t > 86400000 && processed.delete(k));
|
|
169
236
|
}, 3600000);
|
|
170
|
-
ctx.logger.info('✅
|
|
237
|
+
ctx.logger.info('✅ 视频解析插件(修复版)加载完成');
|
|
171
238
|
}
|