koishi-plugin-video-parser-all 0.5.9 → 0.6.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.
Files changed (2) hide show
  1. package/lib/index.js +86 -308
  2. package/package.json +1 -1
package/lib/index.js CHANGED
@@ -22,7 +22,7 @@ exports.Config = koishi_1.Schema.intersect([
22
22
  sameLinkInterval: koishi_1.Schema.number().min(0).default(180).description('相同链接重复解析间隔(秒)'),
23
23
  }).description('基础设置'),
24
24
  koishi_1.Schema.object({
25
- unifiedMessageFormat: koishi_1.Schema.string().role('textarea').default('标题:${标题}\n作者:${作者}\n简介:${简介}\n时长:${视频时长}\n点赞:${点赞数}\n投币:${投币数}\n收藏:${收藏数}\n转发:${转发数}\n播放:${播放数}\n评论:${评论数}\n音乐:${音乐名}').description('统一消息格式(无法获取的变量会自动隐藏)\n变量介绍:\n${标题} - 内容标题\n${作者} - 作者名称\n${简介} - 内容简介\n${视频时长} - 视频时长\n${点赞数} - 点赞数量\n${投币数} - 投币数量(仅B站)\n${收藏数} - 收藏数量\n${转发数} - 转发/分享数量\n${播放数} - 播放数量\n${评论数} - 评论数量\n${音乐名} - 背景音乐名称'),
25
+ unifiedMessageFormat: koishi_1.Schema.string().role('textarea').default('标题:${标题}\n作者:${作者}\n简介:${简介}\n时长:${视频时长}\n点赞:${点赞数}\n投币:${投币数}\n收藏:${收藏数}\n转发:${转发数}\n播放:${播放数}\n评论:${评论数}\n音乐:${音乐名}').description('统一消息格式(无法获取的变量会自动隐藏)\n变量介绍:\n${标题} - 内容标题\n${作者} - 作者名称\n${简介} - 内容简介\n${视频时长} - 视频时长\n${点赞数} - 点赞数量\n${投币数} - 投币数(仅B站)\n收藏数 - 收藏数量\n${转发数} - 转发/分享数量\n${播放数} - 播放数量\n${评论数} - 评论数量\n${音乐名} - 背景音乐名称'),
26
26
  }).description('统一消息格式'),
27
27
  koishi_1.Schema.object({
28
28
  showImageText: koishi_1.Schema.boolean().default(true).description('显示图文内容'),
@@ -394,73 +394,53 @@ function parseData(rawResponse, maxDescLength) {
394
394
  const stat = {};
395
395
  Object.entries(VARIABLE_MAPPING).forEach(([varName, keys]) => {
396
396
  const value = findValueInObject(data, keys) || findValueInObject(rootData, keys);
397
- if (value !== undefined)
398
- stat[varName] = value;
397
+ stat[varName] = value ?? '';
399
398
  });
400
399
  let type = 'video';
401
400
  if (data.jx?.type)
402
401
  type = data.jx.type;
403
402
  else if (data.type)
404
403
  type = data.type;
405
- else if (data.images && data.images.length > 0)
406
- type = 'image';
407
- else if (data.pics && data.pics.length > 0)
408
- type = '图集';
409
- const title = findValueInObject(data, VARIABLE_MAPPING['标题']) || findValueInObject(rootData, VARIABLE_MAPPING['标题']) || '无标题';
410
- const author = findValueInObject(data, VARIABLE_MAPPING['作者']) || findValueInObject(rootData, VARIABLE_MAPPING['作者']) || '未知作者';
411
- let desc = findValueInObject(data, VARIABLE_MAPPING['简介']) || findValueInObject(rootData, VARIABLE_MAPPING['简介']) || title;
412
- desc = desc.toString().slice(0, maxDescLength);
413
- const cover = findValueInObject(data, ['cover', 'imgurl', 'pic', 'thumbnail', 'cover_url']) || findValueInObject(rootData, ['cover', 'imgurl', 'pic', 'thumbnail', 'cover_url']) || '';
404
+ const title = stat['标题'] || '无标题';
405
+ const author = stat['作者'] || '未知作者';
406
+ const desc = (stat['简介'] || title).slice(0, maxDescLength);
407
+ const cover = data.cover ?? data.imgurl ?? data.pic ?? data.thumbnail ?? data.cover_url ?? '';
414
408
  let images = [];
415
- const imgList = findValueInObject(data, ['images', 'pics', 'pic_urls', 'image_list']) || findValueInObject(rootData, ['images', 'pics', 'pic_urls', 'image_list']) || [];
416
- if (Array.isArray(imgList))
417
- images = imgList.filter(img => img && typeof img === 'string');
418
- else if (imgList && typeof imgList === 'string')
419
- images = [imgList];
420
- let video = '';
421
- const videoUrls = [
422
- findValueInObject(data, ['url', 'video_url', 'download_url', 'playUrl', 'mp4_url']),
423
- findValueInObject(rootData, ['url', 'video_url', 'download_url', 'playUrl', 'mp4_url']),
424
- data.video?.url,
425
- data.item?.url,
426
- rootData.video?.url,
427
- rootData.item?.url
428
- ];
429
- for (const url of videoUrls) {
430
- if (url && typeof url === 'string' && url.startsWith('http')) {
431
- video = url;
432
- break;
433
- }
434
- }
435
- const durationValue = findValueInObject(data, VARIABLE_MAPPING['视频时长']) || findValueInObject(rootData, VARIABLE_MAPPING['视频时长']);
409
+ const imgRaw = data.images ?? data.pics ?? data.pic_urls ?? data.image_list ?? [];
410
+ if (Array.isArray(imgRaw))
411
+ images = imgRaw.filter(i => i && typeof i === 'string');
412
+ else if (imgRaw)
413
+ images = [String(imgRaw)];
414
+ let video = data.video?.url ?? data.item?.url ?? data.url ?? data.download_url ?? data.playUrl ?? data.video_url ?? '';
415
+ const durationValue = stat['视频时长'] || 0;
436
416
  const duration = typeof durationValue === 'number' ? durationValue : parseInt(durationValue) || 0;
437
- const durationFormatted = formatDuration(durationValue || 0);
438
- const live_photo = data.live_photo || rootData.live_photo || [];
439
- const music = findValueInObject(data, VARIABLE_MAPPING['音乐名']) || findValueInObject(rootData, VARIABLE_MAPPING['音乐名']) || '';
440
- const h_w = data.item?.h_w || rootData.item?.h_w || [];
441
- const quality_urls = data.quality_urls || rootData.quality_urls || {};
442
- const default_quality = data.default_quality || rootData.default_quality || '';
443
- const download_url = data.download_url || rootData.download_url || video;
444
- const play_count = stat['播放数'] || '';
445
- const reposts_count = stat['转发数'] || 0;
446
- const attitudes_count = stat['点赞数'] || 0;
447
- const comments_count = stat['评论数'] || 0;
417
+ const durationFormatted = formatDuration(durationValue);
418
+ const live_photo = data.live_photo ?? [];
419
+ const music = stat['音乐名'] || '';
420
+ const h_w = data.item?.h_w ?? [];
421
+ const quality_urls = data.quality_urls ?? {};
422
+ const default_quality = data.default_quality ?? '';
423
+ const download_url = data.download_url ?? video;
424
+ const play_count = stat['播放数'] ?? '';
425
+ const reposts_count = Number(stat['转发数']) || 0;
426
+ const attitudes_count = Number(stat['点赞数']) || 0;
427
+ const comments_count = Number(stat['评论数']) || 0;
448
428
  return {
449
429
  type: type,
450
430
  rawData: rawResponse,
451
- title,
452
- author,
453
- desc,
454
- cover,
431
+ title: String(title),
432
+ author: String(author),
433
+ desc: String(desc),
434
+ cover: String(cover),
455
435
  images,
456
- video,
436
+ video: String(video),
457
437
  duration,
458
438
  durationFormatted,
459
439
  stat,
460
440
  live_photo,
461
- music,
441
+ music: String(music),
462
442
  h_w,
463
- jx: data.jx || rootData.jx || null,
443
+ jx: data.jx ?? null,
464
444
  quality_urls,
465
445
  default_quality,
466
446
  download_url,
@@ -471,40 +451,16 @@ function parseData(rawResponse, maxDescLength) {
471
451
  };
472
452
  }
473
453
  function generateFormattedText(parseData, config) {
474
- let format = config.unifiedMessageFormat;
475
- if (!format)
476
- format = '标题:${标题}\n作者:${作者}\n简介:${简介}';
477
- const formatLines = format.split('\n');
478
- const validLines = [];
479
- formatLines.forEach((line) => {
480
- if (!line.trim())
481
- return;
482
- let isValid = true;
483
- let processedLine = line;
484
- const varMatches = line.match(/\$\{([^}]+)\}/g) || [];
485
- if (varMatches.length === 0) {
486
- validLines.push(line);
487
- return;
488
- }
489
- varMatches.forEach((varMatch) => {
490
- const varName = varMatch.replace(/\$\{|\}/g, '');
491
- const value = parseData.stat[varName];
492
- if (value === undefined || value === null || value === '' || value === 0 || value === '00:00') {
493
- isValid = false;
494
- }
495
- else {
496
- processedLine = processedLine.replace(varMatch, String(value));
497
- }
498
- });
499
- if (isValid && processedLine.trim() !== '') {
500
- validLines.push(processedLine);
501
- }
454
+ let format = config.unifiedMessageFormat || '标题:${标题}\n作者:${作者}\n简介:${简介}';
455
+ let result = format;
456
+ const varMatches = result.match(/\$\{([^}]+)\}/g) || [];
457
+ varMatches.forEach((varMatch) => {
458
+ const varName = varMatch.replace(/\$\{|\}/g, '');
459
+ const value = parseData.stat[varName];
460
+ const showValue = value ?? '';
461
+ result = result.replace(varMatch, String(showValue));
502
462
  });
503
- let result = validLines.join('\n').trim();
504
- if (!result) {
505
- result = `标题:${parseData.title}\n作者:${parseData.author}\n简介:${parseData.desc}`;
506
- }
507
- return result;
463
+ return result.trim() || `标题:${parseData.title}\n作者:${parseData.author}\n简介:${parseData.desc}`;
508
464
  }
509
465
  function clearAllCache() {
510
466
  processed.clear();
@@ -611,17 +567,6 @@ function apply(ctx, config) {
611
567
  }
612
568
  try {
613
569
  const parseResult = parseData(resData, config.maxDescLength);
614
- const hasValidContent = parseResult.video ||
615
- (parseResult.images && parseResult.images.length > 0) ||
616
- (parseResult.live_photo && parseResult.live_photo.length > 0) ||
617
- parseResult.type === 'live' ||
618
- parseResult.type === '图集';
619
- if (!hasValidContent) {
620
- const code = ErrorCode.NO_VIDEO_FOUND;
621
- const msg = getErrorInfo(code, '链接有效但未获取到有效视频/图片内容,可能是私密/需要登录/已删除内容');
622
- logger.warn(`[${code}] 解析成功但无有效内容: ${url}`);
623
- return { data: null, code, msg };
624
- }
625
570
  logger.info(`[${ErrorCode.SUCCESS}] 解析成功: ${url}`);
626
571
  return {
627
572
  data: parseResult,
@@ -724,20 +669,17 @@ function apply(ctx, config) {
724
669
  items.push(result.data);
725
670
  }
726
671
  else {
727
- errors.push({ url, code: result.code, msg: result.msg });
728
- logger.error(`[${result.code}] 解析失败: ${url}, 原因: ${result.msg}`);
672
+ errors.push({ url, msg: result.msg });
729
673
  }
730
674
  }
731
675
  if (errors.length > 0) {
732
676
  const errorLines = errors.map(err => `【${err.url.slice(0, 50)}${err.url.length > 50 ? '...' : ''}】: ${err.msg}`);
733
- const errorMsg = `❌ 解析失败列表(共${errors.length}个链接):\n${errorLines.join('\n')}`;
734
- logger.error(`解析失败数量: ${errors.length}, 错误码列表: ${errors.map(e => e.code).join(', ')}`);
677
+ const errorMsg = `❌ 解析失败:\n${errorLines.join('\n')}`;
735
678
  await sendTimeout(session, errorMsg);
736
679
  await delay(500);
737
680
  }
738
681
  if (items.length === 0) {
739
- const failMsg = getErrorInfo(ErrorCode.UNKNOWN_ERROR, '所有链接均解析失败,请检查链接是否有效/是否需要登录,或稍后重试');
740
- await sendTimeout(session, `⚠ ${failMsg}`);
682
+ await sendTimeout(session, '⚠ 未解析到有效内容');
741
683
  return;
742
684
  }
743
685
  const enableForward = config.enableForward && session.platform === 'onebot';
@@ -748,72 +690,31 @@ function apply(ctx, config) {
748
690
  if (enableForward) {
749
691
  if (item.text)
750
692
  forwardMessages.push(buildForwardNode(session, item.text, botName));
751
- if (item.cover && forwardMessages.length < 100)
693
+ if (item.cover)
752
694
  forwardMessages.push(buildForwardNode(session, koishi_1.h.image(item.cover), botName));
753
- if (item.type === '图集' && item.images?.length) {
754
- for (let i = 0; i < item.images.length && forwardMessages.length < 100; i++) {
755
- forwardMessages.push(buildForwardNode(session, koishi_1.h.image(item.images[i]), botName));
756
- }
757
- }
758
- if (item.type === 'image' && item.images?.length) {
759
- for (let i = 0; i < item.images.length && forwardMessages.length < 100; i++) {
760
- forwardMessages.push(buildForwardNode(session, koishi_1.h.image(item.images[i]), botName));
761
- }
762
- }
763
- if (item.type === 'live' || item.type === 'live_photo') {
764
- if (item.live_photo?.length) {
765
- for (let i = 0; i < item.live_photo.length && forwardMessages.length < 100; i++) {
766
- const liveItem = item.live_photo[i];
767
- forwardMessages.push(buildForwardNode(session, koishi_1.h.image(liveItem.image), botName));
768
- if (liveItem.video) {
769
- try {
770
- const videoElem = koishi_1.h.video(liveItem.video);
771
- forwardMessages.push(buildForwardNode(session, videoElem, botName));
772
- }
773
- catch (e) {
774
- forwardMessages.push(buildForwardNode(session, koishi_1.h.text(`视频链接: ${liveItem.video}`), botName));
775
- }
776
- }
777
- }
778
- }
779
- else if (item.images?.length) {
780
- for (let i = 0; i < item.images.length && forwardMessages.length < 100; i++) {
781
- forwardMessages.push(buildForwardNode(session, koishi_1.h.image(item.images[i]), botName));
782
- }
695
+ if ((item.type === '图集' || item.type === 'image') && item.images?.length) {
696
+ for (const img of item.images) {
697
+ forwardMessages.push(buildForwardNode(session, koishi_1.h.image(img), botName));
783
698
  }
784
699
  }
785
- if (item.video && config.showVideoFile && forwardMessages.length < 100) {
786
- let videoElem;
700
+ if (item.video && config.showVideoFile) {
787
701
  try {
788
702
  if (config.downloadVideoBeforeSend) {
789
703
  const filename = crypto_1.default.createHash('md5').update(item.video).digest('hex');
790
- const downloadResult = await downloadVideo(item.video, filename, config.userAgent, config.maxVideoSize, config.downloadThreads);
791
- if (downloadResult.code !== ErrorCode.SUCCESS) {
792
- const errorMsg = getErrorInfo(downloadResult.code);
793
- forwardMessages.push(buildForwardNode(session, koishi_1.h.text(`${errorMsg}\n链接:${item.video}`), botName));
704
+ const dl = await downloadVideo(item.video, filename, config.userAgent, config.maxVideoSize, config.downloadThreads);
705
+ if (dl.code === ErrorCode.SUCCESS) {
706
+ forwardMessages.push(buildForwardNode(session, koishi_1.h.file(dl.filePath), botName));
794
707
  }
795
708
  else {
796
- videoElem = koishi_1.h.file(downloadResult.filePath);
797
- forwardMessages.push(buildForwardNode(session, videoElem, botName));
709
+ forwardMessages.push(buildForwardNode(session, koishi_1.h.text(`视频:${item.video}`), botName));
798
710
  }
799
711
  }
800
712
  else {
801
- const fileSize = await getFileSize(item.video, config.userAgent);
802
- if (config.maxVideoSize > 0 && fileSize > config.maxVideoSize) {
803
- const code = ErrorCode.VIDEO_SIZE_EXCEEDED;
804
- const errorMsg = getErrorInfo(code, `${fileSize}MB超过限制${config.maxVideoSize}MB`);
805
- videoElem = koishi_1.h.text(`${errorMsg},仅发送链接:${item.video}`);
806
- }
807
- else {
808
- videoElem = koishi_1.h.video(item.video);
809
- }
713
+ forwardMessages.push(buildForwardNode(session, koishi_1.h.video(item.video), botName));
810
714
  }
811
715
  }
812
- catch (error) {
813
- const errorMsg = getErrorMessage(error);
814
- const code = ErrorCode.VIDEO_DOWNLOAD_FAILED;
815
- logger.error(`[${code}] 视频处理失败: ${errorMsg}`);
816
- forwardMessages.push(buildForwardNode(session, koishi_1.h.text(getErrorInfo(code, errorMsg) + `\n链接:${item.video}`), botName));
716
+ catch (e) {
717
+ forwardMessages.push(buildForwardNode(session, koishi_1.h.text(`视频:${item.video}`), botName));
817
718
  }
818
719
  }
819
720
  }
@@ -822,98 +723,37 @@ function apply(ctx, config) {
822
723
  await sendTimeout(session, item.text);
823
724
  await delay(300);
824
725
  }
825
- if (item.type === '图集' && item.images?.length) {
826
- const imgMsg = (0, koishi_1.h)('message', ...item.images.map((url) => koishi_1.h.image(url)));
827
- await sendTimeout(session, imgMsg);
726
+ if (item.cover) {
727
+ await sendTimeout(session, koishi_1.h.image(item.cover));
728
+ await delay(300);
828
729
  }
829
- else if (item.type === 'live' || item.type === 'live_photo') {
830
- if (item.live_photo?.length) {
831
- for (const liveItem of item.live_photo) {
832
- await sendTimeout(session, koishi_1.h.image(liveItem.image));
833
- await delay(200);
834
- if (liveItem.video) {
835
- try {
836
- await sendTimeout(session, koishi_1.h.video(liveItem.video));
837
- }
838
- catch (e) {
839
- await sendTimeout(session, koishi_1.h.text(`Live Photo 视频链接: ${liveItem.video}`));
840
- }
841
- await delay(300);
842
- }
843
- }
730
+ if ((item.type === '图集' || item.type === 'image') && item.images?.length) {
731
+ for (const img of item.images) {
732
+ await sendTimeout(session, koishi_1.h.image(img));
733
+ await delay(200);
844
734
  }
845
- else if (item.images?.length) {
846
- const imgMsg = (0, koishi_1.h)('message', ...item.images.map((url) => koishi_1.h.image(url)));
847
- await sendTimeout(session, imgMsg);
848
- }
849
- }
850
- else if (item.type === 'image' && item.images?.length) {
851
- const imgMsg = (0, koishi_1.h)('message', ...item.images.map((url) => koishi_1.h.image(url)));
852
- await sendTimeout(session, imgMsg);
853
735
  }
854
- else {
855
- if (item.cover) {
856
- await sendTimeout(session, koishi_1.h.image(item.cover));
857
- await delay(300);
736
+ if (item.video && config.showVideoFile) {
737
+ try {
738
+ await sendTimeout(session, koishi_1.h.video(item.video));
858
739
  }
859
- if (item.video && config.showVideoFile) {
860
- try {
861
- let videoElem;
862
- if (config.downloadVideoBeforeSend) {
863
- const filename = crypto_1.default.createHash('md5').update(item.video).digest('hex');
864
- const downloadResult = await downloadVideo(item.video, filename, config.userAgent, config.maxVideoSize, config.downloadThreads);
865
- if (downloadResult.code !== ErrorCode.SUCCESS) {
866
- await sendTimeout(session, koishi_1.h.text(`${getErrorInfo(downloadResult.code)}\n链接:${item.video}`));
867
- continue;
868
- }
869
- else {
870
- videoElem = koishi_1.h.file(downloadResult.filePath);
871
- }
872
- }
873
- else {
874
- const fileSize = await getFileSize(item.video, config.userAgent);
875
- if (config.maxVideoSize > 0 && fileSize > config.maxVideoSize) {
876
- const code = ErrorCode.VIDEO_SIZE_EXCEEDED;
877
- const errorMsg = getErrorInfo(code, `${fileSize}MB超过限制${config.maxVideoSize}MB`);
878
- videoElem = koishi_1.h.text(`${errorMsg},仅发送链接:${item.video}`);
879
- }
880
- else {
881
- videoElem = koishi_1.h.video(item.video);
882
- }
883
- }
884
- await sendTimeout(session, videoElem);
885
- }
886
- catch (error) {
887
- const errorMsg = getErrorMessage(error);
888
- const code = ErrorCode.VIDEO_DOWNLOAD_FAILED;
889
- logger.error(`[${code}] 视频处理失败: ${errorMsg}`);
890
- await sendTimeout(session, koishi_1.h.text(getErrorInfo(code, errorMsg) + `\n链接:${item.video}`));
891
- }
740
+ catch (e) {
741
+ await sendTimeout(session, `视频:${item.video}`);
892
742
  }
743
+ await delay(500);
893
744
  }
894
- await delay(1000);
895
745
  }
896
746
  }
897
- catch (error) {
898
- const errorMsg = getErrorMessage(error);
899
- const code = ErrorCode.UNKNOWN_ERROR;
900
- logger.error(`[${code}] 处理内容失败: ${errorMsg}`);
901
- await sendTimeout(session, koishi_1.h.text(getErrorInfo(code, `处理${item.type}内容失败: ${errorMsg}`)));
902
- }
747
+ catch (e) { }
903
748
  }
904
749
  if (enableForward && forwardMessages.length) {
905
750
  try {
906
- const safeForwardMessages = forwardMessages.slice(0, 100);
907
- const forwardMsg = (0, koishi_1.h)('message', { forward: true }, safeForwardMessages);
908
- await sendTimeout(session, forwardMsg);
751
+ await sendTimeout(session, (0, koishi_1.h)('message', { forward: true }, forwardMessages.slice(0, 100)));
909
752
  }
910
- catch (error) {
911
- const errorMsg = getErrorMessage(error);
912
- const code = ErrorCode.FORWARD_MESSAGE_FAILED;
913
- logger.error(`[${code}] 合并转发失败: ${errorMsg}`);
753
+ catch (e) {
914
754
  for (const node of forwardMessages) {
915
755
  await sendTimeout(session, node.data.content);
916
- await delay(500);
756
+ await delay(300);
917
757
  }
918
758
  }
919
759
  }
@@ -922,88 +762,26 @@ function apply(ctx, config) {
922
762
  if (!config.enable)
923
763
  return;
924
764
  const content = session.content?.trim() || '';
925
- let urls = extractUrl(content);
926
- if (urls.length === 0 && hasPlatformKeyword(content)) {
927
- const allLinks = content.match(/https?:\/\/[^\s\"\'\>\]]+/gi) || [];
928
- urls = allLinks.filter((u) => getPlatformType(u));
929
- }
930
- if (urls.length === 0)
931
- return;
932
- const key = `${session.platform}:${session.userId}:${session.channelId}`;
933
- if (linkBuffer.has(key)) {
934
- const buffer = linkBuffer.get(key);
935
- const newUrls = urls.filter(url => !buffer.urls.includes(url));
936
- if (newUrls.length) {
937
- buffer.urls.push(...newUrls);
938
- clearTimeout(buffer.timer);
939
- buffer.timer = setTimeout(() => flush(session), config.messageBufferDelay * 1000);
940
- }
765
+ const urls = extractUrl(content);
766
+ if (!urls.length)
941
767
  return;
942
- }
943
- let tipMsgId;
944
- if (config.showWaitingTip) {
945
- const msg = await sendTimeout(session, config.waitingTipText);
946
- tipMsgId = msg?.messageId || msg?.id || msg;
947
- }
948
- linkBuffer.set(key, {
949
- urls,
950
- timer: setTimeout(() => flush(session), config.messageBufferDelay * 1000),
951
- tipMsgId
952
- });
953
- });
954
- ctx.command('parse <url>', '手动解析视频链接')
955
- .action(async ({ session }, url) => {
956
- if (!url) {
957
- const code = ErrorCode.INVALID_URL;
958
- return getErrorInfo(code, '请输入视频链接');
959
- }
960
- let urls = extractUrl(url);
961
- if (urls.length === 0 && hasPlatformKeyword(url)) {
962
- const allLinks = url.match(/https?:\/\/[^\s\"\'\>\]]+/gi) || [];
963
- urls = allLinks.filter((u) => getPlatformType(u));
964
- }
965
- if (urls.length === 0) {
966
- const code = ErrorCode.UNSUPPORTED_PLATFORM;
967
- return getErrorInfo(code, '不支持该链接');
968
- }
768
+ if (config.showWaitingTip)
769
+ await sendTimeout(session, config.waitingTipText);
969
770
  await flush(session, urls);
970
771
  });
971
- ctx.command('clear-cache', '清空解析缓存与临时文件')
972
- .action(() => {
772
+ ctx.command('parse <url>', '手动解析视频').action(async ({ session }, url) => {
773
+ const us = extractUrl(url);
774
+ if (!us.length)
775
+ return getErrorInfo(ErrorCode.INVALID_URL);
776
+ await flush(session, us);
777
+ });
778
+ ctx.command('clear-cache', '清空缓存').action(() => {
973
779
  clearAllCache();
974
- return getErrorInfo(ErrorCode.SUCCESS, '解析缓存已清空');
780
+ return '✅ 缓存已清空';
975
781
  });
976
782
  setInterval(() => {
977
783
  const now = Date.now();
978
- processed.forEach((timestamp, hash) => {
979
- if (now - timestamp > 86400000)
980
- processed.delete(hash);
981
- });
784
+ processed.forEach((t, h) => now - t > 86400000 && processed.delete(h));
982
785
  }, 3600000);
983
- setInterval(() => {
984
- const tempDir = path_1.default.join(process.cwd(), 'temp_videos');
985
- if (!fs_1.default.existsSync(tempDir))
986
- return;
987
- const now = Date.now();
988
- fs_1.default.readdirSync(tempDir).forEach(file => {
989
- try {
990
- const stat = fs_1.default.statSync(path_1.default.join(tempDir, file));
991
- if (now - stat.mtimeMs > 3600000) {
992
- fs_1.default.unlinkSync(path_1.default.join(tempDir, file));
993
- logger.info(`清理过期临时文件: ${file}`);
994
- }
995
- }
996
- catch (error) {
997
- const errorMsg = getErrorMessage(error);
998
- logger.error(`[${ErrorCode.UNKNOWN_ERROR}] 清理临时文件失败: ${file}, ${errorMsg}`);
999
- }
1000
- });
1001
- }, 1800000);
1002
- if (config.autoClearCacheInterval > 0) {
1003
- setInterval(() => {
1004
- clearAllCache();
1005
- logger.info(getErrorInfo(ErrorCode.SUCCESS, '自动清理缓存完成'));
1006
- }, config.autoClearCacheInterval * 60000);
1007
- }
1008
- logger.info(getErrorInfo(ErrorCode.SUCCESS, '视频解析插件已加载'));
786
+ logger.info('✅ 视频解析插件已启动');
1009
787
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-video-parser-all",
3
3
  "description": "Koishi 全平台视频解析插件,支持抖音/快手/B站/小红书/微博/今日头条/皮皮搞笑/皮皮虾/最右视频链接解析",
4
- "version": "0.5.9",
4
+ "version": "0.6.1",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [