koishi-plugin-video-parser-all 1.0.5 → 1.0.7
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 +10 -0
- package/lib/index.js +123 -184
- package/package.json +1 -1
- package/readme.md +0 -1
package/lib/index.d.ts
CHANGED
|
@@ -25,6 +25,11 @@ export declare const Config: Schema<{
|
|
|
25
25
|
retryInterval?: number | null | undefined;
|
|
26
26
|
} & {
|
|
27
27
|
enableForward?: boolean | null | undefined;
|
|
28
|
+
} & {
|
|
29
|
+
primaryApiUrl?: string | null | undefined;
|
|
30
|
+
backupApiUrl?: string | null | undefined;
|
|
31
|
+
useDedicatedApiFirst?: boolean | null | undefined;
|
|
32
|
+
customApiUrls?: import("cosmokit").Dict<string, string> | null | undefined;
|
|
28
33
|
} & {
|
|
29
34
|
waitingTipText?: string | null | undefined;
|
|
30
35
|
unsupportedPlatformText?: string | null | undefined;
|
|
@@ -56,6 +61,11 @@ export declare const Config: Schema<{
|
|
|
56
61
|
retryInterval: number;
|
|
57
62
|
} & {
|
|
58
63
|
enableForward: boolean;
|
|
64
|
+
} & {
|
|
65
|
+
primaryApiUrl: string;
|
|
66
|
+
backupApiUrl: string;
|
|
67
|
+
useDedicatedApiFirst: boolean;
|
|
68
|
+
customApiUrls: import("cosmokit").Dict<string, string>;
|
|
59
69
|
} & {
|
|
60
70
|
waitingTipText: string;
|
|
61
71
|
unsupportedPlatformText: string;
|
package/lib/index.js
CHANGED
|
@@ -45,6 +45,12 @@ exports.Config = koishi_1.Schema.intersect([
|
|
|
45
45
|
koishi_1.Schema.object({
|
|
46
46
|
enableForward: koishi_1.Schema.boolean().default(false).description('启用合并转发(仅 OneBot 平台)'),
|
|
47
47
|
}).description('发送方式设置'),
|
|
48
|
+
koishi_1.Schema.object({
|
|
49
|
+
primaryApiUrl: koishi_1.Schema.string().default('https://api.bugpk.com/api/short_videos').description('主 API 地址'),
|
|
50
|
+
backupApiUrl: koishi_1.Schema.string().default('https://api.bugpk.com/api/svparse').description('备用主 API 地址(仅支持抖音/小红书/ins/即梦)'),
|
|
51
|
+
useDedicatedApiFirst: koishi_1.Schema.boolean().default(false).description('优先使用平台专属 API,失败后回退到通用 API'),
|
|
52
|
+
customApiUrls: koishi_1.Schema.dict(koishi_1.Schema.string()).default({}).description('自定义专属 API 地址,key 为平台类型(如 bilibili,douyin,doubao),value 为完整 API 地址,留空则使用内置默认专属 API'),
|
|
53
|
+
}).description('API 选择设置'),
|
|
48
54
|
koishi_1.Schema.object({
|
|
49
55
|
waitingTipText: koishi_1.Schema.string().default('正在解析视频,请稍候...').description('解析等待提示'),
|
|
50
56
|
unsupportedPlatformText: koishi_1.Schema.string().default('不支持该平台链接').description('不支持的平台提示'),
|
|
@@ -118,98 +124,9 @@ function linkTypeParser(content) {
|
|
|
118
124
|
}
|
|
119
125
|
return matches;
|
|
120
126
|
}
|
|
121
|
-
function extractUrl(content) {
|
|
122
|
-
if (!content)
|
|
123
|
-
return [];
|
|
124
|
-
const urlMatches = content.match(/https?:\/\/[^\s<>"'(){}[\]]+/gi) || [];
|
|
125
|
-
return urlMatches.filter(url => {
|
|
126
|
-
try {
|
|
127
|
-
const urlObj = new URL(url);
|
|
128
|
-
const hostname = urlObj.hostname.toLowerCase();
|
|
129
|
-
if (hostname.includes('multimedia.nt.qq.com.cn') ||
|
|
130
|
-
hostname.includes('grouptalk.qq.com') ||
|
|
131
|
-
hostname.includes('qpic.cn') ||
|
|
132
|
-
hostname.includes('qlogo.cn')) {
|
|
133
|
-
return false;
|
|
134
|
-
}
|
|
135
|
-
if (hostname === 'v.douyin.com' && urlObj.pathname.length < 3)
|
|
136
|
-
return false;
|
|
137
|
-
if (hostname === 'www.douyin.com' && urlObj.pathname === '/')
|
|
138
|
-
return false;
|
|
139
|
-
return true;
|
|
140
|
-
}
|
|
141
|
-
catch {
|
|
142
|
-
return false;
|
|
143
|
-
}
|
|
144
|
-
}).map(url => {
|
|
145
|
-
return url.replace(/[.,;:!?)]+$/, '');
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
127
|
function extractAllUrlsFromMessage(session) {
|
|
149
128
|
const content = session.content?.trim() || '';
|
|
150
|
-
|
|
151
|
-
const linkMatches = linkTypeParser(content);
|
|
152
|
-
if (linkMatches.length > 0) {
|
|
153
|
-
for (const match of linkMatches) {
|
|
154
|
-
urls.push(match.url);
|
|
155
|
-
}
|
|
156
|
-
return [...new Set(urls)];
|
|
157
|
-
}
|
|
158
|
-
if (content) {
|
|
159
|
-
const textUrls = extractUrl(content);
|
|
160
|
-
urls.push(...textUrls);
|
|
161
|
-
}
|
|
162
|
-
if (session.elements) {
|
|
163
|
-
for (const elem of session.elements) {
|
|
164
|
-
if (elem.type === 'xml' && elem.data) {
|
|
165
|
-
const urlRegex = /https?:\/\/[^\s<>"'(){}[\]]+/gi;
|
|
166
|
-
let match;
|
|
167
|
-
while ((match = urlRegex.exec(elem.data)) !== null) {
|
|
168
|
-
const cleanUrl = match[0].replace(/[.,;:!?)]+$/, '');
|
|
169
|
-
urls.push(cleanUrl);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
else if (elem.type === 'json' && elem.data) {
|
|
173
|
-
try {
|
|
174
|
-
const json = JSON.parse(elem.data);
|
|
175
|
-
const extractFromObject = (obj) => {
|
|
176
|
-
if (!obj || typeof obj !== 'object')
|
|
177
|
-
return;
|
|
178
|
-
for (const val of Object.values(obj)) {
|
|
179
|
-
if (typeof val === 'string') {
|
|
180
|
-
const match = val.match(/https?:\/\/[^\s<>"'(){}[\]]+/gi);
|
|
181
|
-
if (match) {
|
|
182
|
-
match.forEach(url => {
|
|
183
|
-
const cleanUrl = url.replace(/[.,;:!?)]+$/, '');
|
|
184
|
-
urls.push(cleanUrl);
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
else if (typeof val === 'object')
|
|
189
|
-
extractFromObject(val);
|
|
190
|
-
}
|
|
191
|
-
};
|
|
192
|
-
extractFromObject(json);
|
|
193
|
-
}
|
|
194
|
-
catch (e) {
|
|
195
|
-
debugLog('WARN', '解析JSON卡片失败:', e);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
return [...new Set(urls)].filter(url => {
|
|
201
|
-
try {
|
|
202
|
-
const urlObj = new URL(url);
|
|
203
|
-
if (urlObj.hostname === 'v.douyin.com' && urlObj.pathname.length < 3)
|
|
204
|
-
return false;
|
|
205
|
-
if (urlObj.hostname === 'www.douyin.com' && urlObj.pathname === '/')
|
|
206
|
-
return false;
|
|
207
|
-
return true;
|
|
208
|
-
}
|
|
209
|
-
catch {
|
|
210
|
-
return false;
|
|
211
|
-
}
|
|
212
|
-
});
|
|
129
|
+
return linkTypeParser(content);
|
|
213
130
|
}
|
|
214
131
|
function cleanUrl(url) {
|
|
215
132
|
try {
|
|
@@ -381,10 +298,6 @@ function parseApiResponse(raw, maxDescLen) {
|
|
|
381
298
|
else if (extra.create_time) {
|
|
382
299
|
publishTime = extra.create_time * 1000;
|
|
383
300
|
}
|
|
384
|
-
debugLog('DEBUG', '解析后的数据:', {
|
|
385
|
-
type, title, author, video: video.substring(0, 100) + '...',
|
|
386
|
-
images: images.length, live_photo: live_photo.length
|
|
387
|
-
});
|
|
388
301
|
return {
|
|
389
302
|
type, title, desc, author, uid, avatar, cover,
|
|
390
303
|
video, videos, images, live_photo, music,
|
|
@@ -534,48 +447,89 @@ function apply(ctx, config) {
|
|
|
534
447
|
'Content-Type': 'application/x-www-form-urlencoded'
|
|
535
448
|
}
|
|
536
449
|
});
|
|
537
|
-
|
|
450
|
+
const defaultDedicatedApis = {
|
|
451
|
+
bilibili: 'https://api.bugpk.com/api/bilibili',
|
|
452
|
+
douyin: 'https://api.bugpk.com/api/douyin',
|
|
453
|
+
doubao: 'https://api.bugpk.com/api/dbvideos',
|
|
454
|
+
kuaishou: 'https://api.bugpk.com/api/kuaishou',
|
|
455
|
+
xiaohongshu: 'https://api.bugpk.com/api/xhs',
|
|
456
|
+
jimeng: 'https://api.bugpk.com/api/jimengai',
|
|
457
|
+
toutiao: 'https://api.bugpk.com/api/toutiao',
|
|
458
|
+
weibo: 'https://api.bugpk.com/api/weibo',
|
|
459
|
+
huya: 'https://api.bugpk.com/api/huya',
|
|
460
|
+
pipigx: 'https://api.bugpk.com/api/pipigx',
|
|
461
|
+
pipixia: 'https://api.bugpk.com/api/pipixia',
|
|
462
|
+
zuiyou: 'https://api.bugpk.com/api/zuiyou',
|
|
463
|
+
};
|
|
464
|
+
const backupSupportedPlatforms = new Set(['douyin', 'xiaohongshu', 'instagram', 'jimeng']);
|
|
465
|
+
function getDedicatedApiUrl(type) {
|
|
466
|
+
if (config.customApiUrls && config.customApiUrls[type]) {
|
|
467
|
+
return config.customApiUrls[type];
|
|
468
|
+
}
|
|
469
|
+
return defaultDedicatedApis[type] || null;
|
|
470
|
+
}
|
|
471
|
+
async function fetchApi(url, type) {
|
|
538
472
|
const cacheKey = url;
|
|
539
473
|
const cached = urlCache.get(cacheKey);
|
|
540
474
|
if (cached && cached.expire > Date.now()) {
|
|
541
475
|
debugLog('DEBUG', `使用缓存: ${url}`);
|
|
542
476
|
return cached.data;
|
|
543
477
|
}
|
|
544
|
-
|
|
478
|
+
const dedicatedApiUrl = getDedicatedApiUrl(type);
|
|
479
|
+
const primaryApi = config.primaryApiUrl || 'https://api.bugpk.com/api/short_videos';
|
|
480
|
+
const backupApi = config.backupApiUrl || 'https://api.bugpk.com/api/svparse';
|
|
481
|
+
const backupAllowed = backupSupportedPlatforms.has(type);
|
|
482
|
+
const apiList = [];
|
|
483
|
+
if (config.useDedicatedApiFirst) {
|
|
484
|
+
if (dedicatedApiUrl)
|
|
485
|
+
apiList.push({ url: dedicatedApiUrl, label: `专属API(${type})` });
|
|
486
|
+
apiList.push({ url: primaryApi, label: '默认主API' });
|
|
487
|
+
if (backupAllowed)
|
|
488
|
+
apiList.push({ url: backupApi, label: '备用主API' });
|
|
489
|
+
if (dedicatedApiUrl) {
|
|
490
|
+
// already tried dedicated first, don't repeat
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
else {
|
|
494
|
+
apiList.push({ url: primaryApi, label: '默认主API' });
|
|
495
|
+
if (backupAllowed)
|
|
496
|
+
apiList.push({ url: backupApi, label: '备用主API' });
|
|
497
|
+
if (dedicatedApiUrl)
|
|
498
|
+
apiList.push({ url: dedicatedApiUrl, label: `专属API(${type})` });
|
|
499
|
+
}
|
|
545
500
|
let lastError = null;
|
|
546
|
-
for (
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
debugLog('DEBUG', `API响应状态: ${res.status}`);
|
|
553
|
-
if (res.data && (res.data.code === 200 || res.data.code === 0)) {
|
|
554
|
-
const parsed = parseApiResponse(res.data, config.maxDescLength);
|
|
555
|
-
urlCache.set(cacheKey, {
|
|
556
|
-
data: parsed,
|
|
557
|
-
expire: Date.now() + 10 * 60 * 1000
|
|
501
|
+
for (const api of apiList) {
|
|
502
|
+
for (let attempt = 0; attempt <= config.retryTimes; attempt++) {
|
|
503
|
+
try {
|
|
504
|
+
const res = await http.get(api.url, {
|
|
505
|
+
params: { url },
|
|
506
|
+
timeout: config.timeout
|
|
558
507
|
});
|
|
559
|
-
|
|
508
|
+
if (res.data && (res.data.code === 200 || res.data.code === 0)) {
|
|
509
|
+
const parsed = parseApiResponse(res.data, config.maxDescLength);
|
|
510
|
+
urlCache.set(cacheKey, { data: parsed, expire: Date.now() + 10 * 60 * 1000 });
|
|
511
|
+
return parsed;
|
|
512
|
+
}
|
|
513
|
+
throw new Error(res.data?.msg || `API返回错误码: ${res.data?.code}`);
|
|
560
514
|
}
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
await delay(config.retryInterval);
|
|
515
|
+
catch (error) {
|
|
516
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
517
|
+
debugLog('ERROR', `${api.label} 第${attempt + 1}次请求失败: ${lastError.message}`);
|
|
518
|
+
if (attempt < config.retryTimes) {
|
|
519
|
+
await delay(config.retryInterval);
|
|
520
|
+
}
|
|
568
521
|
}
|
|
569
522
|
}
|
|
523
|
+
debugLog('WARN', `${api.label} 所有重试均失败,切换下一个API`);
|
|
570
524
|
}
|
|
571
|
-
throw lastError || new Error('API请求全部失败');
|
|
525
|
+
throw lastError || new Error('所有API请求全部失败');
|
|
572
526
|
}
|
|
573
|
-
async function parseUrl(url) {
|
|
527
|
+
async function parseUrl(url, type) {
|
|
574
528
|
const realUrl = await resolveShortUrl(url);
|
|
575
529
|
const candidates = [realUrl, url];
|
|
576
530
|
for (const candidate of [...new Set(candidates)]) {
|
|
577
531
|
try {
|
|
578
|
-
const info = await fetchApi(candidate);
|
|
532
|
+
const info = await fetchApi(candidate, type);
|
|
579
533
|
if (info.video || info.images.length > 0) {
|
|
580
534
|
return { success: true, data: info };
|
|
581
535
|
}
|
|
@@ -587,8 +541,8 @@ function apply(ctx, config) {
|
|
|
587
541
|
}
|
|
588
542
|
return { success: false, msg: texts.unsupportedPlatformText };
|
|
589
543
|
}
|
|
590
|
-
async function processSingleUrl(url) {
|
|
591
|
-
const result = await parseUrl(url);
|
|
544
|
+
async function processSingleUrl(url, type) {
|
|
545
|
+
const result = await parseUrl(url, type);
|
|
592
546
|
if (!result.success) {
|
|
593
547
|
return { success: false, msg: result.msg, url };
|
|
594
548
|
}
|
|
@@ -641,72 +595,70 @@ function apply(ctx, config) {
|
|
|
641
595
|
}
|
|
642
596
|
async function sendVideoFile(session, videoUrl) {
|
|
643
597
|
if (!videoUrl)
|
|
644
|
-
|
|
598
|
+
return;
|
|
645
599
|
if (!config.showVideoFile) {
|
|
646
600
|
return await sendWithTimeout(session, `视频链接:${videoUrl}`);
|
|
647
601
|
}
|
|
648
|
-
const
|
|
602
|
+
const sendLink = async () => {
|
|
649
603
|
await sendWithTimeout(session, `视频链接:${videoUrl}`).catch(() => { });
|
|
650
604
|
};
|
|
651
|
-
|
|
652
|
-
let tempFilePath = null;
|
|
605
|
+
if (config.forceDownloadVideo) {
|
|
653
606
|
try {
|
|
654
|
-
tempFilePath = await downloadVideoFile(videoUrl, config.tempDir || './temp_videos', config.videoDownloadTimeout || 120000, config.maxVideoSize || 0);
|
|
607
|
+
const tempFilePath = await downloadVideoFile(videoUrl, config.tempDir || './temp_videos', config.videoDownloadTimeout || 120000, config.maxVideoSize || 0);
|
|
655
608
|
const localFile = `file://${tempFilePath}`;
|
|
656
|
-
|
|
657
|
-
return
|
|
609
|
+
await sendWithTimeout(session, koishi_1.h.video(localFile));
|
|
610
|
+
return;
|
|
658
611
|
}
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
612
|
+
catch (e) {
|
|
613
|
+
debugLog('ERROR', '强制下载失败,尝试直接发送URL:', getErrorMessage(e));
|
|
614
|
+
try {
|
|
615
|
+
await sendWithTimeout(session, koishi_1.h.video(videoUrl));
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
catch (urlErr) {
|
|
619
|
+
debugLog('ERROR', '发送URL也失败,降级发送链接:', getErrorMessage(urlErr));
|
|
620
|
+
await sendLink();
|
|
662
621
|
}
|
|
663
622
|
}
|
|
664
|
-
|
|
665
|
-
if (config.forceDownloadVideo) {
|
|
666
|
-
try {
|
|
667
|
-
return await tryDownloadAndSend();
|
|
668
|
-
}
|
|
669
|
-
catch (err) {
|
|
670
|
-
debugLog('ERROR', `下载并发送视频失败: ${getErrorMessage(err)}`);
|
|
671
|
-
await sendLinkAsFallback();
|
|
672
|
-
}
|
|
623
|
+
return;
|
|
673
624
|
}
|
|
674
|
-
|
|
625
|
+
try {
|
|
626
|
+
debugLog('INFO', '尝试直接发送视频URL');
|
|
627
|
+
await sendWithTimeout(session, koishi_1.h.video(videoUrl));
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
catch (urlErr) {
|
|
631
|
+
debugLog('ERROR', '直接发送URL失败,尝试下载:', getErrorMessage(urlErr));
|
|
675
632
|
try {
|
|
676
|
-
|
|
677
|
-
|
|
633
|
+
const tempFilePath = await downloadVideoFile(videoUrl, config.tempDir || './temp_videos', config.videoDownloadTimeout || 120000, config.maxVideoSize || 0);
|
|
634
|
+
const localFile = `file://${tempFilePath}`;
|
|
635
|
+
await sendWithTimeout(session, koishi_1.h.video(localFile));
|
|
636
|
+
return;
|
|
678
637
|
}
|
|
679
|
-
catch (
|
|
680
|
-
debugLog('ERROR',
|
|
681
|
-
|
|
682
|
-
return await tryDownloadAndSend();
|
|
683
|
-
}
|
|
684
|
-
catch (downloadErr) {
|
|
685
|
-
debugLog('ERROR', `下载并发送视频也失败: ${getErrorMessage(downloadErr)}`);
|
|
686
|
-
await sendLinkAsFallback();
|
|
687
|
-
}
|
|
638
|
+
catch (downloadErr) {
|
|
639
|
+
debugLog('ERROR', '下载失败,降级发送链接:', getErrorMessage(downloadErr));
|
|
640
|
+
await sendLink();
|
|
688
641
|
}
|
|
689
642
|
}
|
|
690
643
|
}
|
|
691
|
-
async function flush(session,
|
|
692
|
-
|
|
693
|
-
debugLog('INFO', `开始解析 ${uniqueUrls.length} 个链接`);
|
|
644
|
+
async function flush(session, matches) {
|
|
645
|
+
debugLog('INFO', `开始解析 ${matches.length} 个链接`);
|
|
694
646
|
const items = [];
|
|
695
647
|
const errors = [];
|
|
696
|
-
for (let i = 0; i <
|
|
697
|
-
const
|
|
698
|
-
debugLog('INFO', `正在解析第 ${i + 1}/${
|
|
699
|
-
const result = await processSingleUrl(url);
|
|
648
|
+
for (let i = 0; i < matches.length; i++) {
|
|
649
|
+
const match = matches[i];
|
|
650
|
+
debugLog('INFO', `正在解析第 ${i + 1}/${matches.length} 个链接: ${match.url} (平台: ${match.type})`);
|
|
651
|
+
const result = await processSingleUrl(match.url, match.type);
|
|
700
652
|
if (result.success) {
|
|
701
653
|
items.push(result.data);
|
|
702
654
|
}
|
|
703
655
|
else {
|
|
704
656
|
const item = texts.parseErrorItemFormat
|
|
705
|
-
.replace(/\$\{url\}/g, url.length > 50 ? url.slice(0, 50) + '...' : url)
|
|
657
|
+
.replace(/\$\{url\}/g, match.url.length > 50 ? match.url.slice(0, 50) + '...' : match.url)
|
|
706
658
|
.replace(/\$\{msg\}/g, result.msg);
|
|
707
659
|
errors.push(item);
|
|
708
660
|
}
|
|
709
|
-
if (i <
|
|
661
|
+
if (i < matches.length - 1) {
|
|
710
662
|
await delay(500);
|
|
711
663
|
}
|
|
712
664
|
}
|
|
@@ -722,7 +674,6 @@ function apply(ctx, config) {
|
|
|
722
674
|
const botName = config.botName || '视频解析机器人';
|
|
723
675
|
if (enableForward) {
|
|
724
676
|
const forwardMessages = [];
|
|
725
|
-
const videoItems = [];
|
|
726
677
|
for (const item of items) {
|
|
727
678
|
const p = item.parsed;
|
|
728
679
|
const text = item.text;
|
|
@@ -739,10 +690,7 @@ function apply(ctx, config) {
|
|
|
739
690
|
}
|
|
740
691
|
}
|
|
741
692
|
if (p.video) {
|
|
742
|
-
forwardMessages.push(buildForwardNode(session,
|
|
743
|
-
if (config.showVideoFile && (p.type === 'video' || (p.type === 'live' && !p.live_photo?.length && !p.images?.length))) {
|
|
744
|
-
videoItems.push(p);
|
|
745
|
-
}
|
|
693
|
+
forwardMessages.push(buildForwardNode(session, koishi_1.h.video(p.video), botName));
|
|
746
694
|
}
|
|
747
695
|
}
|
|
748
696
|
if (forwardMessages.length) {
|
|
@@ -759,15 +707,6 @@ function apply(ctx, config) {
|
|
|
759
707
|
}
|
|
760
708
|
}
|
|
761
709
|
}
|
|
762
|
-
for (const p of videoItems) {
|
|
763
|
-
try {
|
|
764
|
-
await sendVideoFile(session, p.video);
|
|
765
|
-
}
|
|
766
|
-
catch (err) {
|
|
767
|
-
debugLog('ERROR', `视频发送失败: ${getErrorMessage(err)}`);
|
|
768
|
-
}
|
|
769
|
-
await delay(500);
|
|
770
|
-
}
|
|
771
710
|
}
|
|
772
711
|
else {
|
|
773
712
|
for (const item of items) {
|
|
@@ -786,8 +725,8 @@ function apply(ctx, config) {
|
|
|
786
725
|
try {
|
|
787
726
|
await sendVideoFile(session, p.video);
|
|
788
727
|
}
|
|
789
|
-
catch (
|
|
790
|
-
debugLog('ERROR', `视频发送失败: ${getErrorMessage(
|
|
728
|
+
catch (e) {
|
|
729
|
+
debugLog('ERROR', `视频发送失败: ${getErrorMessage(e)}`);
|
|
791
730
|
}
|
|
792
731
|
}
|
|
793
732
|
else {
|
|
@@ -815,10 +754,10 @@ function apply(ctx, config) {
|
|
|
815
754
|
return;
|
|
816
755
|
if (session.selfId === session.userId)
|
|
817
756
|
return;
|
|
818
|
-
const
|
|
819
|
-
if (!
|
|
757
|
+
const matches = extractAllUrlsFromMessage(session);
|
|
758
|
+
if (!matches.length)
|
|
820
759
|
return;
|
|
821
|
-
debugLog('INFO', `检测到 ${
|
|
760
|
+
debugLog('INFO', `检测到 ${matches.length} 个链接,开始处理`);
|
|
822
761
|
if (config.showWaitingTip) {
|
|
823
762
|
try {
|
|
824
763
|
await sendWithTimeout(session, texts.waitingTipText);
|
|
@@ -827,15 +766,15 @@ function apply(ctx, config) {
|
|
|
827
766
|
debugLog('WARN', '发送等待提示失败:', e);
|
|
828
767
|
}
|
|
829
768
|
}
|
|
830
|
-
await flush(session,
|
|
769
|
+
await flush(session, matches);
|
|
831
770
|
});
|
|
832
771
|
ctx.command('parse <url>', '手动解析视频').action(async ({ session }, url) => {
|
|
833
772
|
if (!url) {
|
|
834
773
|
await sendWithTimeout(session, texts.invalidLinkText);
|
|
835
774
|
return;
|
|
836
775
|
}
|
|
837
|
-
const
|
|
838
|
-
if (!
|
|
776
|
+
const matches = linkTypeParser(url);
|
|
777
|
+
if (!matches.length) {
|
|
839
778
|
await sendWithTimeout(session, texts.invalidLinkText);
|
|
840
779
|
return;
|
|
841
780
|
}
|
|
@@ -845,7 +784,7 @@ function apply(ctx, config) {
|
|
|
845
784
|
}
|
|
846
785
|
catch { }
|
|
847
786
|
}
|
|
848
|
-
await flush(session,
|
|
787
|
+
await flush(session, matches);
|
|
849
788
|
});
|
|
850
789
|
const tempCleanupInterval = setInterval(async () => {
|
|
851
790
|
try {
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -63,7 +63,6 @@ This is a **multi-platform video/image parsing plugin** developed for the Koishi
|
|
|
63
63
|
| `tempDir` | string | `./temp_videos` | 临时视频存储目录 |
|
|
64
64
|
| `maxVideoSize` | number | 0 | 最大下载视频大小(MB),0 为不限制大小 |
|
|
65
65
|
| `forceDownloadVideo` | boolean | false | 强制下载视频后发送 |
|
|
66
|
-
| `videoLoadWaitTime` | number | 180000 | 视频链接加载等待时间(毫秒),获取到视频链接后等待指定时间再发送,0为不等待 |
|
|
67
66
|
|
|
68
67
|
### 网络与 API 设置
|
|
69
68
|
| 配置项 | 类型 | 默认值 | 说明 |
|