koishi-plugin-video-parser-all 0.6.7 → 0.6.9

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 +103 -72
  2. package/package.json +1 -1
package/lib/index.js CHANGED
@@ -15,12 +15,12 @@ const worker_threads_1 = require("worker_threads");
15
15
  exports.name = 'video-parser-all';
16
16
  exports.Config = koishi_1.Schema.intersect([
17
17
  koishi_1.Schema.object({
18
- enable: koishi_1.Schema.boolean().default(true).description('是否启用视频解析插件'),
19
- botName: koishi_1.Schema.string().default('视频解析机器人').description('机器人显示名称'),
20
- showWaitingTip: koishi_1.Schema.boolean().default(true).description('解析时显示等待提示'),
21
- waitingTipText: koishi_1.Schema.string().default('正在解析视频,请稍候...').description('等待提示文本内容'),
22
- sameLinkInterval: koishi_1.Schema.number().min(0).default(180).description('相同链接重复解析间隔(秒)'),
23
- }).description('基础设置'),
18
+ enable: koishi_1.Schema.boolean().default(true),
19
+ botName: koishi_1.Schema.string().default('视频解析机器人'),
20
+ showWaitingTip: koishi_1.Schema.boolean().default(true),
21
+ waitingTipText: koishi_1.Schema.string().default('正在解析视频,请稍候...'),
22
+ sameLinkInterval: koishi_1.Schema.number().min(0).default(180),
23
+ }),
24
24
  koishi_1.Schema.object({
25
25
  unifiedMessageFormat: koishi_1.Schema.string().role('textarea').default(`标题:${'${标题}'}
26
26
  作者:${'${作者}'}
@@ -42,38 +42,37 @@ IP属地:${'${IP属地}'}
42
42
  直播间ID:${'${直播间ID}'}
43
43
  直播间状态:${'${直播间状态}'}
44
44
  图片数量:${'${图片数量}'}
45
- 作者ID:${'${作者ID}'}
46
- 视频备用链接:${'${视频备用链接}'}`).description('统一消息格式'),
47
- }).description('统一消息格式'),
45
+ 作者ID:${'${作者ID}'}`),
46
+ }),
48
47
  koishi_1.Schema.object({
49
- showImageText: koishi_1.Schema.boolean().default(true).description('显示图文内容'),
50
- showVideoFile: koishi_1.Schema.boolean().default(true).description('发送视频文件(关闭则只发链接)'),
51
- }).description('内容显示设置'),
48
+ showImageText: koishi_1.Schema.boolean().default(true),
49
+ showVideoFile: koishi_1.Schema.boolean().default(true),
50
+ }),
52
51
  koishi_1.Schema.object({
53
- maxDescLength: koishi_1.Schema.number().default(200).description('简介内容最大长度(字符)'),
54
- }).description('内容长度限制'),
52
+ maxDescLength: koishi_1.Schema.number().default(200),
53
+ }),
55
54
  koishi_1.Schema.object({
56
- timeout: koishi_1.Schema.number().min(0).default(180000).description('API请求超时时间(毫秒)'),
57
- videoSendTimeout: koishi_1.Schema.number().min(0).default(0).description('视频发送超时时间(毫秒,0为不限制)'),
58
- userAgent: koishi_1.Schema.string().default('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36').description('请求UA标识'),
59
- }).description('网络与API设置'),
55
+ timeout: koishi_1.Schema.number().min(0).default(180000),
56
+ videoSendTimeout: koishi_1.Schema.number().min(0).default(0),
57
+ userAgent: koishi_1.Schema.string().default('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36'),
58
+ }),
60
59
  koishi_1.Schema.object({
61
- ignoreSendError: koishi_1.Schema.boolean().default(true).description('忽略发送失败错误'),
62
- retryTimes: koishi_1.Schema.number().min(0).default(3).description('API请求重试次数'),
63
- retryInterval: koishi_1.Schema.number().min(0).default(1000).description('重试间隔时间(毫秒)'),
64
- }).description('错误与重试设置'),
60
+ ignoreSendError: koishi_1.Schema.boolean().default(true),
61
+ retryTimes: koishi_1.Schema.number().min(0).default(3),
62
+ retryInterval: koishi_1.Schema.number().min(0).default(1000),
63
+ }),
65
64
  koishi_1.Schema.object({
66
- enableForward: koishi_1.Schema.boolean().default(false).description('启用合并转发(仅OneBot平台)'),
67
- downloadVideoBeforeSend: koishi_1.Schema.boolean().default(false).description('发送前先下载视频'),
68
- maxVideoSize: koishi_1.Schema.number().min(0).default(0).description('最大视频大小限制(MB,0为不限制)'),
69
- downloadThreads: koishi_1.Schema.number().min(0).max(10).default(0).description('多线程下载线程数(0为不使用多线程)'),
70
- }).description('发送方式设置'),
65
+ enableForward: koishi_1.Schema.boolean().default(false),
66
+ downloadVideoBeforeSend: koishi_1.Schema.boolean().default(false),
67
+ maxVideoSize: koishi_1.Schema.number().min(0).default(0),
68
+ downloadThreads: koishi_1.Schema.number().min(0).max(10).default(0),
69
+ }),
71
70
  koishi_1.Schema.object({
72
- messageBufferDelay: koishi_1.Schema.number().min(0).default(0).description('消息缓冲延迟(毫秒)'),
73
- }).description('消息处理设置'),
71
+ messageBufferDelay: koishi_1.Schema.number().min(0).default(0),
72
+ }),
74
73
  koishi_1.Schema.object({
75
- autoClearCacheInterval: koishi_1.Schema.number().min(0).default(0).description('自动清理缓存间隔(分钟,0为关闭)'),
76
- }).description('缓存清理设置'),
74
+ autoClearCacheInterval: koishi_1.Schema.number().min(0).default(0),
75
+ }),
77
76
  ]);
78
77
  var ErrorCode;
79
78
  (function (ErrorCode) {
@@ -166,8 +165,7 @@ const VARIABLE_MAPPING = {
166
165
  '直播间ID': ['room_id', 'live.room_id', 'data.room_id', 'live.room_id', 'data.live.room_id'],
167
166
  '直播间状态': ['status', 'live.status', 'data.status', 'room.status', 'data.live.status'],
168
167
  '图片数量': ['count', 'data.count', 'item.count', 'images.length', 'data.images.length', 'data.item.count'],
169
- '作者ID': ['userId', 'userID', 'author_id', 'data.userId', 'item.userID', 'author.mid', 'user.mid', 'data.item.userID', 'data.author_id', 'data.user.mid', 'author.id', 'uid', 'short_id', 'data.author.id'],
170
- '视频备用链接': ['data.video_backup', 'video_backup']
168
+ '作者ID': ['userId', 'userID', 'author_id', 'data.userId', 'item.userID', 'author.mid', 'user.mid', 'data.item.userID', 'data.author_id', 'data.user.mid', 'author.id', 'uid', 'short_id', 'data.author.id']
171
169
  };
172
170
  function getErrorInfo(code, detail) {
173
171
  const baseMsg = exports.ErrorMessageMap[code] || exports.ErrorMessageMap[ErrorCode.UNKNOWN_ERROR];
@@ -391,7 +389,6 @@ function formatPublishTime(value) {
391
389
  if (!value)
392
390
  return '';
393
391
  const str = String(value).trim();
394
- // 适配B站的 ctime 时间戳字段名(避免直接传入字符串"ctime")
395
392
  if (value === 'ctime')
396
393
  return '';
397
394
  if (/^\d{10}$/.test(str)) {
@@ -469,11 +466,24 @@ function parseData(rawResponse, maxDescLength) {
469
466
  Object.entries(VARIABLE_MAPPING).forEach(([varName, keys]) => {
470
467
  let value = findValueInObject(data, keys) || findValueInObject(root, keys);
471
468
  if (varName === '图片数量' && value === undefined) {
472
- value = Array.isArray(data.images) ? data.images.length :
473
- Array.isArray(root.images) ? root.images.length :
474
- Array.isArray(data.imgurl) ? data.imgurl.length :
475
- Array.isArray(root.imgurl) ? root.imgurl.length :
476
- data.count || root.count || undefined;
469
+ let imgCount = 0;
470
+ const imgSources = [
471
+ data.images, data.pics, data.pic_urls, data.image_list, data.imgurl,
472
+ root.images, root.pics, root.pic_urls, root.image_list, root.imgurl,
473
+ data.item?.images
474
+ ];
475
+ for (const source of imgSources) {
476
+ if (Array.isArray(source) && source.length > 0) {
477
+ imgCount = source.filter(i => i && typeof i === 'string').length;
478
+ break;
479
+ }
480
+ }
481
+ const cover = data.cover || data.video?.fm || data.imgurl || data.pic || data.thumbnail || data.cover_url ||
482
+ data.item?.cover || root.cover || data.live?.cover || data.live?.keyframe || '';
483
+ if (cover && imgCount > 0) {
484
+ imgCount = imgSources.find(source => Array.isArray(source))?.filter(i => i && typeof i === 'string' && i !== cover).length || 0;
485
+ }
486
+ value = imgCount;
477
487
  }
478
488
  if (value !== undefined && value !== null && value !== '' && value !== 0) {
479
489
  stat[varName] = value;
@@ -494,9 +504,13 @@ function parseData(rawResponse, maxDescLength) {
494
504
  const title = stat['标题'] || data.note_title || data.title || data.content_title || data.video?.title ||
495
505
  data.item?.title || root.title || data.live?.title || '无标题';
496
506
  let author = stat['作者'] || data.author?.name || data.nickname || data.user_name || data.owner?.name ||
497
- data.item?.author || root.author || data.user?.name || data.live?.author || '未知作者';
498
- if (typeof author === 'object' && author.name)
499
- author = author.name;
507
+ data.item?.author || root.author || data.user?.name || data.live?.author || '';
508
+ if (typeof author === 'object') {
509
+ author = '';
510
+ }
511
+ else {
512
+ author = author || '未知作者';
513
+ }
500
514
  const rawDesc = stat['简介'] || data.note_desc || data.content || data.text || data.description ||
501
515
  data.video?.desc || data.item?.description || root.desc || root.description ||
502
516
  data.live?.desc || (title !== '无标题' ? title : '') || '暂无简介';
@@ -514,7 +528,7 @@ function parseData(rawResponse, maxDescLength) {
514
528
  ];
515
529
  for (const source of imgSources) {
516
530
  if (Array.isArray(source) && source.length > 0) {
517
- images = source.filter(i => i && typeof i === 'string');
531
+ images = source.filter(i => i && typeof i === 'string' && i !== cover);
518
532
  break;
519
533
  }
520
534
  }
@@ -529,11 +543,15 @@ function parseData(rawResponse, maxDescLength) {
529
543
  stat['发布时间'] = pubTime;
530
544
  else
531
545
  delete stat['发布时间'];
532
- const durShow = durationFormatted === '00:00:00' && (String(durationValue).length > 12) ? '' : durationFormatted;
533
- if (durShow)
534
- stat['视频时长'] = durShow;
535
- else
546
+ if (durationFormatted !== '00:00:00') {
547
+ stat['视频时长'] = durationFormatted;
548
+ }
549
+ else {
536
550
  delete stat['视频时长'];
551
+ }
552
+ if (stat['图片数量'] === 0) {
553
+ delete stat['图片数量'];
554
+ }
537
555
  const sizeVal = stat['文件大小'];
538
556
  if (sizeVal && !String(sizeVal).includes('MB')) {
539
557
  const num = Number(sizeVal);
@@ -560,9 +578,6 @@ function parseData(rawResponse, maxDescLength) {
560
578
  stat['粉丝数'] = data.followers_count;
561
579
  if (data.ip_info_str)
562
580
  stat['IP属地'] = data.ip_info_str;
563
- // 处理小红书视频备用链接
564
- if (data.video_backup)
565
- stat['视频备用链接'] = data.video_backup;
566
581
  return {
567
582
  type: type,
568
583
  rawData: rawResponse,
@@ -599,8 +614,7 @@ function generateFormattedText(parseData, config) {
599
614
  收藏:${'${收藏数}'}
600
615
  转发:${'${转发数}'}
601
616
  播放:${'${播放数}'}
602
- 评论:${'${评论数}'}
603
- 视频备用链接:${'${视频备用链接}'}`;
617
+ 评论:${'${评论数}'}`;
604
618
  }
605
619
  let result = format;
606
620
  const varMatches = result.match(/\$\{([^}]+)\}/g) || [];
@@ -666,14 +680,27 @@ function apply(ctx, config) {
666
680
  for (let i = 0; i <= retryTimes; i++) {
667
681
  try {
668
682
  const params = { url, proxyurl: '' };
669
- const res = await http.get(API_CONFIG[platform], {
670
- params,
671
- timeout: config.timeout,
672
- headers: {
673
- 'X-Requested-With': 'XMLHttpRequest',
674
- 'Origin': platform === 'xiaohongshu' ? 'https://api.bugpk.com' : 'https://www.baidu.com'
675
- }
676
- });
683
+ let res;
684
+ if (platform === 'xiaohongshu') {
685
+ res = await http.post(API_CONFIG[platform], new URLSearchParams(params), {
686
+ timeout: config.timeout,
687
+ headers: {
688
+ 'X-Requested-With': 'XMLHttpRequest',
689
+ 'Origin': 'https://api.bugpk.com',
690
+ 'Content-Type': 'application/x-www-form-urlencoded'
691
+ }
692
+ });
693
+ }
694
+ else {
695
+ res = await http.get(API_CONFIG[platform], {
696
+ params,
697
+ timeout: config.timeout,
698
+ headers: {
699
+ 'X-Requested-With': 'XMLHttpRequest',
700
+ 'Origin': 'https://www.baidu.com'
701
+ }
702
+ });
703
+ }
677
704
  return res.data;
678
705
  }
679
706
  catch (error) {
@@ -844,12 +871,8 @@ function apply(ctx, config) {
844
871
  if (enableForward) {
845
872
  if (item.text)
846
873
  forwardMessages.push(buildForwardNode(session, item.text, botName));
847
- if (item.cover)
874
+ if (item.cover && item.type !== '图集') {
848
875
  forwardMessages.push(buildForwardNode(session, koishi_1.h.image(item.cover), botName));
849
- if ((item.type === '图集' || item.type === 'image') && item.images?.length) {
850
- for (const img of item.images) {
851
- forwardMessages.push(buildForwardNode(session, koishi_1.h.image(img), botName));
852
- }
853
876
  }
854
877
  if (item.video && config.showVideoFile) {
855
878
  try {
@@ -871,22 +894,22 @@ function apply(ctx, config) {
871
894
  forwardMessages.push(buildForwardNode(session, koishi_1.h.video(item.video), botName));
872
895
  }
873
896
  }
897
+ if ((item.type === '图集' || item.type === 'image') && item.images?.length) {
898
+ forwardMessages.push(buildForwardNode(session, `📸 图集内容(共${item.images.length}张)`, botName));
899
+ for (const img of item.images) {
900
+ forwardMessages.push(buildForwardNode(session, koishi_1.h.image(img), botName));
901
+ }
902
+ }
874
903
  }
875
904
  else {
876
905
  if (item.text) {
877
906
  await sendTimeout(session, item.text);
878
907
  await delay(300);
879
908
  }
880
- if (item.cover) {
909
+ if (item.cover && item.type !== '图集') {
881
910
  await sendTimeout(session, koishi_1.h.image(item.cover));
882
911
  await delay(300);
883
912
  }
884
- if ((item.type === '图集' || item.type === 'image') && item.images?.length) {
885
- for (const img of item.images) {
886
- await sendTimeout(session, koishi_1.h.image(img));
887
- await delay(200);
888
- }
889
- }
890
913
  if (item.video && config.showVideoFile) {
891
914
  try {
892
915
  await sendTimeout(session, koishi_1.h.video(item.video));
@@ -896,6 +919,14 @@ function apply(ctx, config) {
896
919
  }
897
920
  await delay(500);
898
921
  }
922
+ if ((item.type === '图集' || item.type === 'image') && item.images?.length) {
923
+ await sendTimeout(session, `📸 图集内容(共${item.images.length}张)`);
924
+ await delay(300);
925
+ for (const img of item.images) {
926
+ await sendTimeout(session, koishi_1.h.image(img));
927
+ await delay(200);
928
+ }
929
+ }
899
930
  }
900
931
  }
901
932
  catch (e) { }
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.6.7",
4
+ "version": "0.6.9",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [