koishi-plugin-video-parser-all 0.5.4 → 0.5.6
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 +140 -44
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -124,8 +124,8 @@ const PLATFORM_KEYWORDS = {
|
|
|
124
124
|
zuiyou: ['zuiyou', '最右', 'xiaochuankeji.cn', 'izuiyou.com', 'izuiyou.com/topic']
|
|
125
125
|
};
|
|
126
126
|
const API_CONFIG = {
|
|
127
|
-
bilibili: 'https://api.
|
|
128
|
-
douyin: 'https://api.
|
|
127
|
+
bilibili: 'https://api.xingzhige.com/API/b_parse',
|
|
128
|
+
douyin: 'https://api.xingzhige.com/API/douyin/',
|
|
129
129
|
kuaishou: 'https://api.bugpk.com/api/ksjx',
|
|
130
130
|
xiaohongshu: 'https://api.bugpk.com/api/xhsjx',
|
|
131
131
|
weibo: 'https://api.bugpk.com/api/weibo',
|
|
@@ -147,16 +147,16 @@ const PLATFORM_ERROR_CODE_MAP = {
|
|
|
147
147
|
};
|
|
148
148
|
const VARIABLE_MAPPING = {
|
|
149
149
|
'标题': ['title', 'Title', 'TITLE'],
|
|
150
|
-
'作者': ['author.name', 'author', 'name', 'Author', 'Name'],
|
|
150
|
+
'作者': ['author.name', 'author', 'name', 'Author', 'Name', 'owner.name'],
|
|
151
151
|
'简介': ['desc', 'description', 'Desc', 'Description', 'content', 'Content'],
|
|
152
152
|
'视频时长': ['duration', 'Duration', 'time', 'Time'],
|
|
153
|
-
'点赞数': ['like', 'Like', 'attitudes_count', 'digg_count', 'praise'],
|
|
153
|
+
'点赞数': ['like', 'Like', 'attitudes_count', 'digg_count', 'praise', 'stat.like'],
|
|
154
154
|
'投币数': ['coin', 'Coin', 'bi', 'Bi'],
|
|
155
|
-
'收藏数': ['collect', 'Collect', 'favorite', 'Favorite', 'star', 'Star'],
|
|
156
|
-
'转发数': ['share', 'Share', 'forward', 'Forward', 'repost'],
|
|
155
|
+
'收藏数': ['collect', 'Collect', 'favorite', 'Favorite', 'star', 'Star', 'stat.collect'],
|
|
156
|
+
'转发数': ['share', 'Share', 'forward', 'Forward', 'repost', 'stat.share', 'reposts_count'],
|
|
157
157
|
'播放数': ['view', 'View', 'play_count', 'PlayCount', 'play'],
|
|
158
|
-
'评论数': ['comment', 'Comment', 'comments_count', 'comment_count', 'discuss'],
|
|
159
|
-
'音乐名': ['music.title', 'music_name', 'audio_name', 'sound_name']
|
|
158
|
+
'评论数': ['comment', 'Comment', 'comments_count', 'comment_count', 'discuss', 'stat.comment'],
|
|
159
|
+
'音乐名': ['music.title', 'music_name', 'audio_name', 'sound_name', 'muisic', 'music']
|
|
160
160
|
};
|
|
161
161
|
function getErrorInfo(code, detail) {
|
|
162
162
|
const baseMsg = exports.ErrorMessageMap[code] || exports.ErrorMessageMap[ErrorCode.UNKNOWN_ERROR];
|
|
@@ -321,7 +321,6 @@ function getPlatformType(url) {
|
|
|
321
321
|
}
|
|
322
322
|
function cleanUrl(url) {
|
|
323
323
|
try {
|
|
324
|
-
// 处理HTML实体编码
|
|
325
324
|
url = url.replace(/&/g, '&');
|
|
326
325
|
const urlObj = new URL(url);
|
|
327
326
|
if (urlObj.hostname.includes('xiaohongshu.com')) {
|
|
@@ -329,7 +328,7 @@ function cleanUrl(url) {
|
|
|
329
328
|
urlObj.searchParams.delete('xhsshare');
|
|
330
329
|
urlObj.searchParams.delete('xsec_token');
|
|
331
330
|
urlObj.searchParams.delete('xsec_source');
|
|
332
|
-
return urlObj.origin + urlObj.pathname
|
|
331
|
+
return urlObj.origin + urlObj.pathname;
|
|
333
332
|
}
|
|
334
333
|
if (urlObj.hostname.includes('douyin.com') || urlObj.hostname.includes('v.douyin.com')) {
|
|
335
334
|
return urlObj.origin + urlObj.pathname;
|
|
@@ -337,7 +336,6 @@ function cleanUrl(url) {
|
|
|
337
336
|
return url;
|
|
338
337
|
}
|
|
339
338
|
catch (e) {
|
|
340
|
-
// 处理HTML实体编码
|
|
341
339
|
return url.replace(/&/g, '&');
|
|
342
340
|
}
|
|
343
341
|
}
|
|
@@ -410,7 +408,13 @@ function findValueInObject(obj, keys) {
|
|
|
410
408
|
}
|
|
411
409
|
function parseData(rawResponse, maxDescLength, platform) {
|
|
412
410
|
let data = rawResponse;
|
|
413
|
-
if (
|
|
411
|
+
if (platform === 'bilibili' && rawResponse.data) {
|
|
412
|
+
data = rawResponse.data;
|
|
413
|
+
}
|
|
414
|
+
else if (platform === 'douyin' && rawResponse.data) {
|
|
415
|
+
data = rawResponse.data;
|
|
416
|
+
}
|
|
417
|
+
else if (data.data) {
|
|
414
418
|
data = data.data;
|
|
415
419
|
}
|
|
416
420
|
const stat = {};
|
|
@@ -420,30 +424,87 @@ function parseData(rawResponse, maxDescLength, platform) {
|
|
|
420
424
|
stat[varName] = value;
|
|
421
425
|
}
|
|
422
426
|
});
|
|
423
|
-
let type =
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
427
|
+
let type = 'video';
|
|
428
|
+
if (platform === 'douyin' && data.jx && data.jx.type) {
|
|
429
|
+
type = data.jx.type;
|
|
430
|
+
}
|
|
431
|
+
else if (data.type) {
|
|
432
|
+
type = data.type;
|
|
433
|
+
}
|
|
434
|
+
let title = '无标题';
|
|
435
|
+
if (platform === 'bilibili' && data.video && data.video.title) {
|
|
436
|
+
title = data.video.title;
|
|
437
|
+
}
|
|
438
|
+
else if (platform === 'douyin' && data.item && data.item.title) {
|
|
439
|
+
title = data.item.title;
|
|
440
|
+
}
|
|
441
|
+
else {
|
|
442
|
+
title = findValueInObject(data, ['title']) || '无标题';
|
|
443
|
+
}
|
|
444
|
+
let author = '未知作者';
|
|
445
|
+
if (platform === 'bilibili' && data.owner && data.owner.name) {
|
|
446
|
+
author = data.owner.name;
|
|
447
|
+
}
|
|
448
|
+
else if (platform === 'douyin' && data.author && data.author.name) {
|
|
449
|
+
author = data.author.name;
|
|
450
|
+
}
|
|
451
|
+
else {
|
|
452
|
+
author = findValueInObject(data, ['author.name', 'author', 'name', 'auther']) || '未知作者';
|
|
453
|
+
}
|
|
454
|
+
let desc = title;
|
|
455
|
+
if (platform === 'bilibili' && data.video && data.video.desc) {
|
|
456
|
+
desc = data.video.desc;
|
|
457
|
+
}
|
|
458
|
+
else if (platform === 'douyin') {
|
|
459
|
+
desc = title;
|
|
460
|
+
}
|
|
461
|
+
else {
|
|
462
|
+
desc = findValueInObject(data, ['desc', 'description', 'content']) || title;
|
|
463
|
+
}
|
|
464
|
+
desc = desc.toString().slice(0, maxDescLength);
|
|
465
|
+
let cover = '';
|
|
466
|
+
if (platform === 'bilibili' && data.video && data.video.fm) {
|
|
467
|
+
cover = data.video.fm;
|
|
468
|
+
}
|
|
469
|
+
else if (platform === 'douyin' && data.item && data.item.cover) {
|
|
470
|
+
cover = data.item.cover;
|
|
471
|
+
}
|
|
472
|
+
else {
|
|
473
|
+
cover = findValueInObject(data, ['cover', 'imgurl', 'pic', 'thumbnail']) || '';
|
|
474
|
+
}
|
|
475
|
+
let images = [];
|
|
476
|
+
if (platform === 'douyin' && data.item && data.item.images) {
|
|
477
|
+
images = data.item.images;
|
|
478
|
+
}
|
|
479
|
+
else {
|
|
480
|
+
images = findValueInObject(data, ['imgurl', 'images', 'pics']) || [];
|
|
481
|
+
}
|
|
429
482
|
if (!Array.isArray(images))
|
|
430
483
|
images = [images];
|
|
431
|
-
const videoUrls = [
|
|
432
|
-
findValueInObject(data, ['url']),
|
|
433
|
-
findValueInObject(data, ['download_url']),
|
|
434
|
-
findValueInObject(data, ['video_backup']),
|
|
435
|
-
findValueInObject(data, ['playUrl']),
|
|
436
|
-
findValueInObject(data, ['video_url'])
|
|
437
|
-
];
|
|
438
484
|
let video = '';
|
|
439
|
-
if (
|
|
440
|
-
video =
|
|
485
|
+
if (platform === 'bilibili' && data.video && data.video.url) {
|
|
486
|
+
video = data.video.url;
|
|
487
|
+
}
|
|
488
|
+
else if (platform === 'douyin' && data.item && data.item.url) {
|
|
489
|
+
video = data.item.url;
|
|
441
490
|
}
|
|
442
491
|
else {
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
492
|
+
const videoUrls = [
|
|
493
|
+
findValueInObject(data, ['url']),
|
|
494
|
+
findValueInObject(data, ['download_url']),
|
|
495
|
+
findValueInObject(data, ['video_backup']),
|
|
496
|
+
findValueInObject(data, ['playUrl']),
|
|
497
|
+
findValueInObject(data, ['video_url'])
|
|
498
|
+
];
|
|
499
|
+
if (Array.isArray(videoUrls[2])) {
|
|
500
|
+
video = videoUrls[2][0]?.url || '';
|
|
501
|
+
}
|
|
502
|
+
else {
|
|
503
|
+
for (const url of videoUrls) {
|
|
504
|
+
if (url && typeof url === 'string' && url.trim() !== '') {
|
|
505
|
+
video = url;
|
|
506
|
+
break;
|
|
507
|
+
}
|
|
447
508
|
}
|
|
448
509
|
}
|
|
449
510
|
}
|
|
@@ -451,6 +512,15 @@ function parseData(rawResponse, maxDescLength, platform) {
|
|
|
451
512
|
const duration = typeof durationValue === 'number' ? durationValue : parseInt(durationValue) || 0;
|
|
452
513
|
const durationFormatted = formatDuration(durationValue || 0);
|
|
453
514
|
const live_photo = data.live_photo || [];
|
|
515
|
+
const music = findValueInObject(data, ['music', 'muisic']) || '';
|
|
516
|
+
const h_w = platform === 'douyin' && data.item && data.item.h_w ? data.item.h_w : [];
|
|
517
|
+
const quality_urls = data.quality_urls || {};
|
|
518
|
+
const default_quality = data.default_quality || '';
|
|
519
|
+
const download_url = data.download_url || '';
|
|
520
|
+
const play_count = data.play_count || '';
|
|
521
|
+
const reposts_count = data.reposts_count || 0;
|
|
522
|
+
const attitudes_count = data.attitudes_count || 0;
|
|
523
|
+
const comments_count = data.comments_count || 0;
|
|
454
524
|
return {
|
|
455
525
|
type: type,
|
|
456
526
|
rawData: rawResponse,
|
|
@@ -463,7 +533,17 @@ function parseData(rawResponse, maxDescLength, platform) {
|
|
|
463
533
|
duration,
|
|
464
534
|
durationFormatted,
|
|
465
535
|
stat,
|
|
466
|
-
live_photo
|
|
536
|
+
live_photo,
|
|
537
|
+
music,
|
|
538
|
+
h_w,
|
|
539
|
+
jx: data.jx || null,
|
|
540
|
+
quality_urls,
|
|
541
|
+
default_quality,
|
|
542
|
+
download_url,
|
|
543
|
+
play_count,
|
|
544
|
+
reposts_count,
|
|
545
|
+
attitudes_count,
|
|
546
|
+
comments_count
|
|
467
547
|
};
|
|
468
548
|
}
|
|
469
549
|
function generateFormattedText(platform, parseData, config) {
|
|
@@ -542,8 +622,9 @@ function apply(ctx, config) {
|
|
|
542
622
|
let lastError = null;
|
|
543
623
|
for (let i = 0; i <= retryTimes; i++) {
|
|
544
624
|
try {
|
|
625
|
+
const params = { url };
|
|
545
626
|
const res = await http.get(API_CONFIG[platform], {
|
|
546
|
-
params
|
|
627
|
+
params,
|
|
547
628
|
timeout: config.timeout
|
|
548
629
|
});
|
|
549
630
|
return res.data;
|
|
@@ -576,9 +657,14 @@ function apply(ctx, config) {
|
|
|
576
657
|
}
|
|
577
658
|
try {
|
|
578
659
|
const resData = await parseWithRetry(realUrl, platform, config.retryTimes);
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
(resData.msg && resData.msg.includes('解析成功'));
|
|
660
|
+
let isSuccess = false;
|
|
661
|
+
if (platform === 'bilibili' || platform === 'douyin') {
|
|
662
|
+
isSuccess = resData.code === 0 || (resData.msg && (resData.msg.includes('解析成功') || resData.msg === 'video'));
|
|
663
|
+
}
|
|
664
|
+
else {
|
|
665
|
+
isSuccess = resData.code === 200 || resData.code === 0 ||
|
|
666
|
+
(resData.msg && resData.msg.includes('解析成功'));
|
|
667
|
+
}
|
|
582
668
|
if (!isSuccess) {
|
|
583
669
|
const apiErrorMsg = resData.msg || '解析失败';
|
|
584
670
|
const platformCode = PLATFORM_ERROR_CODE_MAP[platform] || ErrorCode.API_RETURN_ERROR;
|
|
@@ -593,11 +679,11 @@ function apply(ctx, config) {
|
|
|
593
679
|
}
|
|
594
680
|
try {
|
|
595
681
|
const parseResult = parseData(resData, config.maxDescLength, platform);
|
|
596
|
-
// 修正内容判断逻辑:支持live类型和live_photo
|
|
597
682
|
const hasValidContent = parseResult.video ||
|
|
598
683
|
(parseResult.images && parseResult.images.length > 0) ||
|
|
599
684
|
(parseResult.live_photo && parseResult.live_photo.length > 0) ||
|
|
600
|
-
parseResult.type === 'live'
|
|
685
|
+
parseResult.type === 'live' ||
|
|
686
|
+
parseResult.type === '图集';
|
|
601
687
|
if (!hasValidContent) {
|
|
602
688
|
const code = ErrorCode.NO_VIDEO_FOUND;
|
|
603
689
|
const msg = getErrorInfo(code, '链接有效但未找到视频/图片内容(可能是直播、私密内容或已删除)');
|
|
@@ -658,7 +744,12 @@ function apply(ctx, config) {
|
|
|
658
744
|
images: parseData.images,
|
|
659
745
|
video: parseData.video,
|
|
660
746
|
type: parseData.type,
|
|
661
|
-
live_photo: parseData.live_photo
|
|
747
|
+
live_photo: parseData.live_photo,
|
|
748
|
+
music: parseData.music,
|
|
749
|
+
h_w: parseData.h_w,
|
|
750
|
+
quality_urls: parseData.quality_urls,
|
|
751
|
+
default_quality: parseData.default_quality,
|
|
752
|
+
download_url: parseData.download_url
|
|
662
753
|
},
|
|
663
754
|
code: ErrorCode.SUCCESS,
|
|
664
755
|
msg: getErrorInfo(ErrorCode.SUCCESS)
|
|
@@ -728,6 +819,11 @@ function apply(ctx, config) {
|
|
|
728
819
|
forwardMessages.push(buildForwardNode(session, item.text, botName));
|
|
729
820
|
if (item.cover && forwardMessages.length < 100)
|
|
730
821
|
forwardMessages.push(buildForwardNode(session, koishi_1.h.image(item.cover), botName));
|
|
822
|
+
if (item.type === '图集' && item.images?.length) {
|
|
823
|
+
for (let i = 0; i < item.images.length && forwardMessages.length < 100; i++) {
|
|
824
|
+
forwardMessages.push(buildForwardNode(session, koishi_1.h.image(item.images[i]), botName));
|
|
825
|
+
}
|
|
826
|
+
}
|
|
731
827
|
if (item.type === 'image' && item.images?.length) {
|
|
732
828
|
for (let i = 0; i < item.images.length && forwardMessages.length < 100; i++) {
|
|
733
829
|
forwardMessages.push(buildForwardNode(session, koishi_1.h.image(item.images[i]), botName));
|
|
@@ -795,7 +891,11 @@ function apply(ctx, config) {
|
|
|
795
891
|
await sendTimeout(session, item.text);
|
|
796
892
|
await delay(300);
|
|
797
893
|
}
|
|
798
|
-
if (item.type === '
|
|
894
|
+
if (item.type === '图集' && item.images?.length) {
|
|
895
|
+
const imgMsg = (0, koishi_1.h)('message', ...item.images.map((url) => koishi_1.h.image(url)));
|
|
896
|
+
await sendTimeout(session, imgMsg);
|
|
897
|
+
}
|
|
898
|
+
else if (item.type === 'live' || item.type === 'live_photo') {
|
|
799
899
|
if (item.live_photo?.length) {
|
|
800
900
|
for (const liveItem of item.live_photo) {
|
|
801
901
|
await sendTimeout(session, koishi_1.h.image(liveItem.image));
|
|
@@ -975,9 +1075,5 @@ function apply(ctx, config) {
|
|
|
975
1075
|
logger.info(getErrorInfo(ErrorCode.SUCCESS, '自动清理缓存完成'));
|
|
976
1076
|
}, config.autoClearCacheInterval * 60000);
|
|
977
1077
|
}
|
|
978
|
-
process.on('exit', () => {
|
|
979
|
-
clearAllCache();
|
|
980
|
-
logger.info(getErrorInfo(ErrorCode.SUCCESS, '进程退出,已清理缓存'));
|
|
981
|
-
});
|
|
982
1078
|
logger.info(getErrorInfo(ErrorCode.SUCCESS, '视频解析插件已加载'));
|
|
983
1079
|
}
|
package/package.json
CHANGED