koishi-plugin-video-parser-all 0.5.4 → 0.5.5
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 +133 -34
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -123,9 +123,10 @@ const PLATFORM_KEYWORDS = {
|
|
|
123
123
|
douyin: ['douyin', '抖音', 'v.douyin.com', 'douyinpic.com', 'douyinvod.com', 'douyin.com/video', 'douyin.com/note', 'www.douyin.com', 'tiktok.com'],
|
|
124
124
|
zuiyou: ['zuiyou', '最右', 'xiaochuankeji.cn', 'izuiyou.com', 'izuiyou.com/topic']
|
|
125
125
|
};
|
|
126
|
+
// 更新API配置:B站和抖音使用新的api.xingzhige.com,其他平台保持不变
|
|
126
127
|
const API_CONFIG = {
|
|
127
|
-
bilibili: 'https://api.
|
|
128
|
-
douyin: 'https://api.
|
|
128
|
+
bilibili: 'https://api.xingzhige.com/API/b_parse',
|
|
129
|
+
douyin: 'https://api.xingzhige.com/API/douyin/',
|
|
129
130
|
kuaishou: 'https://api.bugpk.com/api/ksjx',
|
|
130
131
|
xiaohongshu: 'https://api.bugpk.com/api/xhsjx',
|
|
131
132
|
weibo: 'https://api.bugpk.com/api/weibo',
|
|
@@ -147,16 +148,16 @@ const PLATFORM_ERROR_CODE_MAP = {
|
|
|
147
148
|
};
|
|
148
149
|
const VARIABLE_MAPPING = {
|
|
149
150
|
'标题': ['title', 'Title', 'TITLE'],
|
|
150
|
-
'作者': ['author.name', 'author', 'name', 'Author', 'Name'],
|
|
151
|
+
'作者': ['author.name', 'author', 'name', 'Author', 'Name', 'owner.name'],
|
|
151
152
|
'简介': ['desc', 'description', 'Desc', 'Description', 'content', 'Content'],
|
|
152
153
|
'视频时长': ['duration', 'Duration', 'time', 'Time'],
|
|
153
|
-
'点赞数': ['like', 'Like', 'attitudes_count', 'digg_count', 'praise'],
|
|
154
|
+
'点赞数': ['like', 'Like', 'attitudes_count', 'digg_count', 'praise', 'stat.like'],
|
|
154
155
|
'投币数': ['coin', 'Coin', 'bi', 'Bi'],
|
|
155
|
-
'收藏数': ['collect', 'Collect', 'favorite', 'Favorite', 'star', 'Star'],
|
|
156
|
-
'转发数': ['share', 'Share', 'forward', 'Forward', 'repost'],
|
|
156
|
+
'收藏数': ['collect', 'Collect', 'favorite', 'Favorite', 'star', 'Star', 'stat.collect'],
|
|
157
|
+
'转发数': ['share', 'Share', 'forward', 'Forward', 'repost', 'stat.share'],
|
|
157
158
|
'播放数': ['view', 'View', 'play_count', 'PlayCount', 'play'],
|
|
158
|
-
'评论数': ['comment', 'Comment', 'comments_count', 'comment_count', 'discuss'],
|
|
159
|
-
'音乐名': ['music.title', 'music_name', 'audio_name', 'sound_name']
|
|
159
|
+
'评论数': ['comment', 'Comment', 'comments_count', 'comment_count', 'discuss', 'stat.comment'],
|
|
160
|
+
'音乐名': ['music.title', 'music_name', 'audio_name', 'sound_name', 'muisic']
|
|
160
161
|
};
|
|
161
162
|
function getErrorInfo(code, detail) {
|
|
162
163
|
const baseMsg = exports.ErrorMessageMap[code] || exports.ErrorMessageMap[ErrorCode.UNKNOWN_ERROR];
|
|
@@ -408,48 +409,126 @@ function findValueInObject(obj, keys) {
|
|
|
408
409
|
}
|
|
409
410
|
return undefined;
|
|
410
411
|
}
|
|
412
|
+
// 适配新的API返回格式
|
|
411
413
|
function parseData(rawResponse, maxDescLength, platform) {
|
|
412
414
|
let data = rawResponse;
|
|
413
|
-
|
|
415
|
+
// 处理不同平台的返回结构差异
|
|
416
|
+
if (platform === 'bilibili' && rawResponse.data) {
|
|
417
|
+
// 适配api.xingzhige.com的B站返回格式
|
|
418
|
+
data = rawResponse.data;
|
|
419
|
+
}
|
|
420
|
+
else if (platform === 'douyin' && rawResponse.data) {
|
|
421
|
+
// 适配api.xingzhige.com的抖音返回格式
|
|
422
|
+
data = rawResponse.data;
|
|
423
|
+
}
|
|
424
|
+
else if (data.data) {
|
|
425
|
+
// 其他平台保持原有逻辑
|
|
414
426
|
data = data.data;
|
|
415
427
|
}
|
|
416
428
|
const stat = {};
|
|
429
|
+
// 适配新的字段映射
|
|
417
430
|
Object.entries(VARIABLE_MAPPING).forEach(([varName, keys]) => {
|
|
418
431
|
const value = findValueInObject(data, keys);
|
|
419
432
|
if (value !== undefined) {
|
|
420
433
|
stat[varName] = value;
|
|
421
434
|
}
|
|
422
435
|
});
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
436
|
+
// 处理不同平台的类型字段
|
|
437
|
+
let type = 'video';
|
|
438
|
+
if (platform === 'douyin' && data.jx && data.jx.type) {
|
|
439
|
+
type = data.jx.type; // 抖音的图集类型
|
|
440
|
+
}
|
|
441
|
+
else if (data.type) {
|
|
442
|
+
type = data.type;
|
|
443
|
+
}
|
|
444
|
+
// 适配不同平台的标题字段
|
|
445
|
+
let title = '无标题';
|
|
446
|
+
if (platform === 'bilibili' && data.video && data.video.title) {
|
|
447
|
+
title = data.video.title;
|
|
448
|
+
}
|
|
449
|
+
else if (platform === 'douyin' && data.item && data.item.title) {
|
|
450
|
+
title = data.item.title;
|
|
451
|
+
}
|
|
452
|
+
else {
|
|
453
|
+
title = findValueInObject(data, ['title']) || '无标题';
|
|
454
|
+
}
|
|
455
|
+
// 适配不同平台的作者字段
|
|
456
|
+
let author = '未知作者';
|
|
457
|
+
if (platform === 'bilibili' && data.owner && data.owner.name) {
|
|
458
|
+
author = data.owner.name;
|
|
459
|
+
}
|
|
460
|
+
else if (platform === 'douyin' && data.author && data.author.name) {
|
|
461
|
+
author = data.author.name;
|
|
462
|
+
}
|
|
463
|
+
else {
|
|
464
|
+
author = findValueInObject(data, ['author.name', 'author', 'name', 'auther']) || '未知作者';
|
|
465
|
+
}
|
|
466
|
+
// 适配不同平台的描述字段
|
|
467
|
+
let desc = title;
|
|
468
|
+
if (platform === 'bilibili' && data.video && data.video.desc) {
|
|
469
|
+
desc = data.video.desc;
|
|
470
|
+
}
|
|
471
|
+
else if (platform === 'douyin') {
|
|
472
|
+
desc = title; // 抖音没有单独的描述字段
|
|
473
|
+
}
|
|
474
|
+
else {
|
|
475
|
+
desc = findValueInObject(data, ['desc', 'description', 'content']) || title;
|
|
476
|
+
}
|
|
477
|
+
desc = desc.toString().slice(0, maxDescLength);
|
|
478
|
+
// 适配不同平台的封面字段
|
|
479
|
+
let cover = '';
|
|
480
|
+
if (platform === 'bilibili' && data.video && data.video.fm) {
|
|
481
|
+
cover = data.video.fm;
|
|
482
|
+
}
|
|
483
|
+
else if (platform === 'douyin' && data.item && data.item.cover) {
|
|
484
|
+
cover = data.item.cover;
|
|
485
|
+
}
|
|
486
|
+
else {
|
|
487
|
+
cover = findValueInObject(data, ['cover', 'imgurl', 'pic', 'thumbnail']) || '';
|
|
488
|
+
}
|
|
489
|
+
// 适配不同平台的图片字段
|
|
490
|
+
let images = [];
|
|
491
|
+
if (platform === 'douyin' && data.item && data.item.images) {
|
|
492
|
+
images = data.item.images;
|
|
493
|
+
}
|
|
494
|
+
else {
|
|
495
|
+
images = findValueInObject(data, ['imgurl', 'images', 'pics']) || [];
|
|
496
|
+
}
|
|
429
497
|
if (!Array.isArray(images))
|
|
430
498
|
images = [images];
|
|
431
|
-
|
|
432
|
-
findValueInObject(data, ['url']),
|
|
433
|
-
findValueInObject(data, ['download_url']),
|
|
434
|
-
findValueInObject(data, ['video_backup']),
|
|
435
|
-
findValueInObject(data, ['playUrl']),
|
|
436
|
-
findValueInObject(data, ['video_url'])
|
|
437
|
-
];
|
|
499
|
+
// 适配不同平台的视频链接字段
|
|
438
500
|
let video = '';
|
|
439
|
-
if (
|
|
440
|
-
video =
|
|
501
|
+
if (platform === 'bilibili' && data.video && data.video.url) {
|
|
502
|
+
video = data.video.url;
|
|
503
|
+
}
|
|
504
|
+
else if (platform === 'douyin') {
|
|
505
|
+
video = ''; // 抖音图集没有视频链接
|
|
441
506
|
}
|
|
442
507
|
else {
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
508
|
+
const videoUrls = [
|
|
509
|
+
findValueInObject(data, ['url']),
|
|
510
|
+
findValueInObject(data, ['download_url']),
|
|
511
|
+
findValueInObject(data, ['video_backup']),
|
|
512
|
+
findValueInObject(data, ['playUrl']),
|
|
513
|
+
findValueInObject(data, ['video_url'])
|
|
514
|
+
];
|
|
515
|
+
if (Array.isArray(videoUrls[2])) {
|
|
516
|
+
video = videoUrls[2][0]?.url || '';
|
|
517
|
+
}
|
|
518
|
+
else {
|
|
519
|
+
for (const url of videoUrls) {
|
|
520
|
+
if (url && typeof url === 'string' && url.trim() !== '') {
|
|
521
|
+
video = url;
|
|
522
|
+
break;
|
|
523
|
+
}
|
|
447
524
|
}
|
|
448
525
|
}
|
|
449
526
|
}
|
|
527
|
+
// 适配不同平台的时长字段
|
|
450
528
|
const durationValue = findValueInObject(data, ['duration']);
|
|
451
529
|
const duration = typeof durationValue === 'number' ? durationValue : parseInt(durationValue) || 0;
|
|
452
530
|
const durationFormatted = formatDuration(durationValue || 0);
|
|
531
|
+
// 处理live_photo字段
|
|
453
532
|
const live_photo = data.live_photo || [];
|
|
454
533
|
return {
|
|
455
534
|
type: type,
|
|
@@ -576,9 +655,17 @@ function apply(ctx, config) {
|
|
|
576
655
|
}
|
|
577
656
|
try {
|
|
578
657
|
const resData = await parseWithRetry(realUrl, platform, config.retryTimes);
|
|
579
|
-
//
|
|
580
|
-
|
|
581
|
-
|
|
658
|
+
// 适配不同平台的成功判断逻辑
|
|
659
|
+
let isSuccess = false;
|
|
660
|
+
// B站和抖音使用新的判断逻辑(code=0表示成功)
|
|
661
|
+
if (platform === 'bilibili' || platform === 'douyin') {
|
|
662
|
+
isSuccess = resData.code === 0 || (resData.msg && (resData.msg.includes('解析成功') || resData.msg === 'video'));
|
|
663
|
+
}
|
|
664
|
+
else {
|
|
665
|
+
// 其他平台保持原有逻辑
|
|
666
|
+
isSuccess = resData.code === 200 || resData.code === 0 ||
|
|
667
|
+
(resData.msg && resData.msg.includes('解析成功'));
|
|
668
|
+
}
|
|
582
669
|
if (!isSuccess) {
|
|
583
670
|
const apiErrorMsg = resData.msg || '解析失败';
|
|
584
671
|
const platformCode = PLATFORM_ERROR_CODE_MAP[platform] || ErrorCode.API_RETURN_ERROR;
|
|
@@ -593,11 +680,12 @@ function apply(ctx, config) {
|
|
|
593
680
|
}
|
|
594
681
|
try {
|
|
595
682
|
const parseResult = parseData(resData, config.maxDescLength, platform);
|
|
596
|
-
//
|
|
683
|
+
// 修正内容判断逻辑:支持抖音图集、live类型和live_photo
|
|
597
684
|
const hasValidContent = parseResult.video ||
|
|
598
685
|
(parseResult.images && parseResult.images.length > 0) ||
|
|
599
686
|
(parseResult.live_photo && parseResult.live_photo.length > 0) ||
|
|
600
|
-
parseResult.type === 'live'
|
|
687
|
+
parseResult.type === 'live' ||
|
|
688
|
+
parseResult.type === '图集';
|
|
601
689
|
if (!hasValidContent) {
|
|
602
690
|
const code = ErrorCode.NO_VIDEO_FOUND;
|
|
603
691
|
const msg = getErrorInfo(code, '链接有效但未找到视频/图片内容(可能是直播、私密内容或已删除)');
|
|
@@ -728,6 +816,12 @@ function apply(ctx, config) {
|
|
|
728
816
|
forwardMessages.push(buildForwardNode(session, item.text, botName));
|
|
729
817
|
if (item.cover && forwardMessages.length < 100)
|
|
730
818
|
forwardMessages.push(buildForwardNode(session, koishi_1.h.image(item.cover), botName));
|
|
819
|
+
// 处理抖音图集
|
|
820
|
+
if (item.type === '图集' && item.images?.length) {
|
|
821
|
+
for (let i = 0; i < item.images.length && forwardMessages.length < 100; i++) {
|
|
822
|
+
forwardMessages.push(buildForwardNode(session, koishi_1.h.image(item.images[i]), botName));
|
|
823
|
+
}
|
|
824
|
+
}
|
|
731
825
|
if (item.type === 'image' && item.images?.length) {
|
|
732
826
|
for (let i = 0; i < item.images.length && forwardMessages.length < 100; i++) {
|
|
733
827
|
forwardMessages.push(buildForwardNode(session, koishi_1.h.image(item.images[i]), botName));
|
|
@@ -795,7 +889,12 @@ function apply(ctx, config) {
|
|
|
795
889
|
await sendTimeout(session, item.text);
|
|
796
890
|
await delay(300);
|
|
797
891
|
}
|
|
798
|
-
|
|
892
|
+
// 处理抖音图集
|
|
893
|
+
if (item.type === '图集' && item.images?.length) {
|
|
894
|
+
const imgMsg = (0, koishi_1.h)('message', ...item.images.map((url) => koishi_1.h.image(url)));
|
|
895
|
+
await sendTimeout(session, imgMsg);
|
|
896
|
+
}
|
|
897
|
+
else if (item.type === 'live' || item.type === 'live_photo') {
|
|
799
898
|
if (item.live_photo?.length) {
|
|
800
899
|
for (const liveItem of item.live_photo) {
|
|
801
900
|
await sendTimeout(session, koishi_1.h.image(liveItem.image));
|
package/package.json
CHANGED