koishi-plugin-video-parser-all 1.5.6 → 1.5.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.js +147 -131
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -283,68 +283,76 @@ let debugEnabled = false;
|
|
|
283
283
|
function debugLog(level, ...args) {
|
|
284
284
|
if (!debugEnabled)
|
|
285
285
|
return;
|
|
286
|
-
|
|
286
|
+
const safe = args.map(a => {
|
|
287
|
+
try {
|
|
288
|
+
return typeof a === 'object' ? JSON.stringify(a) : String(a);
|
|
289
|
+
}
|
|
290
|
+
catch {
|
|
291
|
+
return '[unserializable]';
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
logger.info(`[${new Date().toISOString()}] [${level}] ${safe.join(' ')}`);
|
|
287
295
|
}
|
|
288
296
|
const BUILTIN_LINK_RULES = [
|
|
289
|
-
{ pattern: /https?:\/\/(?:www\.)?bilibili\.com\/video\/([ab]v[0-9a-zA-Z_-]+)[^\s]
|
|
290
|
-
{ pattern: /https?:\/\/b23\.tv\/[0-9a-zA-Z_\/-]
|
|
291
|
-
{ pattern: /https?:\/\/bili\d+\.cn\/[0-9a-zA-Z_\/-]
|
|
292
|
-
{ pattern: /https?:\/\/b23\.wtf\/[0-9a-zA-Z_\/-]
|
|
293
|
-
{ pattern: /https?:\/\/bili2233\.cn\/[0-9a-zA-Z_\/-]
|
|
294
|
-
{ pattern: /https?:\/\/(?:www\.)?douyin\.com\/video\/\d{10,}
|
|
295
|
-
{ pattern: /https?:\/\/v\.douyin\.com\/[0-9a-zA-Z_\/-]
|
|
296
|
-
{ pattern: /https?:\/\/(?:www\.)?kuaishou\.com\/short-video\/[0-9a-zA-Z_\/-]
|
|
297
|
-
{ pattern: /https?:\/\/v\.kuaishou\.com\/[0-9a-zA-Z_\/-]
|
|
298
|
-
{ pattern: /https?:\/\/(?:www\.)?kuaishou\.com\/f\/[0-9a-zA-Z_\/-]
|
|
299
|
-
{ pattern: /https?:\/\/(?:www\.)?xiaohongshu\.com\/discovery\/item\/[0-9a-zA-Z_\/-]
|
|
300
|
-
{ pattern: /https?:\/\/xhslink\.com\/[0-9a-zA-Z_\/-]
|
|
301
|
-
{ pattern: /https?:\/\/(?:www\.)?xiaohongshu\.com\/explore\/[0-9a-zA-Z_\/-]
|
|
302
|
-
{ pattern: /https?:\/\/(?:www\.)?xiaohongshu\.com\/board\/[0-9a-zA-Z_\/-]
|
|
303
|
-
{ pattern: /https?:\/\/weibo\.com\/\d+\/[0-9a-zA-Z_\/-]
|
|
304
|
-
{ pattern: /https?:\/\/video\.weibo\.com\/show\?fid=[0-9a-zA-Z_\/-]
|
|
305
|
-
{ pattern: /https?:\/\/t\.cn\/[0-9a-zA-Z_\/-]
|
|
306
|
-
{ pattern: /https?:\/\/m\.weibo\.cn\/[0-9a-zA-Z_\/-]
|
|
307
|
-
{ pattern: /https?:\/\/(?:www\.)?ixigua\.com\/\d{10,}
|
|
308
|
-
{ pattern: /https?:\/\/(?:www\.)?youtube\.com\/watch\?v=[a-zA-Z0-9_-]{11}
|
|
309
|
-
{ pattern: /https?:\/\/youtu\.be\/[0-9a-zA-Z_\/-]
|
|
310
|
-
{ pattern: /https?:\/\/(?:www\.)?youtube\.com\/shorts\/[0-9a-zA-Z_\/-]
|
|
311
|
-
{ pattern: /https?:\/\/(?:www\.)?tiktok\.com\/@[\w.]+\/video\/\d{10,}
|
|
312
|
-
{ pattern: /https?:\/\/vm\.tiktok\.com\/[0-9a-zA-Z_\/-]
|
|
313
|
-
{ pattern: /https?:\/\/vt\.tiktok\.com\/[0-9a-zA-Z_\/-]
|
|
314
|
-
{ pattern: /https?:\/\/(?:www\.)?acfun\.cn\/v\/ac\d{10,}
|
|
315
|
-
{ pattern: /https?:\/\/(?:www\.)?zhihu\.com\/video\/\d{10,}
|
|
316
|
-
{ pattern: /https?:\/\/(?:www\.|m\.)?zhihu\.com\/question\/\d+\/answer\/\d
|
|
317
|
-
{ pattern: /https?:\/\/zhuanlan\.zhihu\.com\/p\/\d
|
|
318
|
-
{ pattern: /https?:\/\/(?:www\.|m\.)?zhihu\.com\/zvideo\/\d
|
|
319
|
-
{ pattern: /https?:\/\/weishi\.qq\.com\/weishi\/feed\/[0-9a-zA-Z_\/-]
|
|
320
|
-
{ pattern: /https?:\/\/(?:www\.)?huya\.com\/video\/[0-9a-zA-Z_\/-]
|
|
321
|
-
{ pattern: /https?:\/\/haokan\.baidu\.com\/v\?vid=[0-9a-zA-Z_\/-]
|
|
322
|
-
{ pattern: /https?:\/\/(?:www\.)?meipai\.com\/media\/\d{10,}
|
|
323
|
-
{ pattern: /https?:\/\/twitter\.com\/\w+\/status\/\d{10,}
|
|
324
|
-
{ pattern: /https?:\/\/x\.com\/\w+\/status\/\d{10,}
|
|
325
|
-
{ pattern: /https?:\/\/(?:www\.)?instagram\.com\/p\/[0-9a-zA-Z_\/-]
|
|
326
|
-
{ pattern: /https?:\/\/(?:www\.)?instagram\.com\/reel\/[0-9a-zA-Z_\/-]
|
|
327
|
-
{ pattern: /https?:\/\/(?:www\.)?instagram\.com\/share\/(?:reel|p)\/[0-9a-zA-Z_\/-]
|
|
328
|
-
{ pattern: /https?:\/\/(?:www\.)?doubao\.com\/video\/\d{10,}
|
|
329
|
-
{ pattern: /https?:\/\/(?:www\.)?doubao\.com\/video-sharing\?[^\s]*/gi, type: 'doubao' },
|
|
330
|
-
{ pattern: /https?:\/\/(?:www\.)?doubao\.com\/thread\/[^\s]+/gi, type: 'doubao_image' },
|
|
331
|
-
{ pattern: /https?:\/\/(?:www\.)?jimeng\.jianying\.com\/[^\s]*/gi, type: 'jimeng' },
|
|
332
|
-
{ pattern: /https?:\/\/(?:www\.)?jimeng\.cn\/[^\s]*/gi, type: 'jimeng' },
|
|
333
|
-
{ pattern: /https?:\/\/(?:www\.)?dreamina\.jianying\.com\/[^\s]*/gi, type: 'jimeng' },
|
|
334
|
-
{ pattern: /https?:\/\/(?:www\.)?dreamina\.capcut\.com\/[^\s]*/gi, type: 'jimeng' },
|
|
335
|
-
{ pattern: /https?:\/\/(?:www\.)?oasis\.weibo\.com\/v\/[0-9a-zA-Z_\/-]
|
|
336
|
-
{ pattern: /https?:\/\/channels\.weixin\.qq\.com\/[0-9a-zA-Z_\/-]
|
|
337
|
-
{ pattern: /https?:\/\/weixin\.qq\.com\/sph\/[0-9a-zA-Z_\/-]
|
|
338
|
-
{ pattern: /https?:\/\/(?:www\.)?pearvideo\.com\/video_\d
|
|
339
|
-
{ pattern: /https?:\/\/video\.li\/[0-9a-zA-Z_\/-]
|
|
340
|
-
{ pattern: /https?:\/\/(?:www\.)?quanmin\.tv\/[0-9a-zA-Z_\/-]
|
|
341
|
-
{ pattern: /https?:\/\/(?:www\.)?quanmintv\.cn\/[0-9a-zA-Z_\/-]
|
|
342
|
-
{ pattern: /https?:\/\/h5\.pipigx\.com\/pp\/post\/\d
|
|
343
|
-
{ pattern: /https?:\/\/(?:www\.)?ippzone\.com\/[0-9a-zA-Z_\/-]
|
|
344
|
-
{ pattern: /https?:\/\/(?:h5|www)\.pipix\.com\/[0-9a-zA-Z_\/-]
|
|
345
|
-
{ pattern: /https?:\/\/(?:www\.)?pipixia\.com\/[0-9a-zA-Z_\/-]
|
|
346
|
-
{ pattern: /https?:\/\/share\.xiaochuankeji\.cn\/hybrid\/share\/post\?pid=\d
|
|
347
|
-
{ pattern: /https?:\/\/(?:h5|www)\.izuiyou\.com\/[0-9a-zA-Z_\/-]
|
|
297
|
+
{ pattern: /https?:\/\/(?:www\.)?bilibili\.com\/video\/([ab]v[0-9a-zA-Z_-]+)(?:\?[^\s'"“”‘’]*)?/gi, type: 'bilibili' },
|
|
298
|
+
{ pattern: /https?:\/\/b23\.tv\/[0-9a-zA-Z_\/-]+/gi, type: 'bilibili' },
|
|
299
|
+
{ pattern: /https?:\/\/bili\d+\.cn\/[0-9a-zA-Z_\/-]+/gi, type: 'bilibili' },
|
|
300
|
+
{ pattern: /https?:\/\/b23\.wtf\/[0-9a-zA-Z_\/-]+/gi, type: 'bilibili' },
|
|
301
|
+
{ pattern: /https?:\/\/bili2233\.cn\/[0-9a-zA-Z_\/-]+/gi, type: 'bilibili' },
|
|
302
|
+
{ pattern: /https?:\/\/(?:www\.)?douyin\.com\/video\/\d{10,}/gi, type: 'douyin' },
|
|
303
|
+
{ pattern: /https?:\/\/v\.douyin\.com\/[0-9a-zA-Z_\/-]+/gi, type: 'douyin' },
|
|
304
|
+
{ pattern: /https?:\/\/(?:www\.)?kuaishou\.com\/short-video\/[0-9a-zA-Z_\/-]+/gi, type: 'kuaishou' },
|
|
305
|
+
{ pattern: /https?:\/\/v\.kuaishou\.com\/[0-9a-zA-Z_\/-]+/gi, type: 'kuaishou' },
|
|
306
|
+
{ pattern: /https?:\/\/(?:www\.)?kuaishou\.com\/f\/[0-9a-zA-Z_\/-]+/gi, type: 'kuaishou' },
|
|
307
|
+
{ pattern: /https?:\/\/(?:www\.)?xiaohongshu\.com\/discovery\/item\/[0-9a-zA-Z_\/-]+/gi, type: 'xiaohongshu' },
|
|
308
|
+
{ pattern: /https?:\/\/xhslink\.com\/[0-9a-zA-Z_\/-]+/gi, type: 'xiaohongshu' },
|
|
309
|
+
{ pattern: /https?:\/\/(?:www\.)?xiaohongshu\.com\/explore\/[0-9a-zA-Z_\/-]+/gi, type: 'xiaohongshu' },
|
|
310
|
+
{ pattern: /https?:\/\/(?:www\.)?xiaohongshu\.com\/board\/[0-9a-zA-Z_\/-]+/gi, type: 'xiaohongshu' },
|
|
311
|
+
{ pattern: /https?:\/\/weibo\.com\/\d+\/[0-9a-zA-Z_\/-]+/gi, type: 'weibo' },
|
|
312
|
+
{ pattern: /https?:\/\/video\.weibo\.com\/show\?fid=[0-9a-zA-Z_\/-]+/gi, type: 'weibo' },
|
|
313
|
+
{ pattern: /https?:\/\/t\.cn\/[0-9a-zA-Z_\/-]+/gi, type: 'weibo' },
|
|
314
|
+
{ pattern: /https?:\/\/m\.weibo\.cn\/[0-9a-zA-Z_\/-]+/gi, type: 'weibo' },
|
|
315
|
+
{ pattern: /https?:\/\/(?:www\.)?ixigua\.com\/\d{10,}/gi, type: 'xigua' },
|
|
316
|
+
{ pattern: /https?:\/\/(?:www\.)?youtube\.com\/watch\?v=[a-zA-Z0-9_-]{11}/gi, type: 'youtube' },
|
|
317
|
+
{ pattern: /https?:\/\/youtu\.be\/[0-9a-zA-Z_\/-]+/gi, type: 'youtube' },
|
|
318
|
+
{ pattern: /https?:\/\/(?:www\.)?youtube\.com\/shorts\/[0-9a-zA-Z_\/-]+/gi, type: 'youtube' },
|
|
319
|
+
{ pattern: /https?:\/\/(?:www\.)?tiktok\.com\/@[\w.]+\/video\/\d{10,}/gi, type: 'tiktok' },
|
|
320
|
+
{ pattern: /https?:\/\/vm\.tiktok\.com\/[0-9a-zA-Z_\/-]+/gi, type: 'tiktok' },
|
|
321
|
+
{ pattern: /https?:\/\/vt\.tiktok\.com\/[0-9a-zA-Z_\/-]+/gi, type: 'tiktok' },
|
|
322
|
+
{ pattern: /https?:\/\/(?:www\.)?acfun\.cn\/v\/ac\d{10,}/gi, type: 'acfun' },
|
|
323
|
+
{ pattern: /https?:\/\/(?:www\.)?zhihu\.com\/video\/\d{10,}/gi, type: 'zhihu' },
|
|
324
|
+
{ pattern: /https?:\/\/(?:www\.|m\.)?zhihu\.com\/question\/\d+\/answer\/\d+/gi, type: 'zhihu' },
|
|
325
|
+
{ pattern: /https?:\/\/zhuanlan\.zhihu\.com\/p\/\d+/gi, type: 'zhihu' },
|
|
326
|
+
{ pattern: /https?:\/\/(?:www\.|m\.)?zhihu\.com\/zvideo\/\d+/gi, type: 'zhihu' },
|
|
327
|
+
{ pattern: /https?:\/\/weishi\.qq\.com\/weishi\/feed\/[0-9a-zA-Z_\/-]+/gi, type: 'weishi' },
|
|
328
|
+
{ pattern: /https?:\/\/(?:www\.)?huya\.com\/video\/[0-9a-zA-Z_\/-]+/gi, type: 'huya' },
|
|
329
|
+
{ pattern: /https?:\/\/haokan\.baidu\.com\/v\?vid=[0-9a-zA-Z_\/-]+/gi, type: 'haokan' },
|
|
330
|
+
{ pattern: /https?:\/\/(?:www\.)?meipai\.com\/media\/\d{10,}/gi, type: 'meipai' },
|
|
331
|
+
{ pattern: /https?:\/\/twitter\.com\/\w+\/status\/\d{10,}/gi, type: 'twitter' },
|
|
332
|
+
{ pattern: /https?:\/\/x\.com\/\w+\/status\/\d{10,}/gi, type: 'twitter' },
|
|
333
|
+
{ pattern: /https?:\/\/(?:www\.)?instagram\.com\/p\/[0-9a-zA-Z_\/-]+/gi, type: 'instagram' },
|
|
334
|
+
{ pattern: /https?:\/\/(?:www\.)?instagram\.com\/reel\/[0-9a-zA-Z_\/-]+/gi, type: 'instagram' },
|
|
335
|
+
{ pattern: /https?:\/\/(?:www\.)?instagram\.com\/share\/(?:reel|p)\/[0-9a-zA-Z_\/-]+/gi, type: 'instagram' },
|
|
336
|
+
{ pattern: /https?:\/\/(?:www\.)?doubao\.com\/video\/\d{10,}/gi, type: 'doubao' },
|
|
337
|
+
{ pattern: /https?:\/\/(?:www\.)?doubao\.com\/video-sharing\?[^\s'"“”‘’]*/gi, type: 'doubao' },
|
|
338
|
+
{ pattern: /https?:\/\/(?:www\.)?doubao\.com\/thread\/[^\s'"“”‘’]+/gi, type: 'doubao_image' },
|
|
339
|
+
{ pattern: /https?:\/\/(?:www\.)?jimeng\.jianying\.com\/[^\s'"“”‘’]*/gi, type: 'jimeng' },
|
|
340
|
+
{ pattern: /https?:\/\/(?:www\.)?jimeng\.cn\/[^\s'"“”‘’]*/gi, type: 'jimeng' },
|
|
341
|
+
{ pattern: /https?:\/\/(?:www\.)?dreamina\.jianying\.com\/[^\s'"“”‘’]*/gi, type: 'jimeng' },
|
|
342
|
+
{ pattern: /https?:\/\/(?:www\.)?dreamina\.capcut\.com\/[^\s'"“”‘’]*/gi, type: 'jimeng' },
|
|
343
|
+
{ pattern: /https?:\/\/(?:www\.)?oasis\.weibo\.com\/v\/[0-9a-zA-Z_\/-]+/gi, type: 'oasis' },
|
|
344
|
+
{ pattern: /https?:\/\/channels\.weixin\.qq\.com\/[0-9a-zA-Z_\/-]+/gi, type: 'wechat_channel' },
|
|
345
|
+
{ pattern: /https?:\/\/weixin\.qq\.com\/sph\/[0-9a-zA-Z_\/-]+/gi, type: 'wechat_channel' },
|
|
346
|
+
{ pattern: /https?:\/\/(?:www\.)?pearvideo\.com\/video_\d+/gi, type: 'lishi' },
|
|
347
|
+
{ pattern: /https?:\/\/video\.li\/[0-9a-zA-Z_\/-]+/gi, type: 'lishi' },
|
|
348
|
+
{ pattern: /https?:\/\/(?:www\.)?quanmin\.tv\/[0-9a-zA-Z_\/-]+/gi, type: 'quanmin' },
|
|
349
|
+
{ pattern: /https?:\/\/(?:www\.)?quanmintv\.cn\/[0-9a-zA-Z_\/-]+/gi, type: 'quanmin' },
|
|
350
|
+
{ pattern: /https?:\/\/h5\.pipigx\.com\/pp\/post\/\d+/gi, type: 'pipigx' },
|
|
351
|
+
{ pattern: /https?:\/\/(?:www\.)?ippzone\.com\/[0-9a-zA-Z_\/-]+/gi, type: 'pipigx' },
|
|
352
|
+
{ pattern: /https?:\/\/(?:h5|www)\.pipix\.com\/[0-9a-zA-Z_\/-]+/gi, type: 'pipixia' },
|
|
353
|
+
{ pattern: /https?:\/\/(?:www\.)?pipixia\.com\/[0-9a-zA-Z_\/-]+/gi, type: 'pipixia' },
|
|
354
|
+
{ pattern: /https?:\/\/share\.xiaochuankeji\.cn\/hybrid\/share\/post\?pid=\d+/gi, type: 'zuiyou' },
|
|
355
|
+
{ pattern: /https?:\/\/(?:h5|www)\.izuiyou\.com\/[0-9a-zA-Z_\/-]+/gi, type: 'zuiyou' },
|
|
348
356
|
];
|
|
349
357
|
function buildCustomLinkRules(customPlatforms) {
|
|
350
358
|
if (!Array.isArray(customPlatforms) || customPlatforms.length === 0)
|
|
@@ -356,7 +364,7 @@ function buildCustomLinkRules(customPlatforms) {
|
|
|
356
364
|
if (keywords.length === 0)
|
|
357
365
|
return null;
|
|
358
366
|
const escaped = keywords.map((k) => k.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
|
|
359
|
-
const pattern = new RegExp(
|
|
367
|
+
const pattern = new RegExp(`https?://[^/\\s"'“”‘’]*(${escaped.join('|')})[^\\s"'“”‘’]*`, 'gi');
|
|
360
368
|
return { pattern, type: `custom_${p.name}` };
|
|
361
369
|
})
|
|
362
370
|
.filter(Boolean);
|
|
@@ -370,7 +378,10 @@ function linkTypeParser(content, customRules) {
|
|
|
370
378
|
let match;
|
|
371
379
|
rule.pattern.lastIndex = 0;
|
|
372
380
|
while ((match = rule.pattern.exec(content)) !== null) {
|
|
373
|
-
|
|
381
|
+
let url = match[0];
|
|
382
|
+
url = cleanUrl(url);
|
|
383
|
+
if (!url)
|
|
384
|
+
continue;
|
|
374
385
|
if (seen.has(url))
|
|
375
386
|
continue;
|
|
376
387
|
seen.add(url);
|
|
@@ -385,8 +396,8 @@ function cleanUrl(url) {
|
|
|
385
396
|
.replace(/</g, '<')
|
|
386
397
|
.replace(/>/g, '>')
|
|
387
398
|
.replace(/\\\//g, '/');
|
|
388
|
-
url = url.replace(/^[\s"'
|
|
389
|
-
url = url.replace(/[\s"'<>\{\}\[\]
|
|
399
|
+
url = url.replace(/^[\s"'<“”‘’]+/, '');
|
|
400
|
+
url = url.replace(/[\s"'<>\{\}\[\]`,;,。!?:;“”‘’…—~]+$/, '');
|
|
390
401
|
if (!/^https?:\/\//i.test(url)) {
|
|
391
402
|
if (/^\/\//.test(url))
|
|
392
403
|
url = 'https:' + url;
|
|
@@ -630,7 +641,7 @@ function parseApiResponse(raw, maxDescLen, fieldMapping) {
|
|
|
630
641
|
return { type, title, desc, author, uid, avatar, cover, video, videos, images, live_photo, music, like, comment, collect, share, play, duration, publishTime, author_followers, author_signature, admire };
|
|
631
642
|
}
|
|
632
643
|
const formatVarRegex = /\$\{([^}]+)\}/g;
|
|
633
|
-
function generateFormattedText(p, format) {
|
|
644
|
+
function generateFormattedText(p, format, index, total) {
|
|
634
645
|
const imageCount = p.images.length || p.live_photo.length;
|
|
635
646
|
const vars = {
|
|
636
647
|
'标题': p.title,
|
|
@@ -648,10 +659,6 @@ function generateFormattedText(p, format) {
|
|
|
648
659
|
'音乐标题': p.music.title || '',
|
|
649
660
|
'音乐作者': p.music.author || '',
|
|
650
661
|
};
|
|
651
|
-
const varReplacements = Object.entries(vars).map(([key, val]) => ({
|
|
652
|
-
regex: new RegExp(`\\$\\{${key}\\}`, 'g'),
|
|
653
|
-
value: val,
|
|
654
|
-
}));
|
|
655
662
|
const lines = format.split('\n');
|
|
656
663
|
const resultLines = [];
|
|
657
664
|
for (const line of lines) {
|
|
@@ -670,12 +677,16 @@ function generateFormattedText(p, format) {
|
|
|
670
677
|
continue;
|
|
671
678
|
}
|
|
672
679
|
let newLine = line;
|
|
673
|
-
for (const
|
|
674
|
-
newLine = newLine.replace(
|
|
680
|
+
for (const [key, val] of Object.entries(vars)) {
|
|
681
|
+
newLine = newLine.replace(new RegExp(`\\$\\{${key}\\}`, 'g'), val);
|
|
675
682
|
}
|
|
676
683
|
resultLines.push(newLine);
|
|
677
684
|
}
|
|
678
|
-
|
|
685
|
+
let text = resultLines.join('\n').trim();
|
|
686
|
+
if (index !== undefined && total !== undefined && total > 1) {
|
|
687
|
+
text = `【${index}/${total}】\n${text}`;
|
|
688
|
+
}
|
|
689
|
+
return text;
|
|
679
690
|
}
|
|
680
691
|
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
681
692
|
function buildForwardNode(session, content, botName) {
|
|
@@ -719,14 +730,17 @@ function apply(ctx, config) {
|
|
|
719
730
|
const imgSig = p.images?.length ? p.images.slice(0, 3).join('|') : (p.live_photo?.slice(0, 3).map(lp => lp.image).join('|') || '');
|
|
720
731
|
return [p.type, p.title, p.author, p.uid, p.video, imgSig].map(v => String(v ?? '')).join('::');
|
|
721
732
|
}
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
733
|
+
function getText(key) {
|
|
734
|
+
const defaults = {
|
|
735
|
+
waitingTipText: '正在解析视频,请稍候...',
|
|
736
|
+
unsupportedPlatformText: '不支持该平台链接',
|
|
737
|
+
invalidLinkText: '无效的视频链接',
|
|
738
|
+
parseErrorPrefix: '❌ 解析失败:',
|
|
739
|
+
parseErrorItemFormat: '【${url}】: ${msg}',
|
|
740
|
+
deduplicationTipText: '链接 ${url} 在最近 ${interval} 秒内已解析过,已跳过。',
|
|
741
|
+
};
|
|
742
|
+
return config[key] || defaults[key] || '';
|
|
743
|
+
}
|
|
730
744
|
const proxyConfig = config.proxy || {};
|
|
731
745
|
const customPlatforms = (config.customPlatforms || []).map((p) => ({
|
|
732
746
|
name: p.name,
|
|
@@ -803,6 +817,31 @@ function apply(ctx, config) {
|
|
|
803
817
|
return { [customHeaderName]: apiKey };
|
|
804
818
|
return {};
|
|
805
819
|
}
|
|
820
|
+
async function sendWithTimeout(session, content, customRetries) {
|
|
821
|
+
const maxRetries = customRetries ?? config.retryTimes ?? 3;
|
|
822
|
+
const retryDelay = config.retryInterval || 1000;
|
|
823
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
824
|
+
try {
|
|
825
|
+
let sendPromise = session.send(content);
|
|
826
|
+
if (config.videoSendTimeout > 0) {
|
|
827
|
+
const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('发送超时')), config.videoSendTimeout));
|
|
828
|
+
return await Promise.race([sendPromise, timeoutPromise]);
|
|
829
|
+
}
|
|
830
|
+
else {
|
|
831
|
+
return await sendPromise;
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
catch (err) {
|
|
835
|
+
const errMsg = getErrorMessage(err);
|
|
836
|
+
debugLog('ERROR', `发送失败尝试 ${attempt + 1}: ${errMsg}`);
|
|
837
|
+
if (attempt < maxRetries)
|
|
838
|
+
await delay(retryDelay);
|
|
839
|
+
else if (!config.ignoreSendError)
|
|
840
|
+
throw err;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
return null;
|
|
844
|
+
}
|
|
806
845
|
async function sendMedia(session, url, type, showFile) {
|
|
807
846
|
if (!url)
|
|
808
847
|
return;
|
|
@@ -835,7 +874,7 @@ function apply(ctx, config) {
|
|
|
835
874
|
if (lastTime && (Date.now() - lastTime < config.deduplicationInterval * 1000)) {
|
|
836
875
|
debugLog('INFO', `跳过重复链接: ${match.url}`);
|
|
837
876
|
const shortUrl = match.url.length > 80 ? match.url.slice(0, 80) + '...' : match.url;
|
|
838
|
-
const tip =
|
|
877
|
+
const tip = getText('deduplicationTipText').replace(/\$\{url\}/g, shortUrl).replace(/\$\{interval\}/g, String(config.deduplicationInterval));
|
|
839
878
|
await sendWithTimeout(session, tip).catch(() => { });
|
|
840
879
|
return;
|
|
841
880
|
}
|
|
@@ -859,7 +898,7 @@ function apply(ctx, config) {
|
|
|
859
898
|
}
|
|
860
899
|
else {
|
|
861
900
|
const displayUrl = match.url.length > 80 ? match.url.slice(0, 80) + '...' : match.url;
|
|
862
|
-
const item =
|
|
901
|
+
const item = getText('parseErrorItemFormat').replace(/\$\{url\}/g, displayUrl).replace(/\$\{msg\}/g, result.msg);
|
|
863
902
|
errors.push(item);
|
|
864
903
|
}
|
|
865
904
|
}
|
|
@@ -869,16 +908,19 @@ function apply(ctx, config) {
|
|
|
869
908
|
});
|
|
870
909
|
await Promise.all(promises);
|
|
871
910
|
if (errors.length)
|
|
872
|
-
await sendWithTimeout(session, `${
|
|
911
|
+
await sendWithTimeout(session, `${getText('parseErrorPrefix')}\n${errors.join('\n')}`);
|
|
873
912
|
if (!items.length)
|
|
874
913
|
return;
|
|
914
|
+
const totalItems = items.length;
|
|
875
915
|
const enableForward = config.enableForward && (session.platform === 'onebot' || session.platform === 'satori');
|
|
876
916
|
const botName = config.botName || '视频解析机器人';
|
|
877
917
|
if (enableForward) {
|
|
878
918
|
const forwardMessages = [];
|
|
879
|
-
for (
|
|
919
|
+
for (let i = 0; i < items.length; i++) {
|
|
920
|
+
const item = items[i];
|
|
880
921
|
const p = item.parsed;
|
|
881
|
-
|
|
922
|
+
const textWithIndex = (totalItems > 1) ? `【${i + 1}/${totalItems}】\n${item.text}` : item.text;
|
|
923
|
+
let text = textWithIndex;
|
|
882
924
|
if (config.showAuthorAvatar && p.avatar && config.showAuthorAvatarText) {
|
|
883
925
|
text = text ? text + '\n' + (config.authorAvatarText || '作者头像:') : (config.authorAvatarText || '作者头像:');
|
|
884
926
|
}
|
|
@@ -900,8 +942,6 @@ function apply(ctx, config) {
|
|
|
900
942
|
if (p.type === 'live_photo' && p.live_photo?.length) {
|
|
901
943
|
for (const lp of p.live_photo) {
|
|
902
944
|
forwardMessages.push(buildForwardNode(session, koishi_1.h.image(lp.image), botName));
|
|
903
|
-
if (lp.video)
|
|
904
|
-
forwardMessages.push(buildForwardNode(session, koishi_1.h.video(lp.video), botName));
|
|
905
945
|
}
|
|
906
946
|
}
|
|
907
947
|
else if (p.type === 'image' || (p.type === 'live' && (p.live_photo?.length || p.images?.length))) {
|
|
@@ -917,13 +957,15 @@ function apply(ctx, config) {
|
|
|
917
957
|
forwardMessages.push(buildForwardNode(session, koishi_1.h.audio(p.music.url), botName));
|
|
918
958
|
}
|
|
919
959
|
}
|
|
920
|
-
|
|
960
|
+
const MAX_NODES = 50;
|
|
961
|
+
for (let i = 0; i < forwardMessages.length; i += MAX_NODES) {
|
|
962
|
+
const batch = forwardMessages.slice(i, i + MAX_NODES);
|
|
921
963
|
try {
|
|
922
|
-
await sendWithTimeout(session, (0, koishi_1.h)('message', { forward: true },
|
|
964
|
+
await sendWithTimeout(session, (0, koishi_1.h)('message', { forward: true }, batch), config.retryTimes);
|
|
923
965
|
}
|
|
924
966
|
catch (err) {
|
|
925
967
|
debugLog('ERROR', '合并转发失败,降级逐条发送:', err);
|
|
926
|
-
for (const node of
|
|
968
|
+
for (const node of batch) {
|
|
927
969
|
await sendWithTimeout(session, node.data.content).catch(() => { });
|
|
928
970
|
await delay(300);
|
|
929
971
|
}
|
|
@@ -931,9 +973,11 @@ function apply(ctx, config) {
|
|
|
931
973
|
}
|
|
932
974
|
}
|
|
933
975
|
else {
|
|
934
|
-
for (
|
|
976
|
+
for (let i = 0; i < items.length; i++) {
|
|
977
|
+
const item = items[i];
|
|
935
978
|
const p = item.parsed;
|
|
936
|
-
|
|
979
|
+
const textWithIndex = (totalItems > 1) ? `【${i + 1}/${totalItems}】\n${item.text}` : item.text;
|
|
980
|
+
let text = textWithIndex;
|
|
937
981
|
if (config.showAuthorAvatar && p.avatar && config.showAuthorAvatarText) {
|
|
938
982
|
text = text ? text + '\n' + (config.authorAvatarText || '作者头像:') : (config.authorAvatarText || '作者头像:');
|
|
939
983
|
}
|
|
@@ -958,16 +1002,14 @@ function apply(ctx, config) {
|
|
|
958
1002
|
if (p.type === 'live_photo' && p.live_photo?.length) {
|
|
959
1003
|
for (const lp of p.live_photo) {
|
|
960
1004
|
await sendMedia(session, lp.image, 'image', config.showImageFileNew).catch(() => { });
|
|
961
|
-
if (lp.video)
|
|
962
|
-
await sendMedia(session, lp.video, 'video', config.showVideoFile).catch(() => { });
|
|
963
1005
|
await delay(500);
|
|
964
1006
|
}
|
|
965
1007
|
}
|
|
966
1008
|
else if (p.type === 'image' || (p.type === 'live' && (p.live_photo?.length || p.images?.length))) {
|
|
967
1009
|
const imageUrls = p.images?.length ? p.images : (p.live_photo?.map(lp => lp.image) ?? []);
|
|
968
|
-
for (let
|
|
969
|
-
logger.info(`[发送] 图片 ${
|
|
970
|
-
await sendMedia(session, imageUrls[
|
|
1010
|
+
for (let j = 0; j < imageUrls.length; j++) {
|
|
1011
|
+
logger.info(`[发送] 图片 ${j + 1}/${imageUrls.length}`);
|
|
1012
|
+
await sendMedia(session, imageUrls[j], 'image', config.showImageFileNew).catch(() => { });
|
|
971
1013
|
await delay(1000);
|
|
972
1014
|
}
|
|
973
1015
|
}
|
|
@@ -1072,18 +1114,17 @@ function apply(ctx, config) {
|
|
|
1072
1114
|
throw lastError || new Error('所有API请求全部失败');
|
|
1073
1115
|
}
|
|
1074
1116
|
async function parseUrl(url, type, fieldMapping, platformConf) {
|
|
1075
|
-
const cleanedUrl = cleanUrl(url);
|
|
1076
1117
|
try {
|
|
1077
|
-
const info = await fetchApi(
|
|
1118
|
+
const info = await fetchApi(url, type, fieldMapping, platformConf);
|
|
1078
1119
|
if (info.video || info.images.length > 0 || info.live_photo.length > 0)
|
|
1079
1120
|
return { success: true, data: info };
|
|
1080
|
-
debugLog('WARN', `解析成功但无内容: ${
|
|
1121
|
+
debugLog('WARN', `解析成功但无内容: ${url}`);
|
|
1122
|
+
return { success: false, msg: '解析接口返回空内容' };
|
|
1081
1123
|
}
|
|
1082
1124
|
catch (error) {
|
|
1083
|
-
debugLog('ERROR', `解析失败: ${
|
|
1125
|
+
debugLog('ERROR', `解析失败: ${url}`, getErrorMessage(error));
|
|
1084
1126
|
return { success: false, msg: getErrorMessage(error) };
|
|
1085
1127
|
}
|
|
1086
|
-
return { success: false, msg: texts.unsupportedPlatformText };
|
|
1087
1128
|
}
|
|
1088
1129
|
async function processSingleUrl(url, type, fieldMapping, platformConf) {
|
|
1089
1130
|
const result = await parseUrl(url, type, fieldMapping, platformConf);
|
|
@@ -1092,31 +1133,6 @@ function apply(ctx, config) {
|
|
|
1092
1133
|
const text = generateFormattedText(result.data, config.unifiedMessageFormat);
|
|
1093
1134
|
return { success: true, data: { text, parsed: result.data } };
|
|
1094
1135
|
}
|
|
1095
|
-
async function sendWithTimeout(session, content, customRetries) {
|
|
1096
|
-
const maxRetries = customRetries ?? config.retryTimes ?? 3;
|
|
1097
|
-
const retryDelay = config.retryInterval || 1000;
|
|
1098
|
-
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
1099
|
-
try {
|
|
1100
|
-
let sendPromise = session.send(content);
|
|
1101
|
-
if (config.videoSendTimeout > 0) {
|
|
1102
|
-
const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('发送超时')), config.videoSendTimeout));
|
|
1103
|
-
return await Promise.race([sendPromise, timeoutPromise]);
|
|
1104
|
-
}
|
|
1105
|
-
else {
|
|
1106
|
-
return await sendPromise;
|
|
1107
|
-
}
|
|
1108
|
-
}
|
|
1109
|
-
catch (err) {
|
|
1110
|
-
const errMsg = getErrorMessage(err);
|
|
1111
|
-
debugLog('ERROR', `发送失败尝试 ${attempt + 1}: ${errMsg}`);
|
|
1112
|
-
if (attempt < maxRetries)
|
|
1113
|
-
await delay(retryDelay);
|
|
1114
|
-
else if (!config.ignoreSendError)
|
|
1115
|
-
throw err;
|
|
1116
|
-
}
|
|
1117
|
-
}
|
|
1118
|
-
return null;
|
|
1119
|
-
}
|
|
1120
1136
|
const customRules = buildCustomLinkRules(config.customPlatforms || []);
|
|
1121
1137
|
const axiosConfig = {
|
|
1122
1138
|
timeout: config.timeout,
|
|
@@ -1155,7 +1171,7 @@ function apply(ctx, config) {
|
|
|
1155
1171
|
debugLog('INFO', `检测到 ${matches.length} 个链接`);
|
|
1156
1172
|
if (config.showWaitingTip) {
|
|
1157
1173
|
try {
|
|
1158
|
-
await sendWithTimeout(session,
|
|
1174
|
+
await sendWithTimeout(session, koishi_1.h.quote(session.messageId) + getText('waitingTipText'));
|
|
1159
1175
|
}
|
|
1160
1176
|
catch (e) {
|
|
1161
1177
|
debugLog('WARN', '等待提示发送失败:', e);
|
|
@@ -1165,17 +1181,17 @@ function apply(ctx, config) {
|
|
|
1165
1181
|
});
|
|
1166
1182
|
ctx.command('parse <url>', '手动解析视频').action(async ({ session }, url) => {
|
|
1167
1183
|
if (!url) {
|
|
1168
|
-
await sendWithTimeout(session,
|
|
1184
|
+
await sendWithTimeout(session, getText('invalidLinkText'));
|
|
1169
1185
|
return;
|
|
1170
1186
|
}
|
|
1171
1187
|
const matches = linkTypeParser(url, customRules);
|
|
1172
1188
|
if (!matches.length) {
|
|
1173
|
-
await sendWithTimeout(session,
|
|
1189
|
+
await sendWithTimeout(session, getText('invalidLinkText'));
|
|
1174
1190
|
return;
|
|
1175
1191
|
}
|
|
1176
1192
|
if (config.showWaitingTip) {
|
|
1177
1193
|
try {
|
|
1178
|
-
await sendWithTimeout(session,
|
|
1194
|
+
await sendWithTimeout(session, koishi_1.h.quote(session?.messageId) + getText('waitingTipText'));
|
|
1179
1195
|
}
|
|
1180
1196
|
catch { }
|
|
1181
1197
|
}
|
package/package.json
CHANGED