koishi-plugin-video-parser-all 1.1.1 → 1.1.2
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 +38 -4
- package/lib/index.js +53 -78
- package/package.json +1 -1
- package/readme.md +1 -2
package/lib/index.d.ts
CHANGED
|
@@ -28,8 +28,25 @@ export declare const Config: Schema<{
|
|
|
28
28
|
} & {
|
|
29
29
|
primaryApiUrl?: string | null | undefined;
|
|
30
30
|
backupApiUrl?: string | null | undefined;
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
platformDedicatedFirst?: ({
|
|
32
|
+
bilibili?: boolean | null | undefined;
|
|
33
|
+
douyin?: boolean | null | undefined;
|
|
34
|
+
kuaishou?: boolean | null | undefined;
|
|
35
|
+
xiaohongshu?: boolean | null | undefined;
|
|
36
|
+
weibo?: boolean | null | undefined;
|
|
37
|
+
xigua?: boolean | null | undefined;
|
|
38
|
+
youtube?: boolean | null | undefined;
|
|
39
|
+
tiktok?: boolean | null | undefined;
|
|
40
|
+
acfun?: boolean | null | undefined;
|
|
41
|
+
zhihu?: boolean | null | undefined;
|
|
42
|
+
weishi?: boolean | null | undefined;
|
|
43
|
+
huya?: boolean | null | undefined;
|
|
44
|
+
haokan?: boolean | null | undefined;
|
|
45
|
+
meipai?: boolean | null | undefined;
|
|
46
|
+
twitter?: boolean | null | undefined;
|
|
47
|
+
instagram?: boolean | null | undefined;
|
|
48
|
+
doubao?: boolean | null | undefined;
|
|
49
|
+
} & import("cosmokit").Dict) | null | undefined;
|
|
33
50
|
customApis?: ({
|
|
34
51
|
platform?: "bilibili" | "douyin" | "kuaishou" | "xiaohongshu" | "weibo" | "xigua" | "youtube" | "tiktok" | "acfun" | "zhihu" | "weishi" | "huya" | "haokan" | "meipai" | "twitter" | "instagram" | "doubao" | null | undefined;
|
|
35
52
|
apiUrl?: string | null | undefined;
|
|
@@ -68,8 +85,25 @@ export declare const Config: Schema<{
|
|
|
68
85
|
} & {
|
|
69
86
|
primaryApiUrl: string;
|
|
70
87
|
backupApiUrl: string;
|
|
71
|
-
|
|
72
|
-
|
|
88
|
+
platformDedicatedFirst: Schemastery.ObjectT<{
|
|
89
|
+
bilibili: Schema<boolean, boolean>;
|
|
90
|
+
douyin: Schema<boolean, boolean>;
|
|
91
|
+
kuaishou: Schema<boolean, boolean>;
|
|
92
|
+
xiaohongshu: Schema<boolean, boolean>;
|
|
93
|
+
weibo: Schema<boolean, boolean>;
|
|
94
|
+
xigua: Schema<boolean, boolean>;
|
|
95
|
+
youtube: Schema<boolean, boolean>;
|
|
96
|
+
tiktok: Schema<boolean, boolean>;
|
|
97
|
+
acfun: Schema<boolean, boolean>;
|
|
98
|
+
zhihu: Schema<boolean, boolean>;
|
|
99
|
+
weishi: Schema<boolean, boolean>;
|
|
100
|
+
huya: Schema<boolean, boolean>;
|
|
101
|
+
haokan: Schema<boolean, boolean>;
|
|
102
|
+
meipai: Schema<boolean, boolean>;
|
|
103
|
+
twitter: Schema<boolean, boolean>;
|
|
104
|
+
instagram: Schema<boolean, boolean>;
|
|
105
|
+
doubao: Schema<boolean, boolean>;
|
|
106
|
+
}>;
|
|
73
107
|
customApis: Schemastery.ObjectT<{
|
|
74
108
|
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">;
|
|
75
109
|
apiUrl: Schema<string, string>;
|
package/lib/index.js
CHANGED
|
@@ -10,6 +10,7 @@ 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");
|
|
13
14
|
const lru_cache_1 = require("lru-cache");
|
|
14
15
|
exports.name = 'video-parser-all';
|
|
15
16
|
exports.Config = koishi_1.Schema.intersect([
|
|
@@ -47,28 +48,25 @@ exports.Config = koishi_1.Schema.intersect([
|
|
|
47
48
|
koishi_1.Schema.object({
|
|
48
49
|
primaryApiUrl: koishi_1.Schema.string().default('https://api.bugpk.com/api/short_videos').description('主 API 地址'),
|
|
49
50
|
backupApiUrl: koishi_1.Schema.string().default('https://api.bugpk.com/api/svparse').description('备用主 API 地址(仅支持抖音/小红书/ins/即梦)'),
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
.default(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
doubao: false,
|
|
70
|
-
})
|
|
71
|
-
.description('各平台独立开关:是否优先使用专属 API(key 为平台名)'),
|
|
51
|
+
platformDedicatedFirst: koishi_1.Schema.object({
|
|
52
|
+
bilibili: koishi_1.Schema.boolean().default(false).description('哔哩哔哩'),
|
|
53
|
+
douyin: koishi_1.Schema.boolean().default(false).description('抖音'),
|
|
54
|
+
kuaishou: koishi_1.Schema.boolean().default(false).description('快手'),
|
|
55
|
+
xiaohongshu: koishi_1.Schema.boolean().default(false).description('小红书'),
|
|
56
|
+
weibo: koishi_1.Schema.boolean().default(false).description('微博'),
|
|
57
|
+
xigua: koishi_1.Schema.boolean().default(false).description('西瓜视频'),
|
|
58
|
+
youtube: koishi_1.Schema.boolean().default(false).description('YouTube'),
|
|
59
|
+
tiktok: koishi_1.Schema.boolean().default(false).description('TikTok'),
|
|
60
|
+
acfun: koishi_1.Schema.boolean().default(false).description('AcFun'),
|
|
61
|
+
zhihu: koishi_1.Schema.boolean().default(false).description('知乎'),
|
|
62
|
+
weishi: koishi_1.Schema.boolean().default(false).description('微视'),
|
|
63
|
+
huya: koishi_1.Schema.boolean().default(false).description('虎牙'),
|
|
64
|
+
haokan: koishi_1.Schema.boolean().default(false).description('好看视频'),
|
|
65
|
+
meipai: koishi_1.Schema.boolean().default(false).description('美拍'),
|
|
66
|
+
twitter: koishi_1.Schema.boolean().default(false).description('Twitter/X'),
|
|
67
|
+
instagram: koishi_1.Schema.boolean().default(false).description('Instagram'),
|
|
68
|
+
doubao: koishi_1.Schema.boolean().default(false).description('豆包'),
|
|
69
|
+
}).description('各平台独立开关:是否优先使用专属 API'),
|
|
72
70
|
customApis: koishi_1.Schema.array(koishi_1.Schema.object({
|
|
73
71
|
platform: koishi_1.Schema.union([
|
|
74
72
|
koishi_1.Schema.const('bilibili').description('哔哩哔哩'),
|
|
@@ -127,32 +125,32 @@ const urlCache = new lru_cache_1.LRUCache({
|
|
|
127
125
|
function linkTypeParser(content) {
|
|
128
126
|
content = content.replace(/\\\//g, '/');
|
|
129
127
|
const rules = [
|
|
130
|
-
{ pattern: /bilibili\.com\/video\/([ab]v[0-9a-zA-
|
|
131
|
-
{ pattern: /b23\.tv\/
|
|
132
|
-
{ pattern: /bili
|
|
133
|
-
{ pattern: /douyin\.com\/video
|
|
134
|
-
{ pattern: /v\.douyin\.com\/
|
|
135
|
-
{ pattern: /kuaishou\.com\/short-video\/
|
|
136
|
-
{ pattern: /v\.kuaishou\.com\/
|
|
137
|
-
{ pattern: /xiaohongshu\.com\/discovery\/item\/
|
|
138
|
-
{ pattern: /xhslink\.com\/
|
|
139
|
-
{ pattern: /weibo\.com\/\d+\/
|
|
140
|
-
{ pattern: /video\.weibo\.com\/show\?fid=
|
|
141
|
-
{ pattern: /ixigua\.com
|
|
142
|
-
{ pattern: /youtube\.com\/watch\?v=
|
|
143
|
-
{ pattern: /youtu\.be\/
|
|
144
|
-
{ pattern: /tiktok\.com\/@[\w.]+\/video
|
|
145
|
-
{ pattern: /vm\.tiktok\.com\/
|
|
146
|
-
{ pattern: /acfun\.cn\/v\/
|
|
147
|
-
{ pattern: /zhihu\.com\/video
|
|
148
|
-
{ pattern: /weishi\.qq\.com\/weishi\/feed\/
|
|
149
|
-
{ pattern: /huya\.com\/video\/
|
|
150
|
-
{ pattern: /haokan\.baidu\.com\/v\?vid=
|
|
151
|
-
{ pattern: /meipai\.com\/media
|
|
152
|
-
{ pattern: /twitter\.com\/\w+\/status
|
|
153
|
-
{ pattern: /x\.com\/\w+\/status
|
|
154
|
-
{ pattern: /instagram\.com\/p\/
|
|
155
|
-
{ pattern: /doubao\.com\/video
|
|
128
|
+
{ pattern: /https?:\/\/(?:www\.)?bilibili\.com\/video\/([ab]v[0-9a-zA-Z_-]+)/gi, type: 'bilibili' },
|
|
129
|
+
{ pattern: /https?:\/\/b23\.tv\/[0-9a-zA-Z_-]{5,}/gi, type: 'bilibili' },
|
|
130
|
+
{ pattern: /https?:\/\/bili\d+\.cn\/[0-9a-zA-Z_-]{5,}/gi, type: 'bilibili' },
|
|
131
|
+
{ pattern: /https?:\/\/(?:www\.)?douyin\.com\/video\/\d{10,}/gi, type: 'douyin' },
|
|
132
|
+
{ pattern: /https?:\/\/v\.douyin\.com\/[0-9a-zA-Z_-]{8,}/gi, type: 'douyin' },
|
|
133
|
+
{ pattern: /https?:\/\/(?:www\.)?kuaishou\.com\/short-video\/[0-9a-zA-Z_-]{10,}/gi, type: 'kuaishou' },
|
|
134
|
+
{ pattern: /https?:\/\/v\.kuaishou\.com\/[0-9a-zA-Z_-]{8,}/gi, type: 'kuaishou' },
|
|
135
|
+
{ pattern: /https?:\/\/(?:www\.)?xiaohongshu\.com\/discovery\/item\/[0-9a-zA-Z_-]{10,}/gi, type: 'xiaohongshu' },
|
|
136
|
+
{ pattern: /https?:\/\/xhslink\.com\/[0-9a-zA-Z_-]{8,}/gi, type: 'xiaohongshu' },
|
|
137
|
+
{ pattern: /https?:\/\/weibo\.com\/\d+\/[0-9a-zA-Z_-]{10,}/gi, type: 'weibo' },
|
|
138
|
+
{ pattern: /https?:\/\/video\.weibo\.com\/show\?fid=[0-9a-zA-Z_-]{10,}/gi, type: 'weibo' },
|
|
139
|
+
{ pattern: /https?:\/\/(?:www\.)?ixigua\.com\/\d{10,}/gi, type: 'xigua' },
|
|
140
|
+
{ pattern: /https?:\/\/(?:www\.)?youtube\.com\/watch\?v=[a-zA-Z0-9_-]{11}/gi, type: 'youtube' },
|
|
141
|
+
{ pattern: /https?:\/\/youtu\.be\/[a-zA-Z0-9_-]{11}/gi, type: 'youtube' },
|
|
142
|
+
{ pattern: /https?:\/\/(?:www\.)?tiktok\.com\/@[\w.]+\/video\/\d{10,}/gi, type: 'tiktok' },
|
|
143
|
+
{ pattern: /https?:\/\/vm\.tiktok\.com\/[0-9a-zA-Z_-]{8,}/gi, type: 'tiktok' },
|
|
144
|
+
{ pattern: /https?:\/\/(?:www\.)?acfun\.cn\/v\/ac\d{10,}/gi, type: 'acfun' },
|
|
145
|
+
{ pattern: /https?:\/\/(?:www\.)?zhihu\.com\/video\/\d{10,}/gi, type: 'zhihu' },
|
|
146
|
+
{ pattern: /https?:\/\/weishi\.qq\.com\/weishi\/feed\/[0-9a-zA-Z_-]{10,}/gi, type: 'weishi' },
|
|
147
|
+
{ pattern: /https?:\/\/(?:www\.)?huya\.com\/video\/[0-9a-zA-Z_-]{10,}/gi, type: 'huya' },
|
|
148
|
+
{ pattern: /https?:\/\/haokan\.baidu\.com\/v\?vid=[0-9a-zA-Z_-]{10,}/gi, type: 'haokan' },
|
|
149
|
+
{ pattern: /https?:\/\/(?:www\.)?meipai\.com\/media\/\d{10,}/gi, type: 'meipai' },
|
|
150
|
+
{ pattern: /https?:\/\/twitter\.com\/\w+\/status\/\d{10,}/gi, type: 'twitter' },
|
|
151
|
+
{ pattern: /https?:\/\/x\.com\/\w+\/status\/\d{10,}/gi, type: 'twitter' },
|
|
152
|
+
{ pattern: /https?:\/\/(?:www\.)?instagram\.com\/p\/[0-9a-zA-Z_-]{10,}/gi, type: 'instagram' },
|
|
153
|
+
{ pattern: /https?:\/\/(?:www\.)?doubao\.com\/video\/\d{10,}/gi, type: 'doubao' },
|
|
156
154
|
];
|
|
157
155
|
const matches = [];
|
|
158
156
|
const seen = new Set();
|
|
@@ -160,12 +158,11 @@ function linkTypeParser(content) {
|
|
|
160
158
|
let match;
|
|
161
159
|
rule.pattern.lastIndex = 0;
|
|
162
160
|
while ((match = rule.pattern.exec(content)) !== null) {
|
|
163
|
-
const
|
|
164
|
-
if (seen.has(
|
|
161
|
+
const url = match[0];
|
|
162
|
+
if (seen.has(url))
|
|
165
163
|
continue;
|
|
166
|
-
seen.add(
|
|
167
|
-
|
|
168
|
-
matches.push({ type: rule.type, url, id });
|
|
164
|
+
seen.add(url);
|
|
165
|
+
matches.push({ type: rule.type, url, id: match[1] || url });
|
|
169
166
|
}
|
|
170
167
|
}
|
|
171
168
|
return matches;
|
|
@@ -484,31 +481,9 @@ async function downloadVideoFile(videoUrl, tempDir, timeout, maxSizeMB) {
|
|
|
484
481
|
await promises_1.default.unlink(filePath).catch(() => { });
|
|
485
482
|
throw new Error(`视频文件过大(${Math.round(contentLength / 1024 / 1024)}MB),超过限制(${maxSizeMB}MB)`);
|
|
486
483
|
}
|
|
487
|
-
let downloadedSize = 0;
|
|
488
|
-
response.data.on('data', (chunk) => {
|
|
489
|
-
downloadedSize += chunk.length;
|
|
490
|
-
if (maxSizeMB > 0 && downloadedSize > maxSizeBytes) {
|
|
491
|
-
response.data.destroy();
|
|
492
|
-
writer.destroy();
|
|
493
|
-
promises_1.default.unlink(filePath).catch(() => { });
|
|
494
|
-
throw new Error(`视频文件过大,超过限制(${maxSizeMB}MB)`);
|
|
495
|
-
}
|
|
496
|
-
});
|
|
497
484
|
try {
|
|
498
|
-
await
|
|
499
|
-
|
|
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
|
-
});
|
|
511
|
-
debugLog('INFO', `视频下载完成,大小: ${Math.round(downloadedSize / 1024 / 1024)}MB`);
|
|
485
|
+
await (0, promises_2.pipeline)(response.data, writer);
|
|
486
|
+
debugLog('INFO', `视频下载完成`);
|
|
512
487
|
return filePath;
|
|
513
488
|
}
|
|
514
489
|
catch (e) {
|
|
@@ -560,7 +535,7 @@ function apply(ctx, config) {
|
|
|
560
535
|
if (custom && custom.apiUrl) {
|
|
561
536
|
apiUrl = custom.apiUrl;
|
|
562
537
|
}
|
|
563
|
-
const dedicatedFirst = config.platformDedicatedFirst?.[type] ??
|
|
538
|
+
const dedicatedFirst = config.platformDedicatedFirst?.[type] ?? false;
|
|
564
539
|
return { apiUrl, dedicatedFirst };
|
|
565
540
|
}
|
|
566
541
|
async function fetchApi(url, type) {
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -56,8 +56,7 @@ 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
|
-
| `
|
|
60
|
-
| `platformDedicatedFirst` | object | 见下方说明 | 各平台独立开关:是否优先使用专属 API(key 为平台名)。支持的 key:bilibili、douyin、kuaishou、xiaohongshu、weibo、xigua、youtube、tiktok、acfun、zhihu、weishi、huya、haokan、meipai、twitter、instagram、doubao |
|
|
59
|
+
| `platformDedicatedFirst` | object | 各平台均为 `false` | 各平台独立开关:是否优先使用平台专属 API。对象键为平台标识(英文),值为布尔值。支持的键:`bilibili`(哔哩哔哩)、`douyin`(抖音)、`kuaishou`(快手)、`xiaohongshu`(小红书)、`weibo`(微博)、`xigua`(西瓜视频)、`youtube`(YouTube)、`tiktok`(TikTok)、`acfun`(AcFun)、`zhihu`(知乎)、`weishi`(微视)、`huya`(虎牙)、`haokan`(好看视频)、`meipai`(美拍)、`twitter`(Twitter/X)、`instagram`(Instagram)、`doubao`(豆包) |
|
|
61
60
|
| `customApis` | array | [] | 自定义平台专属 API 列表。每项包含:`platform`(平台类型)、`apiUrl`(API 地址)。可覆盖内置默认专属 API |
|
|
62
61
|
|
|
63
62
|
### 错误与重试设置
|