koishi-plugin-video-parser-all 1.0.9 → 1.1.1
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 +2 -2
- package/lib/index.js +65 -41
- package/package.json +1 -1
- package/readme.md +3 -2
package/lib/index.d.ts
CHANGED
|
@@ -29,10 +29,10 @@ export declare const Config: Schema<{
|
|
|
29
29
|
primaryApiUrl?: string | null | undefined;
|
|
30
30
|
backupApiUrl?: string | null | undefined;
|
|
31
31
|
useDedicatedApiFirst?: boolean | null | undefined;
|
|
32
|
+
platformDedicatedFirst?: import("cosmokit").Dict<boolean, string> | null | undefined;
|
|
32
33
|
customApis?: ({
|
|
33
34
|
platform?: "bilibili" | "douyin" | "kuaishou" | "xiaohongshu" | "weibo" | "xigua" | "youtube" | "tiktok" | "acfun" | "zhihu" | "weishi" | "huya" | "haokan" | "meipai" | "twitter" | "instagram" | "doubao" | null | undefined;
|
|
34
35
|
apiUrl?: string | null | undefined;
|
|
35
|
-
useDedicatedFirst?: boolean | null | undefined;
|
|
36
36
|
} & import("cosmokit").Dict)[] | null | undefined;
|
|
37
37
|
} & {
|
|
38
38
|
waitingTipText?: string | null | undefined;
|
|
@@ -69,10 +69,10 @@ export declare const Config: Schema<{
|
|
|
69
69
|
primaryApiUrl: string;
|
|
70
70
|
backupApiUrl: string;
|
|
71
71
|
useDedicatedApiFirst: boolean;
|
|
72
|
+
platformDedicatedFirst: import("cosmokit").Dict<boolean, string>;
|
|
72
73
|
customApis: Schemastery.ObjectT<{
|
|
73
74
|
platform: Schema<"bilibili" | "douyin" | "kuaishou" | "xiaohongshu" | "weibo" | "xigua" | "youtube" | "tiktok" | "acfun" | "zhihu" | "weishi" | "huya" | "haokan" | "meipai" | "twitter" | "instagram" | "doubao", "bilibili" | "douyin" | "kuaishou" | "xiaohongshu" | "weibo" | "xigua" | "youtube" | "tiktok" | "acfun" | "zhihu" | "weishi" | "huya" | "haokan" | "meipai" | "twitter" | "instagram" | "doubao">;
|
|
74
75
|
apiUrl: Schema<string, string>;
|
|
75
|
-
useDedicatedFirst: Schema<boolean, boolean>;
|
|
76
76
|
}>[];
|
|
77
77
|
} & {
|
|
78
78
|
waitingTipText: string;
|
package/lib/index.js
CHANGED
|
@@ -10,7 +10,6 @@ const axios_1 = __importDefault(require("axios"));
|
|
|
10
10
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
11
11
|
const path_1 = __importDefault(require("path"));
|
|
12
12
|
const fs_1 = require("fs");
|
|
13
|
-
const promises_2 = require("stream/promises");
|
|
14
13
|
const lru_cache_1 = require("lru-cache");
|
|
15
14
|
exports.name = 'video-parser-all';
|
|
16
15
|
exports.Config = koishi_1.Schema.intersect([
|
|
@@ -49,6 +48,27 @@ exports.Config = koishi_1.Schema.intersect([
|
|
|
49
48
|
primaryApiUrl: koishi_1.Schema.string().default('https://api.bugpk.com/api/short_videos').description('主 API 地址'),
|
|
50
49
|
backupApiUrl: koishi_1.Schema.string().default('https://api.bugpk.com/api/svparse').description('备用主 API 地址(仅支持抖音/小红书/ins/即梦)'),
|
|
51
50
|
useDedicatedApiFirst: koishi_1.Schema.boolean().default(false).description('全局默认:是否优先使用平台专属 API(各平台可单独覆盖)'),
|
|
51
|
+
platformDedicatedFirst: koishi_1.Schema.dict(koishi_1.Schema.boolean())
|
|
52
|
+
.default({
|
|
53
|
+
bilibili: false,
|
|
54
|
+
douyin: false,
|
|
55
|
+
kuaishou: false,
|
|
56
|
+
xiaohongshu: false,
|
|
57
|
+
weibo: false,
|
|
58
|
+
xigua: false,
|
|
59
|
+
youtube: false,
|
|
60
|
+
tiktok: false,
|
|
61
|
+
acfun: false,
|
|
62
|
+
zhihu: false,
|
|
63
|
+
weishi: false,
|
|
64
|
+
huya: false,
|
|
65
|
+
haokan: false,
|
|
66
|
+
meipai: false,
|
|
67
|
+
twitter: false,
|
|
68
|
+
instagram: false,
|
|
69
|
+
doubao: false,
|
|
70
|
+
})
|
|
71
|
+
.description('各平台独立开关:是否优先使用专属 API(key 为平台名)'),
|
|
52
72
|
customApis: koishi_1.Schema.array(koishi_1.Schema.object({
|
|
53
73
|
platform: koishi_1.Schema.union([
|
|
54
74
|
koishi_1.Schema.const('bilibili').description('哔哩哔哩'),
|
|
@@ -70,8 +90,7 @@ exports.Config = koishi_1.Schema.intersect([
|
|
|
70
90
|
koishi_1.Schema.const('doubao').description('豆包'),
|
|
71
91
|
]).description('选择平台'),
|
|
72
92
|
apiUrl: koishi_1.Schema.string().description('API 地址'),
|
|
73
|
-
|
|
74
|
-
})).default([]).description('自定义平台专属 API,可覆盖默认专属 API,并可单独设定优先策略'),
|
|
93
|
+
})).default([]).description('自定义平台专属 API 地址,留空则使用内置默认专属 API'),
|
|
75
94
|
}).description('API 选择设置'),
|
|
76
95
|
koishi_1.Schema.object({
|
|
77
96
|
waitingTipText: koishi_1.Schema.string().default('正在解析视频,请稍候...').description('解析等待提示'),
|
|
@@ -109,27 +128,26 @@ function linkTypeParser(content) {
|
|
|
109
128
|
content = content.replace(/\\\//g, '/');
|
|
110
129
|
const rules = [
|
|
111
130
|
{ pattern: /bilibili\.com\/video\/([ab]v[0-9a-zA-Z]+)/gi, type: 'bilibili', buildUrl: (id) => `https://www.bilibili.com/video/${id}` },
|
|
112
|
-
{ pattern: /b23\.tv\/([0-9a-zA-
|
|
113
|
-
{ pattern: /bili(?:22|23|33)\.cn\/([0-9a-zA-
|
|
114
|
-
{ pattern: /bili2233\.cn\/([0-9a-zA-Z]{5,})/gi, type: 'bilibili', buildUrl: (id) => `https://bili2233.cn/${id}` },
|
|
131
|
+
{ pattern: /b23\.tv\/([0-9a-zA-Z_-]{5,})/gi, type: 'bilibili', buildUrl: (id) => `https://b23.tv/${id}` },
|
|
132
|
+
{ pattern: /bili(?:22|23|33|2233)\.cn\/([0-9a-zA-Z_-]{5,})/gi, type: 'bilibili', buildUrl: (id) => `https://bili${RegExp.$1 || ''}.cn/${id}` },
|
|
115
133
|
{ pattern: /douyin\.com\/video\/(\d{10,})/gi, type: 'douyin', buildUrl: (id) => `https://www.douyin.com/video/${id}` },
|
|
116
|
-
{ pattern: /v\.douyin\.com\/([0-9a-zA-
|
|
117
|
-
{ pattern: /kuaishou\.com\/short-video\/([0-9a-zA-
|
|
118
|
-
{ pattern: /v\.kuaishou\.com\/([0-9a-zA-
|
|
119
|
-
{ pattern: /xiaohongshu\.com\/discovery\/item\/([0-9a-zA-
|
|
120
|
-
{ pattern: /xhslink\.com\/([0-9a-zA-
|
|
121
|
-
{ pattern: /weibo\.com\/\d+\/([0-9a-zA-
|
|
122
|
-
{ pattern: /video\.weibo\.com\/show\?fid=([0-9a-zA-
|
|
134
|
+
{ pattern: /v\.douyin\.com\/([0-9a-zA-Z_-]{8,})/gi, type: 'douyin', buildUrl: (id) => `https://v.douyin.com/${id}/` },
|
|
135
|
+
{ pattern: /kuaishou\.com\/short-video\/([0-9a-zA-Z_-]{10,})/gi, type: 'kuaishou', buildUrl: (id) => `https://www.kuaishou.com/short-video/${id}` },
|
|
136
|
+
{ pattern: /v\.kuaishou\.com\/([0-9a-zA-Z_-]{8,})/gi, type: 'kuaishou', buildUrl: (id) => `https://v.kuaishou.com/${id}` },
|
|
137
|
+
{ pattern: /xiaohongshu\.com\/discovery\/item\/([0-9a-zA-Z_-]{10,})/gi, type: 'xiaohongshu', buildUrl: (id) => `https://www.xiaohongshu.com/discovery/item/${id}` },
|
|
138
|
+
{ pattern: /xhslink\.com\/([0-9a-zA-Z_-]{8,})/gi, type: 'xiaohongshu', buildUrl: (id) => `https://xhslink.com/${id}` },
|
|
139
|
+
{ pattern: /weibo\.com\/\d+\/([0-9a-zA-Z_-]{10,})/gi, type: 'weibo', buildUrl: (id) => `https://weibo.com/${id}` },
|
|
140
|
+
{ pattern: /video\.weibo\.com\/show\?fid=([0-9a-zA-Z_-]{10,})/gi, type: 'weibo', buildUrl: (id) => `https://video.weibo.com/show?fid=${id}` },
|
|
123
141
|
{ pattern: /ixigua\.com\/(\d{10,})/gi, type: 'xigua', buildUrl: (id) => `https://www.ixigua.com/${id}` },
|
|
124
142
|
{ pattern: /youtube\.com\/watch\?v=([a-zA-Z0-9_-]{11})/gi, type: 'youtube', buildUrl: (id) => `https://www.youtube.com/watch?v=${id}` },
|
|
125
143
|
{ pattern: /youtu\.be\/([a-zA-Z0-9_-]{11})/gi, type: 'youtube', buildUrl: (id) => `https://youtu.be/${id}` },
|
|
126
144
|
{ pattern: /tiktok\.com\/@[\w.]+\/video\/(\d{10,})/gi, type: 'tiktok', buildUrl: (id) => `https://www.tiktok.com/@user/video/${id}` },
|
|
127
|
-
{ pattern: /vm\.tiktok\.com\/([0-9a-zA-
|
|
145
|
+
{ pattern: /vm\.tiktok\.com\/([0-9a-zA-Z_-]{8,})/gi, type: 'tiktok', buildUrl: (id) => `https://vm.tiktok.com/${id}` },
|
|
128
146
|
{ pattern: /acfun\.cn\/v\/(ac\d{10,})/gi, type: 'acfun', buildUrl: (id) => `https://www.acfun.cn/v/${id}` },
|
|
129
147
|
{ pattern: /zhihu\.com\/video\/(\d{10,})/gi, type: 'zhihu', buildUrl: (id) => `https://www.zhihu.com/video/${id}` },
|
|
130
|
-
{ pattern: /weishi\.qq\.com\/weishi\/feed\/([0-9a-zA-
|
|
131
|
-
{ pattern: /huya\.com\/video\/([0-9a-zA-
|
|
132
|
-
{ pattern: /haokan\.baidu\.com\/v\?vid=([0-9a-zA-
|
|
148
|
+
{ pattern: /weishi\.qq\.com\/weishi\/feed\/([0-9a-zA-Z_-]{10,})/gi, type: 'weishi', buildUrl: (id) => `https://weishi.qq.com/weishi/feed/${id}` },
|
|
149
|
+
{ pattern: /huya\.com\/video\/([0-9a-zA-Z_-]{10,})/gi, type: 'huya', buildUrl: (id) => `https://www.huya.com/video/${id}` },
|
|
150
|
+
{ pattern: /haokan\.baidu\.com\/v\?vid=([0-9a-zA-Z_-]{10,})/gi, type: 'haokan', buildUrl: (id) => `https://haokan.baidu.com/v?vid=${id}` },
|
|
133
151
|
{ pattern: /meipai\.com\/media\/(\d{10,})/gi, type: 'meipai', buildUrl: (id) => `https://www.meipai.com/media/${id}` },
|
|
134
152
|
{ pattern: /twitter\.com\/\w+\/status\/(\d{10,})/gi, type: 'twitter', buildUrl: (id) => `https://twitter.com/i/status/${id}` },
|
|
135
153
|
{ pattern: /x\.com\/\w+\/status\/(\d{10,})/gi, type: 'twitter', buildUrl: (id) => `https://x.com/i/status/${id}` },
|
|
@@ -140,6 +158,7 @@ function linkTypeParser(content) {
|
|
|
140
158
|
const seen = new Set();
|
|
141
159
|
for (const rule of rules) {
|
|
142
160
|
let match;
|
|
161
|
+
rule.pattern.lastIndex = 0;
|
|
143
162
|
while ((match = rule.pattern.exec(content)) !== null) {
|
|
144
163
|
const id = match[1];
|
|
145
164
|
if (seen.has(id))
|
|
@@ -153,16 +172,12 @@ function linkTypeParser(content) {
|
|
|
153
172
|
}
|
|
154
173
|
function extractAllUrlsFromMessage(session) {
|
|
155
174
|
const content = session.content?.trim() || '';
|
|
156
|
-
const
|
|
157
|
-
const
|
|
158
|
-
if (textMatch)
|
|
159
|
-
rawUrls.push(...textMatch);
|
|
175
|
+
const matchedLinks = linkTypeParser(content);
|
|
176
|
+
const cardsContent = [];
|
|
160
177
|
if (session.elements) {
|
|
161
178
|
for (const elem of session.elements) {
|
|
162
179
|
if (elem.type === 'xml' && elem.data) {
|
|
163
|
-
|
|
164
|
-
if (matches)
|
|
165
|
-
rawUrls.push(...matches);
|
|
180
|
+
cardsContent.push(elem.data);
|
|
166
181
|
}
|
|
167
182
|
else if (elem.type === 'json' && elem.data) {
|
|
168
183
|
try {
|
|
@@ -172,9 +187,7 @@ function extractAllUrlsFromMessage(session) {
|
|
|
172
187
|
return;
|
|
173
188
|
for (const val of Object.values(obj)) {
|
|
174
189
|
if (typeof val === 'string') {
|
|
175
|
-
|
|
176
|
-
if (matches)
|
|
177
|
-
rawUrls.push(...matches);
|
|
190
|
+
cardsContent.push(val);
|
|
178
191
|
}
|
|
179
192
|
else if (typeof val === 'object')
|
|
180
193
|
extract(val);
|
|
@@ -186,18 +199,16 @@ function extractAllUrlsFromMessage(session) {
|
|
|
186
199
|
}
|
|
187
200
|
}
|
|
188
201
|
}
|
|
202
|
+
for (const cardContent of cardsContent) {
|
|
203
|
+
const cardLinks = linkTypeParser(cardContent);
|
|
204
|
+
matchedLinks.push(...cardLinks);
|
|
205
|
+
}
|
|
189
206
|
const seen = new Set();
|
|
190
207
|
const result = [];
|
|
191
|
-
for (const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
const matches = linkTypeParser(cleanedUrl);
|
|
196
|
-
for (const match of matches) {
|
|
197
|
-
if (!seen.has(match.url)) {
|
|
198
|
-
seen.add(match.url);
|
|
199
|
-
result.push(match);
|
|
200
|
-
}
|
|
208
|
+
for (const link of matchedLinks) {
|
|
209
|
+
if (!seen.has(link.url)) {
|
|
210
|
+
seen.add(link.url);
|
|
211
|
+
result.push(link);
|
|
201
212
|
}
|
|
202
213
|
}
|
|
203
214
|
return result;
|
|
@@ -484,7 +495,19 @@ async function downloadVideoFile(videoUrl, tempDir, timeout, maxSizeMB) {
|
|
|
484
495
|
}
|
|
485
496
|
});
|
|
486
497
|
try {
|
|
487
|
-
await (
|
|
498
|
+
await new Promise((resolve, reject) => {
|
|
499
|
+
const stream = require('stream');
|
|
500
|
+
const pipeline = stream.promises?.pipeline || require('util').promisify(stream.pipeline);
|
|
501
|
+
if (typeof pipeline === 'function') {
|
|
502
|
+
pipeline(response.data, writer).then(resolve).catch(reject);
|
|
503
|
+
}
|
|
504
|
+
else {
|
|
505
|
+
response.data.pipe(writer);
|
|
506
|
+
writer.on('finish', resolve);
|
|
507
|
+
writer.on('error', reject);
|
|
508
|
+
response.data.on('error', reject);
|
|
509
|
+
}
|
|
510
|
+
});
|
|
488
511
|
debugLog('INFO', `视频下载完成,大小: ${Math.round(downloadedSize / 1024 / 1024)}MB`);
|
|
489
512
|
return filePath;
|
|
490
513
|
}
|
|
@@ -533,11 +556,12 @@ function apply(ctx, config) {
|
|
|
533
556
|
const backupSupportedPlatforms = new Set(['douyin', 'xiaohongshu', 'instagram', 'jimeng']);
|
|
534
557
|
function getPlatformConfig(type) {
|
|
535
558
|
const custom = config.customApis?.find((item) => item.platform === type);
|
|
559
|
+
let apiUrl = defaultDedicatedApis[type] || null;
|
|
536
560
|
if (custom && custom.apiUrl) {
|
|
537
|
-
|
|
561
|
+
apiUrl = custom.apiUrl;
|
|
538
562
|
}
|
|
539
|
-
const
|
|
540
|
-
return { apiUrl
|
|
563
|
+
const dedicatedFirst = config.platformDedicatedFirst?.[type] ?? config.useDedicatedApiFirst ?? false;
|
|
564
|
+
return { apiUrl, dedicatedFirst };
|
|
541
565
|
}
|
|
542
566
|
async function fetchApi(url, type) {
|
|
543
567
|
const cacheKey = url;
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -56,8 +56,9 @@ This is a **multi-platform video/image parsing plugin** developed for the Koishi
|
|
|
56
56
|
|--------|------|--------|------|
|
|
57
57
|
| `primaryApiUrl` | string | `https://api.bugpk.com/api/short_videos` | 主 API 地址,解析时优先使用 |
|
|
58
58
|
| `backupApiUrl` | string | `https://api.bugpk.com/api/svparse` | 备用主 API 地址,仅支持抖音、小红书、Instagram、即梦平台解析 |
|
|
59
|
-
| `useDedicatedApiFirst` | boolean | false |
|
|
60
|
-
| `
|
|
59
|
+
| `useDedicatedApiFirst` | boolean | false | 全局默认值:是否优先使用平台专属 API,各平台可单独覆盖该行为 |
|
|
60
|
+
| `platformDedicatedFirst` | object | 见下方说明 | 各平台独立开关:是否优先使用专属 API(key 为平台名)。支持的 key:bilibili、douyin、kuaishou、xiaohongshu、weibo、xigua、youtube、tiktok、acfun、zhihu、weishi、huya、haokan、meipai、twitter、instagram、doubao |
|
|
61
|
+
| `customApis` | array | [] | 自定义平台专属 API 列表。每项包含:`platform`(平台类型)、`apiUrl`(API 地址)。可覆盖内置默认专属 API |
|
|
61
62
|
|
|
62
63
|
### 错误与重试设置
|
|
63
64
|
| 配置项 | 类型 | 默认值 | 说明 |
|