koishi-plugin-video-parser-all 0.6.5 → 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 +109 -60
  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('显示图文内容'),
@@ -141,31 +142,32 @@ const API_CONFIG = {
141
142
  weibo: 'https://api.bugpk.com/api/weibo',
142
143
  toutiao: 'https://api.bugpk.com/api/toutiao',
143
144
  pipigx: 'https://api.bugpk.com/api/pipigx',
144
- pipixia: 'https://api.bugpk.com/api/ppx',
145
+ pipixia: 'https://api.bugpk.com/api/pipixia',
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'],
149
- '作者': ['author.name', 'author', 'name', 'Author', 'Name', 'owner.name', 'nickname', 'user_name', 'data.author', 'item.author', 'user.name', 'live.author'],
150
- '简介': ['desc', 'description', 'Desc', 'Description', 'content', 'Content', 'note_desc', 'text', 'data.desc', 'item.description', 'live.desc'],
151
- '视频时长': ['duration', 'Duration', 'time', 'Time', 'video_duration', 'item.duration', 'stat.duration'],
152
- '点赞数': ['like', 'Like', 'attitudes_count', 'digg_count', 'praise', 'stat.like', 'liked_count', 'data.like', 'data.attitudes_count', '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'],
155
- '转发数': ['share', 'Share', 'forward', 'Forward', 'repost', 'stat.share', 'reposts_count', 'shared_count', 'stast.share', 'data.reposts_count'],
156
- '播放数': ['view', 'View', 'play_count', 'PlayCount', 'play', 'stat.view', 'play_times', 'stast.view', 'data.play_count', 'item.play_count'],
157
- '评论数': ['comment', 'Comment', 'comments_count', 'comment_count', 'discuss', 'stat.comment', 'stast.reply', 'data.comments_count', 'item.comments_count'],
158
- 'IP属地': ['ip_info_str', 'data.ip_info_str', 'item.ip_info'],
159
- '发布时间': ['date', 'time', 'publish_time', 'data.date', 'item.publish_time', 'live.time', 'stast.publish_time'],
160
- '粉丝数': ['followers_count', 'data.followers_count', 'item.followers', 'author.fans'],
161
- '在线人数': ['online', 'data.online', 'live.online', 'room.online'],
162
- '关注数': ['attention', 'data.attention', 'live.attention', 'stast.attention'],
163
- '文件大小': ['size', 'size_str', 'item.size', 'item.size_str', 'data.size'],
164
- '直播间地址': ['room_url', 'live.room_url', 'data.room_url', 'live.url'],
165
- '直播间ID': ['room_id', 'live.room_id', 'data.room_id', 'live.room_id'],
166
- '直播间状态': ['status', 'live.status', 'data.status', 'room.status'],
167
- '图片数量': ['count', 'data.count', 'item.count', 'images.length', 'data.images.length'],
168
- '作者ID': ['userId', 'userID', 'author_id', 'data.userId', 'item.userID', 'author.mid', 'user.mid'],
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'],
159
+ 'IP属地': ['ip_info_str', 'data.ip_info_str', 'item.ip_info', 'data.item.ip_info_str'],
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'],
161
+ '粉丝数': ['followers_count', 'data.followers_count', 'item.followers', 'author.fans', 'data.item.followers_count'],
162
+ '在线人数': ['online', 'data.online', 'live.online', 'room.online', 'data.live.online'],
163
+ '关注数': ['attention', 'data.attention', 'live.attention', 'stast.attention', 'data.live.attention'],
164
+ '文件大小': ['size', 'size_str', 'item.size', 'item.size_str', 'data.size', 'data.item.size_str'],
165
+ '直播间地址': ['room_url', 'live.room_url', 'data.room_url', 'live.url', 'data.live.room_url'],
166
+ '直播间ID': ['room_id', 'live.room_id', 'data.room_id', 'live.room_id', 'data.live.room_id'],
167
+ '直播间状态': ['status', 'live.status', 'data.status', 'room.status', 'data.live.status'],
168
+ '图片数量': ['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']
169
171
  };
170
172
  function getErrorInfo(code, detail) {
171
173
  const baseMsg = exports.ErrorMessageMap[code] || exports.ErrorMessageMap[ErrorCode.UNKNOWN_ERROR];
@@ -389,12 +391,21 @@ function formatPublishTime(value) {
389
391
  if (!value)
390
392
  return '';
391
393
  const str = String(value).trim();
392
- if (/^\d{10,}$/.test(str) && Number(str) > 1e12)
394
+ // 适配B站的 ctime 时间戳字段名(避免直接传入字符串"ctime")
395
+ if (value === 'ctime')
393
396
  return '';
397
+ if (/^\d{10}$/.test(str)) {
398
+ value = Number(str) * 1000;
399
+ }
400
+ if (/^\d{10,}$/.test(str) && Number(str) > 1e12) {
401
+ if (Number(str) > 1e15) {
402
+ value = Number(str) / 1000;
403
+ }
404
+ }
394
405
  try {
395
- const d = new Date(/^\d+$/.test(str) ? Number(str) * 1000 : str);
406
+ const d = new Date(/^\d+$/.test(str) ? Number(str) : str);
396
407
  if (isNaN(d.getTime()))
397
- return '';
408
+ return str;
398
409
  const y = d.getFullYear();
399
410
  const m = (d.getMonth() + 1).toString().padStart(2, '0');
400
411
  const d_ = d.getDate().toString().padStart(2, '0');
@@ -412,7 +423,7 @@ function formatPublishTime(value) {
412
423
  return parts.join(' ').trim();
413
424
  }
414
425
  catch {
415
- return '';
426
+ return str;
416
427
  }
417
428
  }
418
429
  function getNestedValue(obj, path) {
@@ -452,15 +463,19 @@ function findValueInObject(obj, keys) {
452
463
  return undefined;
453
464
  }
454
465
  function parseData(rawResponse, maxDescLength) {
455
- const rootData = rawResponse || {};
456
- const data = rootData.data || rootData.result || rootData || {};
466
+ const root = rawResponse || {};
467
+ const data = root.data || root.result || root || {};
457
468
  const stat = {};
458
469
  Object.entries(VARIABLE_MAPPING).forEach(([varName, keys]) => {
459
- let value = findValueInObject(data, keys) || findValueInObject(rootData, keys);
470
+ let value = findValueInObject(data, keys) || findValueInObject(root, keys);
460
471
  if (varName === '图片数量' && value === undefined) {
461
- value = Array.isArray(data.images) ? data.images.length : (Array.isArray(rootData.images) ? rootData.images.length : 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;
462
477
  }
463
- if (value !== undefined && value !== null && value !== '') {
478
+ if (value !== undefined && value !== null && value !== '' && value !== 0) {
464
479
  stat[varName] = value;
465
480
  }
466
481
  });
@@ -469,25 +484,44 @@ function parseData(rawResponse, maxDescLength) {
469
484
  type = data.jx.type;
470
485
  else if (data.type)
471
486
  type = data.type;
472
- else if (rootData.msg === 'cv')
487
+ else if (root.msg === 'cv')
473
488
  type = 'cv';
474
- else if (rootData.msg === 'live')
489
+ else if (root.msg === 'live')
475
490
  type = 'live';
476
- else if ((data.images && data.images.length > 1) || (rootData.images && rootData.images.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))
477
493
  type = '图集';
478
- const title = stat['标题'] || (data.note_title || data.title || data.content_title || '无标题');
479
- const author = stat['作者'] || (data.author?.name || data.nickname || data.user_name || '未知作者');
480
- const rawDesc = stat['简介'] || data.note_desc || data.content || data.text || data.description || '';
481
- const desc = rawDesc.length > 0 ? rawDesc.slice(0, maxDescLength) : (title.length > 0 ? title : '暂无简介');
482
- const cover = data.cover ?? data.imgurl ?? data.pic ?? data.thumbnail ?? data.cover_url ?? (Array.isArray(data.images) && data.images[0] ? data.images[0] : '');
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 || '未知作者';
498
+ if (typeof author === 'object' && author.name)
499
+ author = author.name;
500
+ const rawDesc = stat['简介'] || data.note_desc || data.content || data.text || data.description ||
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]) || '';
483
509
  let images = [];
484
- const imgRaw = data.images ?? data.pics ?? data.pic_urls ?? data.image_list ?? [];
485
- if (Array.isArray(imgRaw))
486
- images = imgRaw.filter(i => i && typeof i === 'string');
487
- else if (imgRaw)
488
- images = [String(imgRaw)];
489
- let video = data.video?.url ?? data.item?.url ?? data.url ?? data.download_url ?? data.playUrl ?? data.video_url ?? '';
490
- const durationValue = stat['视频时长'] || 0;
510
+ const imgSources = [
511
+ data.images, data.pics, data.pic_urls, data.image_list, data.imgurl,
512
+ root.images, root.pics, root.pic_urls, root.image_list, root.imgurl,
513
+ data.item?.images
514
+ ];
515
+ for (const source of imgSources) {
516
+ if (Array.isArray(source) && source.length > 0) {
517
+ images = source.filter(i => i && typeof i === 'string');
518
+ break;
519
+ }
520
+ }
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] : '') || '';
524
+ const durationValue = stat['视频时长'] || data.item?.duration || data.duration || 0;
491
525
  const duration = typeof durationValue === 'number' ? durationValue : parseInt(durationValue) || 0;
492
526
  const durationFormatted = formatDuration(durationValue);
493
527
  const pubTime = formatPublishTime(stat['发布时间']);
@@ -506,15 +540,29 @@ function parseData(rawResponse, maxDescLength) {
506
540
  if (!isNaN(num) && num > 0)
507
541
  stat['文件大小'] = `${num.toFixed(2)} MB`;
508
542
  }
509
- const live_photo = data.live_photo ?? [];
510
- const h_w = data.item?.h_w ?? [];
511
- const quality_urls = data.quality_urls ?? {};
512
- const default_quality = data.default_quality ?? '';
513
- const download_url = data.download_url ?? video;
514
- const play_count = stat['播放数'] ?? '';
515
- const reposts_count = Number(stat['转发数']) || 0;
516
- const attitudes_count = Number(stat['点赞数']) || 0;
517
- const comments_count = Number(stat['评论数']) || 0;
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 || '';
547
+ const download_url = data.download_url || video;
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;
518
566
  return {
519
567
  type: type,
520
568
  rawData: rawResponse,
@@ -529,7 +577,7 @@ function parseData(rawResponse, maxDescLength) {
529
577
  stat,
530
578
  live_photo,
531
579
  h_w,
532
- jx: data.jx ?? null,
580
+ jx: data.jx || null,
533
581
  quality_urls,
534
582
  default_quality,
535
583
  download_url,
@@ -551,7 +599,8 @@ function generateFormattedText(parseData, config) {
551
599
  收藏:${'${收藏数}'}
552
600
  转发:${'${转发数}'}
553
601
  播放:${'${播放数}'}
554
- 评论:${'${评论数}'}`;
602
+ 评论:${'${评论数}'}
603
+ 视频备用链接:${'${视频备用链接}'}`;
555
604
  }
556
605
  let result = format;
557
606
  const varMatches = result.match(/\$\{([^}]+)\}/g) || [];
@@ -662,8 +711,8 @@ function apply(ctx, config) {
662
711
  return { data: null, code, msg };
663
712
  }
664
713
  const isSuccess = resData.code === 0 || resData.code === 200 || resData.code === 1 ||
665
- (resData.msg && (resData.msg.includes('解析成功') || resData.msg.includes('success') || resData.msg.includes('请求成功'))) ||
666
- !!resData.data || !!resData.result || !!resData.video || !!resData.images;
714
+ (resData.msg && (resData.msg.includes('解析成功') || resData.msg.includes('success') || resData.msg.includes('请求成功') || resData.msg === 'video' || resData.msg === 'cv' || resData.msg === 'live')) ||
715
+ !!resData.data || !!resData.result || !!resData.video || !!resData.images || !!resData.imgurl;
667
716
  if (!isSuccess) {
668
717
  const apiErrorMsg = resData.msg || resData.error || '解析失败';
669
718
  const code = ErrorCode.API_RETURN_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.5",
4
+ "version": "0.6.7",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [