koishi-plugin-video-parser-all 0.6.6 → 0.6.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.
Files changed (2) hide show
  1. package/lib/index.js +75 -46
  2. package/package.json +1 -1
package/lib/index.js CHANGED
@@ -42,7 +42,8 @@ IP属地:${'${IP属地}'}
42
42
  直播间ID:${'${直播间ID}'}
43
43
  直播间状态:${'${直播间状态}'}
44
44
  图片数量:${'${图片数量}'}
45
- 作者ID:${'${作者ID}'}`).description('统一消息格式'),
45
+ 作者ID:${'${作者ID}'}
46
+ 视频备用链接:${'${视频备用链接}'}`).description('统一消息格式'),
46
47
  }).description('统一消息格式'),
47
48
  koishi_1.Schema.object({
48
49
  showImageText: koishi_1.Schema.boolean().default(true).description('显示图文内容'),
@@ -145,18 +146,18 @@ const API_CONFIG = {
145
146
  zuiyou: 'https://api.bugpk.com/api/zuiyou'
146
147
  };
147
148
  const VARIABLE_MAPPING = {
148
- '标题': ['title', 'Title', 'TITLE', 'note_title', 'content_title', 'item.title', 'data.title', 'video.title', 'live.title', 'data.item.title'],
149
- '作者': ['author.name', 'author', 'name', 'Author', 'Name', 'owner.name', 'nickname', 'user_name', 'data.author', 'item.author', 'user.name', 'live.author', 'data.user.name', 'data.author.name'],
150
- '简介': ['desc', 'description', 'Desc', 'Description', 'content', 'Content', 'note_desc', 'text', 'data.desc', 'item.description', 'live.desc', 'data.item.description'],
151
- '视频时长': ['duration', 'Duration', 'time', 'Time', 'video_duration', 'item.duration', 'stat.duration', 'data.item.duration'],
152
- '点赞数': ['like', 'Like', 'attitudes_count', 'digg_count', 'praise', 'stat.like', 'liked_count', 'data.like', 'data.attitudes_count', 'item.attitudes_count', 'data.item.attitudes_count'],
153
- '投币数': ['coin', 'Coin', 'bi', 'Bi', 'stat.coin', 'stast.coin'],
154
- '收藏数': ['collect', 'Collect', 'favorite', 'Favorite', 'star', 'Star', 'stat.collect', 'collected_count', 'stast.favorite', 'data.favorite', 'stast.favorite'],
155
- '转发数': ['share', 'Share', 'forward', 'Forward', 'repost', 'stat.share', 'reposts_count', 'shared_count', 'stast.share', 'data.reposts_count', 'data.item.reposts_count'],
156
- '播放数': ['view', 'View', 'play_count', 'PlayCount', 'play', 'stat.view', 'play_times', 'stast.view', 'data.play_count', 'item.play_count', 'data.item.play_count'],
157
- '评论数': ['comment', 'Comment', 'comments_count', 'comment_count', 'discuss', 'stat.comment', 'stast.reply', 'data.comments_count', 'item.comments_count', 'data.item.comments_count'],
149
+ '标题': ['title', 'note_title', 'content_title', 'item.title', 'data.title', 'video.title', 'live.title', 'data.item.title', 'data.live.title'],
150
+ '作者': ['author', 'author.name', 'name', 'nickname', 'user_name', 'owner.name', 'data.author', 'item.author', 'user.name', 'live.author', 'data.user.name', 'data.author.name'],
151
+ '简介': ['desc', 'description', 'content', 'note_desc', 'text', 'data.desc', 'item.description', 'live.desc', 'data.item.description'],
152
+ '视频时长': ['duration', 'time', 'video_duration', 'item.duration', 'stat.duration', 'data.item.duration'],
153
+ '点赞数': ['like', 'attitudes_count', 'digg_count', 'praise', 'stat.like', 'liked_count', 'data.like', 'data.attitudes_count', 'item.attitudes_count', 'data.item.attitudes_count'],
154
+ '投币数': ['coin', 'bi', 'stat.coin', 'stast.coin'],
155
+ '收藏数': ['collect', 'favorite', 'star', 'stat.collect', 'collected_count', 'stast.favorite', 'data.favorite'],
156
+ '转发数': ['share', 'forward', 'repost', 'stat.share', 'reposts_count', 'shared_count', 'stast.share', 'data.reposts_count', 'data.item.reposts_count'],
157
+ '播放数': ['view', 'play_count', 'play', 'stat.view', 'play_times', 'stast.view', 'data.play_count', 'item.play_count', 'data.item.play_count'],
158
+ '评论数': ['comment', 'comments_count', 'comment_count', 'discuss', 'stat.comment', 'stast.reply', 'data.comments_count', 'item.comments_count', 'data.item.comments_count', 'stat.reply'],
158
159
  'IP属地': ['ip_info_str', 'data.ip_info_str', 'item.ip_info', 'data.item.ip_info_str'],
159
- '发布时间': ['date', 'time', 'publish_time', 'data.date', 'item.publish_time', 'live.time', 'stast.publish_time', 'stat.time', 'data.time.publish_time', 'data.live.time'],
160
+ '发布时间': ['date', 'time', 'publish_time', 'data.date', 'item.publish_time', 'live.time', 'stast.publish_time', 'stat.time', 'data.time.publish_time', 'data.live.time', 'stat.ctime'],
160
161
  '粉丝数': ['followers_count', 'data.followers_count', 'item.followers', 'author.fans', 'data.item.followers_count'],
161
162
  '在线人数': ['online', 'data.online', 'live.online', 'room.online', 'data.live.online'],
162
163
  '关注数': ['attention', 'data.attention', 'live.attention', 'stast.attention', 'data.live.attention'],
@@ -165,7 +166,8 @@ const VARIABLE_MAPPING = {
165
166
  '直播间ID': ['room_id', 'live.room_id', 'data.room_id', 'live.room_id', 'data.live.room_id'],
166
167
  '直播间状态': ['status', 'live.status', 'data.status', 'room.status', 'data.live.status'],
167
168
  '图片数量': ['count', 'data.count', 'item.count', 'images.length', 'data.images.length', 'data.item.count'],
168
- '作者ID': ['userId', 'userID', 'author_id', 'data.userId', 'item.userID', 'author.mid', 'user.mid', 'data.item.userID', 'data.author_id', 'data.user.mid'],
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']
169
171
  };
170
172
  function getErrorInfo(code, detail) {
171
173
  const baseMsg = exports.ErrorMessageMap[code] || exports.ErrorMessageMap[ErrorCode.UNKNOWN_ERROR];
@@ -389,15 +391,21 @@ function formatPublishTime(value) {
389
391
  if (!value)
390
392
  return '';
391
393
  const str = String(value).trim();
394
+ // 适配B站的 ctime 时间戳字段名(避免直接传入字符串"ctime")
395
+ if (value === 'ctime')
396
+ return '';
397
+ if (/^\d{10}$/.test(str)) {
398
+ value = Number(str) * 1000;
399
+ }
392
400
  if (/^\d{10,}$/.test(str) && Number(str) > 1e12) {
393
401
  if (Number(str) > 1e15) {
394
402
  value = Number(str) / 1000;
395
403
  }
396
404
  }
397
405
  try {
398
- const d = new Date(/^\d+$/.test(str) ? Number(str) * 1000 : str);
406
+ const d = new Date(/^\d+$/.test(str) ? Number(str) : str);
399
407
  if (isNaN(d.getTime()))
400
- return '';
408
+ return str;
401
409
  const y = d.getFullYear();
402
410
  const m = (d.getMonth() + 1).toString().padStart(2, '0');
403
411
  const d_ = d.getDate().toString().padStart(2, '0');
@@ -455,18 +463,19 @@ function findValueInObject(obj, keys) {
455
463
  return undefined;
456
464
  }
457
465
  function parseData(rawResponse, maxDescLength) {
458
- const rootData = rawResponse || {};
459
- const data = rootData.data || rootData.result || rootData || {};
466
+ const root = rawResponse || {};
467
+ const data = root.data || root.result || root || {};
460
468
  const stat = {};
461
469
  Object.entries(VARIABLE_MAPPING).forEach(([varName, keys]) => {
462
- let value = findValueInObject(data, keys) || findValueInObject(rootData, keys);
470
+ let value = findValueInObject(data, keys) || findValueInObject(root, keys);
463
471
  if (varName === '图片数量' && value === undefined) {
464
472
  value = Array.isArray(data.images) ? data.images.length :
465
- Array.isArray(rootData.images) ? rootData.images.length :
473
+ Array.isArray(root.images) ? root.images.length :
466
474
  Array.isArray(data.imgurl) ? data.imgurl.length :
467
- Array.isArray(rootData.imgurl) ? rootData.imgurl.length : undefined;
475
+ Array.isArray(root.imgurl) ? root.imgurl.length :
476
+ data.count || root.count || undefined;
468
477
  }
469
- if (value !== undefined && value !== null && value !== '') {
478
+ if (value !== undefined && value !== null && value !== '' && value !== 0) {
470
479
  stat[varName] = value;
471
480
  }
472
481
  });
@@ -475,28 +484,32 @@ function parseData(rawResponse, maxDescLength) {
475
484
  type = data.jx.type;
476
485
  else if (data.type)
477
486
  type = data.type;
478
- else if (rootData.msg === 'cv')
487
+ else if (root.msg === 'cv')
479
488
  type = 'cv';
480
- else if (rootData.msg === 'live')
489
+ else if (root.msg === 'live')
481
490
  type = 'live';
482
- else if ((data.images && data.images.length > 1) || (rootData.images && rootData.images.length > 1) ||
483
- (data.imgurl && data.imgurl.length > 1) || (rootData.imgurl && rootData.imgurl.length > 1))
491
+ else if ((data.images && data.images.length > 1) || (root.images && root.images.length > 1) ||
492
+ (data.imgurl && data.imgurl.length > 1) || (root.imgurl && root.imgurl.length > 1))
484
493
  type = '图集';
485
- const title = stat['标题'] || data.note_title || data.title || data.content_title || data.item?.title || rootData.title || '无标题';
486
- let author = stat['作者'] || data.author?.name || data.nickname || data.user_name || data.item?.author ||
487
- rootData.author || data.user?.name || data.owner?.name || '未知作者';
494
+ const title = stat['标题'] || data.note_title || data.title || data.content_title || data.video?.title ||
495
+ data.item?.title || root.title || data.live?.title || '无标题';
496
+ 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 || '未知作者';
488
498
  if (typeof author === 'object' && author.name)
489
499
  author = author.name;
490
500
  const rawDesc = stat['简介'] || data.note_desc || data.content || data.text || data.description ||
491
- data.item?.description || rootData.desc || rootData.description || '';
492
- const desc = rawDesc.length > 0 ? rawDesc.slice(0, maxDescLength) : (title.length > 0 ? title : '暂无简介');
493
- const cover = data.cover || data.imgurl || data.pic || data.thumbnail || data.cover_url ||
494
- data.item?.cover || rootData.cover || (Array.isArray(data.images) && data.images[0]) ||
495
- (Array.isArray(rootData.images) && rootData.images[0]) || '';
501
+ data.video?.desc || data.item?.description || root.desc || root.description ||
502
+ data.live?.desc || (title !== '无标题' ? title : '') || '暂无简介';
503
+ const desc = rawDesc.length > 0 ? rawDesc.slice(0, maxDescLength) : '暂无简介';
504
+ const cover = data.cover || data.video?.fm || data.imgurl || data.pic || data.thumbnail || data.cover_url ||
505
+ data.item?.cover || root.cover || data.live?.cover || data.live?.keyframe ||
506
+ (Array.isArray(data.images) && data.images[0]) ||
507
+ (Array.isArray(root.images) && root.images[0]) ||
508
+ (Array.isArray(data.imgurl) && data.imgurl[0]) || '';
496
509
  let images = [];
497
510
  const imgSources = [
498
511
  data.images, data.pics, data.pic_urls, data.image_list, data.imgurl,
499
- rootData.images, rootData.pics, rootData.pic_urls, rootData.image_list, rootData.imgurl,
512
+ root.images, root.pics, root.pic_urls, root.image_list, root.imgurl,
500
513
  data.item?.images
501
514
  ];
502
515
  for (const source of imgSources) {
@@ -505,8 +518,9 @@ function parseData(rawResponse, maxDescLength) {
505
518
  break;
506
519
  }
507
520
  }
508
- let video = data.video?.url || data.item?.url || data.url || data.download_url || data.playUrl ||
509
- data.video_url || rootData.url || data.item?.url || '';
521
+ let video = data.video?.url || data.url || data.download_url || data.playUrl ||
522
+ data.video_url || root.url || data.item?.url || data.live?.url ||
523
+ (data.live?.url && Array.isArray(data.live.url) ? data.live.url[0] : '') || '';
510
524
  const durationValue = stat['视频时长'] || data.item?.duration || data.duration || 0;
511
525
  const duration = typeof durationValue === 'number' ? durationValue : parseInt(durationValue) || 0;
512
526
  const durationFormatted = formatDuration(durationValue);
@@ -526,15 +540,29 @@ function parseData(rawResponse, maxDescLength) {
526
540
  if (!isNaN(num) && num > 0)
527
541
  stat['文件大小'] = `${num.toFixed(2)} MB`;
528
542
  }
529
- const live_photo = data.live_photo || rootData.live_photo || [];
530
- const h_w = data.item?.h_w || rootData.h_w || [];
531
- const quality_urls = data.quality_urls || rootData.quality_urls || {};
532
- const default_quality = data.default_quality || rootData.default_quality || '';
543
+ const live_photo = data.live_photo || root.live_photo || [];
544
+ const h_w = data.item?.h_w || root.h_w || [];
545
+ const quality_urls = data.quality_urls || root.quality_urls || {};
546
+ const default_quality = data.default_quality || root.default_quality || '';
533
547
  const download_url = data.download_url || video;
534
- const play_count = stat['播放数'] || '';
535
- const reposts_count = Number(stat['转发数']) || data.reposts_count || rootData.reposts_count || 0;
536
- const attitudes_count = Number(stat['点赞数']) || data.attitudes_count || rootData.attitudes_count || 0;
537
- const comments_count = Number(stat['评论数']) || data.comments_count || rootData.comments_count || 0;
548
+ const play_count = stat['播放数'] || data.play_count || root.play_count || '';
549
+ const reposts_count = Number(stat['转发数']) || data.reposts_count || root.reposts_count || 0;
550
+ const attitudes_count = Number(stat['点赞数']) || data.attitudes_count || root.attitudes_count || data.like || root.like || 0;
551
+ const comments_count = Number(stat['评论数']) || data.comments_count || root.comments_count || 0;
552
+ if (data.live) {
553
+ stat['直播间地址'] = data.live.room_url || '';
554
+ stat['直播间ID'] = data.live.room_id || '';
555
+ stat['直播间状态'] = data.live.status === 1 ? '直播中' : (data.live.status === 0 ? '未开播' : data.live.status || '未知');
556
+ stat['在线人数'] = data.live.online || '';
557
+ stat['关注数'] = data.live.attention || '';
558
+ }
559
+ if (data.followers_count)
560
+ stat['粉丝数'] = data.followers_count;
561
+ if (data.ip_info_str)
562
+ stat['IP属地'] = data.ip_info_str;
563
+ // 处理小红书视频备用链接
564
+ if (data.video_backup)
565
+ stat['视频备用链接'] = data.video_backup;
538
566
  return {
539
567
  type: type,
540
568
  rawData: rawResponse,
@@ -571,7 +599,8 @@ function generateFormattedText(parseData, config) {
571
599
  收藏:${'${收藏数}'}
572
600
  转发:${'${转发数}'}
573
601
  播放:${'${播放数}'}
574
- 评论:${'${评论数}'}`;
602
+ 评论:${'${评论数}'}
603
+ 视频备用链接:${'${视频备用链接}'}`;
575
604
  }
576
605
  let result = format;
577
606
  const varMatches = result.match(/\$\{([^}]+)\}/g) || [];
@@ -682,7 +711,7 @@ function apply(ctx, config) {
682
711
  return { data: null, code, msg };
683
712
  }
684
713
  const isSuccess = resData.code === 0 || resData.code === 200 || resData.code === 1 ||
685
- (resData.msg && (resData.msg.includes('解析成功') || resData.msg.includes('success') || resData.msg.includes('请求成功'))) ||
714
+ (resData.msg && (resData.msg.includes('解析成功') || resData.msg.includes('success') || resData.msg.includes('请求成功') || resData.msg === 'video' || resData.msg === 'cv' || resData.msg === 'live')) ||
686
715
  !!resData.data || !!resData.result || !!resData.video || !!resData.images || !!resData.imgurl;
687
716
  if (!isSuccess) {
688
717
  const apiErrorMsg = resData.msg || resData.error || '解析失败';
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.6",
4
+ "version": "0.6.7",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [