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 +4 -4
- package/lib/index.js +81 -109
- package/package.json +1 -1
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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)
|
|
14
|
-
showWaitingTip: koishi_1.Schema.boolean().default(true)
|
|
15
|
-
waitingTipText: koishi_1.Schema.string().default('正在解析视频…')
|
|
16
|
-
sameLinkInterval: koishi_1.Schema.number().default(180)
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
//
|
|
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
|
-
//
|
|
48
|
-
if (url.includes('
|
|
79
|
+
// 抖音
|
|
80
|
+
if (url.includes('douyin.com') || url.includes('v.douyin.com')) {
|
|
49
81
|
try {
|
|
50
|
-
const res = await request.get(config.
|
|
51
|
-
|
|
52
|
-
|
|
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
|
|
67
|
-
|
|
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
|
-
//
|
|
71
|
-
if (url.includes('kuaishou.com')
|
|
94
|
+
// 快手
|
|
95
|
+
if (url.includes('kuaishou.com')) {
|
|
72
96
|
try {
|
|
73
|
-
const res = await request.get(config.
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
//
|
|
94
|
-
if (url.includes('
|
|
103
|
+
// B站
|
|
104
|
+
if (url.includes('bilibili.com') || url.includes('b23.tv')) {
|
|
95
105
|
try {
|
|
96
|
-
const res = await request.get(config.
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
//
|
|
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(`🔗
|
|
143
|
+
await session.send(`🔗 ${data.video}`);
|
|
172
144
|
}
|
|
173
|
-
catch
|
|
174
|
-
await session.send(`📥
|
|
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
|
-
|
|
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
|
}
|