koishi-plugin-video-parser-all 0.7.6 → 0.8.0
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 +114 -41
- package/package.json +1 -1
- package/readme.md +4 -2
package/lib/index.js
CHANGED
|
@@ -42,7 +42,7 @@ exports.Config = koishi_1.Schema.intersect([
|
|
|
42
42
|
}).description('内容长度限制'),
|
|
43
43
|
koishi_1.Schema.object({
|
|
44
44
|
timeout: koishi_1.Schema.number().min(0).default(180000).description('API请求超时时间(毫秒)'),
|
|
45
|
-
videoSendTimeout: koishi_1.Schema.number().min(0).default(
|
|
45
|
+
videoSendTimeout: koishi_1.Schema.number().min(0).default(60000).description('视频发送超时时间(毫秒,0为不限制)'),
|
|
46
46
|
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标识'),
|
|
47
47
|
}).description('网络与API设置'),
|
|
48
48
|
koishi_1.Schema.object({
|
|
@@ -399,7 +399,20 @@ function parseData(rawResponse, maxDescLength) {
|
|
|
399
399
|
const data = root.data || root;
|
|
400
400
|
const stat = {};
|
|
401
401
|
let totalImageCount = 0;
|
|
402
|
+
if (root.msg === 'live' && data.live) {
|
|
403
|
+
const liveData = data.live;
|
|
404
|
+
stat['标题'] = liveData.title || '';
|
|
405
|
+
stat['直播间地址'] = liveData.room_url || '';
|
|
406
|
+
stat['直播间ID'] = liveData.room_id || '';
|
|
407
|
+
stat['直播间状态'] = liveData.status === 1 ? '直播中' : (liveData.status === 0 ? '未开播' : '未知');
|
|
408
|
+
stat['在线人数'] = liveData.online || '';
|
|
409
|
+
stat['关注数'] = liveData.attention || '';
|
|
410
|
+
stat['发布时间'] = formatPublishTime(liveData.time);
|
|
411
|
+
stat['简介'] = liveData.desc || '';
|
|
412
|
+
}
|
|
402
413
|
Object.entries(VARIABLE_MAPPING).forEach(([varName, keys]) => {
|
|
414
|
+
if (stat[varName] !== undefined)
|
|
415
|
+
return;
|
|
403
416
|
let value = findValueInObject(data, keys) || findValueInObject(root, keys);
|
|
404
417
|
if (varName === '图片数量' && value === undefined) {
|
|
405
418
|
let imgCount = 0;
|
|
@@ -438,7 +451,7 @@ function parseData(rawResponse, maxDescLength) {
|
|
|
438
451
|
else if ((data.images && data.images.length > 1) || (root.images && root.images.length > 1) ||
|
|
439
452
|
(data.imgurl && data.imgurl.length > 1) || (root.imgurl && root.imgurl.length > 1))
|
|
440
453
|
type = '图集';
|
|
441
|
-
const title = data.title || '无标题';
|
|
454
|
+
const title = stat['标题'] || data.title || '无标题';
|
|
442
455
|
let author = '';
|
|
443
456
|
if (data.author && typeof data.author === 'object') {
|
|
444
457
|
author = data.author.name || '';
|
|
@@ -446,16 +459,16 @@ function parseData(rawResponse, maxDescLength) {
|
|
|
446
459
|
else {
|
|
447
460
|
author = data.author || '';
|
|
448
461
|
}
|
|
449
|
-
author = author || '未知作者';
|
|
450
|
-
const rawDesc = data.desc || data.content || '暂无简介';
|
|
462
|
+
author = author || stat['作者'] || '未知作者';
|
|
463
|
+
const rawDesc = data.desc || data.content || stat['简介'] || '暂无简介';
|
|
451
464
|
const desc = rawDesc.slice(0, maxDescLength);
|
|
452
|
-
const cover = data.cover || '';
|
|
465
|
+
const cover = data.cover || data.live?.cover || data.live?.keyframe || '';
|
|
453
466
|
const images = Array.isArray(data.images) ? data.images : [];
|
|
454
|
-
const video = data.url || data.video_backup || '';
|
|
467
|
+
const video = data.url || data.video_backup || (data.live?.url && Array.isArray(data.live.url) ? data.live.url[0] : '') || '';
|
|
455
468
|
const durationValue = data.duration || 0;
|
|
456
469
|
const duration = typeof durationValue === 'number' ? durationValue : parseInt(durationValue) || 0;
|
|
457
470
|
const durationFormatted = formatDuration(durationValue);
|
|
458
|
-
const pubTime = formatPublishTime(data.create_time || data.publish_time);
|
|
471
|
+
const pubTime = formatPublishTime(data.create_time || data.publish_time || data.live?.time);
|
|
459
472
|
if (pubTime)
|
|
460
473
|
stat['发布时间'] = pubTime;
|
|
461
474
|
if (durationFormatted !== '00:00:00')
|
|
@@ -471,13 +484,6 @@ function parseData(rawResponse, maxDescLength) {
|
|
|
471
484
|
const reposts_count = Number(stat['转发数']) || 0;
|
|
472
485
|
const attitudes_count = Number(stat['点赞数']) || 0;
|
|
473
486
|
const comments_count = Number(stat['评论数']) || 0;
|
|
474
|
-
if (data.live) {
|
|
475
|
-
stat['直播间地址'] = data.live.room_url || '';
|
|
476
|
-
stat['直播间ID'] = data.live.room_id || '';
|
|
477
|
-
stat['直播间状态'] = data.live.status === 1 ? '直播中' : (data.live.status === 0 ? '未开播' : '未知');
|
|
478
|
-
stat['在线人数'] = data.live.online || '';
|
|
479
|
-
stat['关注数'] = data.live.attention || '';
|
|
480
|
-
}
|
|
481
487
|
return {
|
|
482
488
|
type: type,
|
|
483
489
|
rawData: rawResponse,
|
|
@@ -627,6 +633,17 @@ function apply(ctx, config) {
|
|
|
627
633
|
}
|
|
628
634
|
try {
|
|
629
635
|
const parseResult = parseData(resData, config.maxDescLength);
|
|
636
|
+
const isAllDefault = parseResult.title === '无标题' &&
|
|
637
|
+
parseResult.author === '未知作者' &&
|
|
638
|
+
parseResult.desc === '暂无简介';
|
|
639
|
+
if (isAllDefault) {
|
|
640
|
+
logger.warn(`解析结果均为默认值(可能暂不支持该链接): ${url}`);
|
|
641
|
+
return {
|
|
642
|
+
data: null,
|
|
643
|
+
success: false,
|
|
644
|
+
msg: '解析失败: 暂不支持解析该链接'
|
|
645
|
+
};
|
|
646
|
+
}
|
|
630
647
|
logger.info(`解析成功: ${url}`);
|
|
631
648
|
return {
|
|
632
649
|
data: parseResult,
|
|
@@ -684,26 +701,32 @@ function apply(ctx, config) {
|
|
|
684
701
|
msg: '处理成功'
|
|
685
702
|
};
|
|
686
703
|
}
|
|
687
|
-
async function
|
|
704
|
+
async function sendWithTimeout(session, content) {
|
|
688
705
|
if (config.videoSendTimeout <= 0) {
|
|
689
|
-
|
|
706
|
+
try {
|
|
707
|
+
return await session.send(content);
|
|
708
|
+
}
|
|
709
|
+
catch (err) {
|
|
690
710
|
const errorMsg = getErrorMessage(err);
|
|
691
711
|
logger.error(`发送消息失败: ${errorMsg}`);
|
|
692
712
|
if (!config.ignoreSendError)
|
|
693
|
-
|
|
713
|
+
throw err;
|
|
694
714
|
return null;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
try {
|
|
718
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
719
|
+
setTimeout(() => reject(new Error('发送超时')), config.videoSendTimeout);
|
|
695
720
|
});
|
|
721
|
+
return await Promise.race([session.send(content), timeoutPromise]);
|
|
696
722
|
}
|
|
697
|
-
|
|
698
|
-
session.send(content),
|
|
699
|
-
new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), config.videoSendTimeout))
|
|
700
|
-
]).catch((err) => {
|
|
723
|
+
catch (err) {
|
|
701
724
|
const errorMsg = getErrorMessage(err);
|
|
702
|
-
logger.error(
|
|
725
|
+
logger.error(`发送消息超时或失败: ${errorMsg}`);
|
|
703
726
|
if (!config.ignoreSendError)
|
|
704
|
-
|
|
727
|
+
throw err;
|
|
705
728
|
return null;
|
|
706
|
-
}
|
|
729
|
+
}
|
|
707
730
|
}
|
|
708
731
|
async function flush(session, manualUrls) {
|
|
709
732
|
const key = `${session.platform}:${session.userId}:${session.channelId}`;
|
|
@@ -717,7 +740,7 @@ function apply(ctx, config) {
|
|
|
717
740
|
const errors = [];
|
|
718
741
|
for (const url of urls) {
|
|
719
742
|
const result = await processSingleUrl(session, url);
|
|
720
|
-
if (result.success) {
|
|
743
|
+
if (result.success && result.data) {
|
|
721
744
|
items.push(result.data);
|
|
722
745
|
}
|
|
723
746
|
else {
|
|
@@ -727,11 +750,15 @@ function apply(ctx, config) {
|
|
|
727
750
|
if (errors.length > 0) {
|
|
728
751
|
const errorLines = errors.map(err => `【${err.url.slice(0, 50)}${err.url.length > 50 ? '...' : ''}】: ${err.msg}`);
|
|
729
752
|
const errorMsg = `❌ 解析失败:\n${errorLines.join('\n')}`;
|
|
730
|
-
|
|
753
|
+
try {
|
|
754
|
+
await sendWithTimeout(session, errorMsg);
|
|
755
|
+
}
|
|
756
|
+
catch (e) {
|
|
757
|
+
logger.error(`发送错误消息失败: ${getErrorMessage(e)}`);
|
|
758
|
+
}
|
|
731
759
|
await delay(500);
|
|
732
760
|
}
|
|
733
761
|
if (items.length === 0) {
|
|
734
|
-
await sendTimeout(session, '⚠ 未解析到有效内容');
|
|
735
762
|
return;
|
|
736
763
|
}
|
|
737
764
|
const enableForward = config.enableForward && session.platform === 'onebot';
|
|
@@ -750,7 +777,7 @@ function apply(ctx, config) {
|
|
|
750
777
|
if (config.downloadVideoBeforeSend) {
|
|
751
778
|
const filename = crypto_1.default.createHash('md5').update(item.video).digest('hex');
|
|
752
779
|
const dl = await downloadVideo(item.video, filename, config.userAgent, config.maxVideoSize, config.downloadThreads);
|
|
753
|
-
if (dl.success) {
|
|
780
|
+
if (dl.success && dl.filePath) {
|
|
754
781
|
forwardMessages.push(buildForwardNode(session, koishi_1.h.file(dl.filePath), botName));
|
|
755
782
|
}
|
|
756
783
|
else {
|
|
@@ -762,6 +789,7 @@ function apply(ctx, config) {
|
|
|
762
789
|
}
|
|
763
790
|
}
|
|
764
791
|
catch (e) {
|
|
792
|
+
logger.error(`处理视频发送失败: ${getErrorMessage(e)}`);
|
|
765
793
|
forwardMessages.push(buildForwardNode(session, koishi_1.h.video(item.video), botName));
|
|
766
794
|
}
|
|
767
795
|
}
|
|
@@ -774,27 +802,60 @@ function apply(ctx, config) {
|
|
|
774
802
|
}
|
|
775
803
|
else {
|
|
776
804
|
if (item.text) {
|
|
777
|
-
await
|
|
805
|
+
await sendWithTimeout(session, item.text);
|
|
778
806
|
await delay(300);
|
|
779
807
|
}
|
|
780
808
|
if (item.cover && item.type !== '图集') {
|
|
781
|
-
|
|
809
|
+
try {
|
|
810
|
+
await sendWithTimeout(session, koishi_1.h.image(item.cover));
|
|
811
|
+
}
|
|
812
|
+
catch (e) {
|
|
813
|
+
logger.error(`发送封面失败: ${getErrorMessage(e)}`);
|
|
814
|
+
}
|
|
782
815
|
await delay(300);
|
|
783
816
|
}
|
|
784
817
|
if (item.video && config.showVideoFile) {
|
|
785
818
|
try {
|
|
786
|
-
|
|
819
|
+
if (config.downloadVideoBeforeSend) {
|
|
820
|
+
const filename = crypto_1.default.createHash('md5').update(item.video).digest('hex');
|
|
821
|
+
const dl = await downloadVideo(item.video, filename, config.userAgent, config.maxVideoSize, config.downloadThreads);
|
|
822
|
+
if (dl.success && dl.filePath) {
|
|
823
|
+
await sendWithTimeout(session, koishi_1.h.file(dl.filePath));
|
|
824
|
+
}
|
|
825
|
+
else {
|
|
826
|
+
await sendWithTimeout(session, koishi_1.h.video(item.video));
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
else {
|
|
830
|
+
await sendWithTimeout(session, koishi_1.h.video(item.video));
|
|
831
|
+
}
|
|
787
832
|
}
|
|
788
833
|
catch (e) {
|
|
789
|
-
|
|
834
|
+
logger.error(`发送视频失败: ${getErrorMessage(e)}`);
|
|
835
|
+
try {
|
|
836
|
+
await sendWithTimeout(session, koishi_1.h.video(item.video));
|
|
837
|
+
}
|
|
838
|
+
catch (e2) {
|
|
839
|
+
logger.error(`发送视频链接也失败: ${getErrorMessage(e2)}`);
|
|
840
|
+
}
|
|
790
841
|
}
|
|
791
842
|
await delay(500);
|
|
792
843
|
}
|
|
793
844
|
if ((item.type === '图集' || item.type === 'image') && item.images?.length) {
|
|
794
|
-
|
|
845
|
+
try {
|
|
846
|
+
await sendWithTimeout(session, `📸 图集内容(共${item.totalImageCount}张)`);
|
|
847
|
+
}
|
|
848
|
+
catch (e) {
|
|
849
|
+
logger.error(`发送图集提示失败: ${getErrorMessage(e)}`);
|
|
850
|
+
}
|
|
795
851
|
await delay(300);
|
|
796
852
|
for (const img of item.images) {
|
|
797
|
-
|
|
853
|
+
try {
|
|
854
|
+
await sendWithTimeout(session, koishi_1.h.image(img));
|
|
855
|
+
}
|
|
856
|
+
catch (e) {
|
|
857
|
+
logger.error(`发送图集图片失败: ${getErrorMessage(e)}`);
|
|
858
|
+
}
|
|
798
859
|
await delay(200);
|
|
799
860
|
}
|
|
800
861
|
}
|
|
@@ -806,12 +867,18 @@ function apply(ctx, config) {
|
|
|
806
867
|
}
|
|
807
868
|
if (enableForward && forwardMessages.length) {
|
|
808
869
|
try {
|
|
809
|
-
await
|
|
870
|
+
await sendWithTimeout(session, (0, koishi_1.h)('message', { forward: true }, forwardMessages.slice(0, 100)));
|
|
810
871
|
}
|
|
811
872
|
catch (e) {
|
|
873
|
+
logger.error(`合并转发失败,降级为逐条发送: ${getErrorMessage(e)}`);
|
|
812
874
|
for (const node of forwardMessages) {
|
|
813
|
-
|
|
814
|
-
|
|
875
|
+
try {
|
|
876
|
+
await sendWithTimeout(session, node.data.content);
|
|
877
|
+
await delay(300);
|
|
878
|
+
}
|
|
879
|
+
catch (e2) {
|
|
880
|
+
logger.error(`降级发送失败: ${getErrorMessage(e2)}`);
|
|
881
|
+
}
|
|
815
882
|
}
|
|
816
883
|
}
|
|
817
884
|
}
|
|
@@ -823,21 +890,27 @@ function apply(ctx, config) {
|
|
|
823
890
|
const urls = extractUrl(content);
|
|
824
891
|
if (!urls.length)
|
|
825
892
|
return;
|
|
826
|
-
if (config.showWaitingTip)
|
|
827
|
-
|
|
893
|
+
if (config.showWaitingTip) {
|
|
894
|
+
try {
|
|
895
|
+
await sendWithTimeout(session, config.waitingTipText);
|
|
896
|
+
}
|
|
897
|
+
catch (e) {
|
|
898
|
+
logger.error(`发送等待提示失败: ${getErrorMessage(e)}`);
|
|
899
|
+
}
|
|
900
|
+
}
|
|
828
901
|
await flush(session, urls);
|
|
829
902
|
});
|
|
830
903
|
ctx.command('parse <url>', '手动解析视频').action(async ({ session }, url) => {
|
|
831
904
|
const us = extractUrl(url);
|
|
832
905
|
if (!us.length) {
|
|
833
|
-
await
|
|
906
|
+
await sendWithTimeout(session, '无效的视频链接');
|
|
834
907
|
return;
|
|
835
908
|
}
|
|
836
909
|
await flush(session, us);
|
|
837
910
|
});
|
|
838
911
|
ctx.command('clear-cache', '清空缓存').action(async ({ session }) => {
|
|
839
912
|
clearAllCache();
|
|
840
|
-
await
|
|
913
|
+
await sendWithTimeout(session, '✅ 缓存已清空');
|
|
841
914
|
});
|
|
842
915
|
setInterval(() => {
|
|
843
916
|
const now = Date.now();
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -116,8 +116,8 @@ This is a **multi-platform video/image parsing plugin** developed for the Koishi
|
|
|
116
116
|
## 支持的平台 (Supported Platforms)
|
|
117
117
|
| 平台名称 | 关键词识别 | 解析能力 |
|
|
118
118
|
|----------|------------|----------|
|
|
119
|
-
| 哔哩哔哩 (B站) | bilibili、b23、B站 |
|
|
120
|
-
| 抖音 | douyin、v.douyin.com |
|
|
119
|
+
| 哔哩哔哩 (B站) | bilibili、b23、B站 | 视频、直播 |
|
|
120
|
+
| 抖音 | douyin、v.douyin.com | 短视频、图集 |
|
|
121
121
|
| 快手 | kuaishou、v.kuaishou.com | 短视频、图集 |
|
|
122
122
|
| 微博 | weibo、video.weibo.com | 视频、图集 |
|
|
123
123
|
| 今日头条 | toutiao、ixigua.com | 短视频 |
|
|
@@ -131,6 +131,8 @@ This is a **multi-platform video/image parsing plugin** developed for the Koishi
|
|
|
131
131
|
|----------------------|-------------------------|
|
|
132
132
|
| Minecraft-1314 | 插件完整开发 (Complete plugin development) |
|
|
133
133
|
| JH-Ahua | BugPk-Api 支持 |
|
|
134
|
+
| 星之阁API | 星之阁API 支持 |
|
|
135
|
+
| shangxue | 灵感来源 |
|
|
134
136
|
| (欢迎提交 PR 加入贡献者列表) | (Welcome to submit PR to join the contributor list) |
|
|
135
137
|
|
|
136
138
|
## 许可协议 (License)
|