koishi-plugin-video-parser-all 0.5.7 → 0.5.9
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.d.ts +1 -6
- package/lib/index.js +116 -186
- package/package.json +1 -1
package/lib/index.d.ts
CHANGED
|
@@ -81,12 +81,7 @@ export declare enum ErrorCode {
|
|
|
81
81
|
NO_IMAGE_FOUND = 3004,
|
|
82
82
|
MESSAGE_SEND_FAILED = 4000,
|
|
83
83
|
MESSAGE_SEND_TIMEOUT = 4001,
|
|
84
|
-
FORWARD_MESSAGE_FAILED = 4002
|
|
85
|
-
DOUYIN_PARSE_FAILED = 5001,
|
|
86
|
-
XIAOHONGSHU_PARSE_FAILED = 5002,
|
|
87
|
-
BILIBILI_PARSE_FAILED = 5003,
|
|
88
|
-
KUAISHOU_PARSE_FAILED = 5004,
|
|
89
|
-
WEIBO_PARSE_FAILED = 5005
|
|
84
|
+
FORWARD_MESSAGE_FAILED = 4002
|
|
90
85
|
}
|
|
91
86
|
export declare const ErrorMessageMap: Record<ErrorCode, string>;
|
|
92
87
|
export declare function apply(ctx: Context, config: any): void;
|
package/lib/index.js
CHANGED
|
@@ -76,11 +76,6 @@ var ErrorCode;
|
|
|
76
76
|
ErrorCode[ErrorCode["MESSAGE_SEND_FAILED"] = 4000] = "MESSAGE_SEND_FAILED";
|
|
77
77
|
ErrorCode[ErrorCode["MESSAGE_SEND_TIMEOUT"] = 4001] = "MESSAGE_SEND_TIMEOUT";
|
|
78
78
|
ErrorCode[ErrorCode["FORWARD_MESSAGE_FAILED"] = 4002] = "FORWARD_MESSAGE_FAILED";
|
|
79
|
-
ErrorCode[ErrorCode["DOUYIN_PARSE_FAILED"] = 5001] = "DOUYIN_PARSE_FAILED";
|
|
80
|
-
ErrorCode[ErrorCode["XIAOHONGSHU_PARSE_FAILED"] = 5002] = "XIAOHONGSHU_PARSE_FAILED";
|
|
81
|
-
ErrorCode[ErrorCode["BILIBILI_PARSE_FAILED"] = 5003] = "BILIBILI_PARSE_FAILED";
|
|
82
|
-
ErrorCode[ErrorCode["KUAISHOU_PARSE_FAILED"] = 5004] = "KUAISHOU_PARSE_FAILED";
|
|
83
|
-
ErrorCode[ErrorCode["WEIBO_PARSE_FAILED"] = 5005] = "WEIBO_PARSE_FAILED";
|
|
84
79
|
})(ErrorCode || (exports.ErrorCode = ErrorCode = {}));
|
|
85
80
|
exports.ErrorMessageMap = {
|
|
86
81
|
[ErrorCode.SUCCESS]: '操作成功',
|
|
@@ -103,11 +98,6 @@ exports.ErrorMessageMap = {
|
|
|
103
98
|
[ErrorCode.MESSAGE_SEND_FAILED]: '消息发送失败',
|
|
104
99
|
[ErrorCode.MESSAGE_SEND_TIMEOUT]: '消息发送超时',
|
|
105
100
|
[ErrorCode.FORWARD_MESSAGE_FAILED]: '合并转发失败',
|
|
106
|
-
[ErrorCode.DOUYIN_PARSE_FAILED]: '抖音链接解析失败',
|
|
107
|
-
[ErrorCode.XIAOHONGSHU_PARSE_FAILED]: '小红书链接解析失败',
|
|
108
|
-
[ErrorCode.BILIBILI_PARSE_FAILED]: 'B站链接解析失败',
|
|
109
|
-
[ErrorCode.KUAISHOU_PARSE_FAILED]: '快手链接解析失败',
|
|
110
|
-
[ErrorCode.WEIBO_PARSE_FAILED]: '微博链接解析失败',
|
|
111
101
|
};
|
|
112
102
|
const processed = new Map();
|
|
113
103
|
const linkBuffer = new Map();
|
|
@@ -134,29 +124,18 @@ const API_CONFIG = {
|
|
|
134
124
|
pipixia: 'https://api.bugpk.com/api/ppx',
|
|
135
125
|
zuiyou: 'https://api.bugpk.com/api/zuiyou'
|
|
136
126
|
};
|
|
137
|
-
const PLATFORM_ERROR_CODE_MAP = {
|
|
138
|
-
douyin: ErrorCode.DOUYIN_PARSE_FAILED,
|
|
139
|
-
xiaohongshu: ErrorCode.XIAOHONGSHU_PARSE_FAILED,
|
|
140
|
-
bilibili: ErrorCode.BILIBILI_PARSE_FAILED,
|
|
141
|
-
kuaishou: ErrorCode.KUAISHOU_PARSE_FAILED,
|
|
142
|
-
weibo: ErrorCode.WEIBO_PARSE_FAILED,
|
|
143
|
-
toutiao: ErrorCode.API_RETURN_ERROR,
|
|
144
|
-
pipigx: ErrorCode.API_RETURN_ERROR,
|
|
145
|
-
pipixia: ErrorCode.API_RETURN_ERROR,
|
|
146
|
-
zuiyou: ErrorCode.API_RETURN_ERROR
|
|
147
|
-
};
|
|
148
127
|
const VARIABLE_MAPPING = {
|
|
149
|
-
'标题': ['title', 'Title', 'TITLE'],
|
|
150
|
-
'作者': ['author.name', 'author', 'name', 'Author', 'Name', 'owner.name'],
|
|
151
|
-
'简介': ['desc', 'description', 'Desc', 'Description', 'content', 'Content'],
|
|
152
|
-
'视频时长': ['duration', 'Duration', 'time', 'Time'],
|
|
153
|
-
'点赞数': ['like', 'Like', 'attitudes_count', 'digg_count', 'praise', 'stat.like'],
|
|
154
|
-
'投币数': ['coin', 'Coin', 'bi', 'Bi'],
|
|
155
|
-
'收藏数': ['collect', 'Collect', 'favorite', 'Favorite', 'star', 'Star', 'stat.collect'],
|
|
156
|
-
'转发数': ['share', 'Share', 'forward', 'Forward', 'repost', 'stat.share', 'reposts_count'],
|
|
157
|
-
'播放数': ['view', 'View', 'play_count', 'PlayCount', 'play'],
|
|
128
|
+
'标题': ['title', 'Title', 'TITLE', 'note_title', 'content_title'],
|
|
129
|
+
'作者': ['author.name', 'author', 'name', 'Author', 'Name', 'owner.name', 'nickname', 'user_name'],
|
|
130
|
+
'简介': ['desc', 'description', 'Desc', 'Description', 'content', 'Content', 'note_desc', 'text'],
|
|
131
|
+
'视频时长': ['duration', 'Duration', 'time', 'Time', 'video_duration'],
|
|
132
|
+
'点赞数': ['like', 'Like', 'attitudes_count', 'digg_count', 'praise', 'stat.like', 'liked_count'],
|
|
133
|
+
'投币数': ['coin', 'Coin', 'bi', 'Bi', 'stat.coin'],
|
|
134
|
+
'收藏数': ['collect', 'Collect', 'favorite', 'Favorite', 'star', 'Star', 'stat.collect', 'collected_count'],
|
|
135
|
+
'转发数': ['share', 'Share', 'forward', 'Forward', 'repost', 'stat.share', 'reposts_count', 'shared_count'],
|
|
136
|
+
'播放数': ['view', 'View', 'play_count', 'PlayCount', 'play', 'stat.view', 'play_times'],
|
|
158
137
|
'评论数': ['comment', 'Comment', 'comments_count', 'comment_count', 'discuss', 'stat.comment'],
|
|
159
|
-
'音乐名': ['music.title', 'music_name', 'audio_name', 'sound_name', 'muisic', 'music']
|
|
138
|
+
'音乐名': ['music.title', 'music_name', 'audio_name', 'sound_name', 'muisic', 'music', 'bgm_name']
|
|
160
139
|
};
|
|
161
140
|
function getErrorInfo(code, detail) {
|
|
162
141
|
const baseMsg = exports.ErrorMessageMap[code] || exports.ErrorMessageMap[ErrorCode.UNKNOWN_ERROR];
|
|
@@ -324,19 +303,18 @@ function cleanUrl(url) {
|
|
|
324
303
|
url = url.replace(/&/g, '&');
|
|
325
304
|
const urlObj = new URL(url);
|
|
326
305
|
if (urlObj.hostname.includes('xiaohongshu.com')) {
|
|
327
|
-
urlObj.searchParams.delete(
|
|
328
|
-
urlObj.searchParams.delete('xhsshare');
|
|
329
|
-
urlObj.searchParams.delete('xsec_token');
|
|
330
|
-
urlObj.searchParams.delete('xsec_source');
|
|
306
|
+
urlObj.searchParams.forEach((_, key) => urlObj.searchParams.delete(key));
|
|
331
307
|
return urlObj.origin + urlObj.pathname;
|
|
332
308
|
}
|
|
333
309
|
if (urlObj.hostname.includes('douyin.com') || urlObj.hostname.includes('v.douyin.com')) {
|
|
310
|
+
urlObj.searchParams.delete('source');
|
|
311
|
+
urlObj.searchParams.delete('share_type');
|
|
334
312
|
return urlObj.origin + urlObj.pathname;
|
|
335
313
|
}
|
|
336
314
|
return url;
|
|
337
315
|
}
|
|
338
316
|
catch (e) {
|
|
339
|
-
return url.replace(/&/g, '&');
|
|
317
|
+
return url.replace(/&/g, '&').replace(/\?.*/, '');
|
|
340
318
|
}
|
|
341
319
|
}
|
|
342
320
|
async function resolveShortUrl(url) {
|
|
@@ -345,7 +323,9 @@ async function resolveShortUrl(url) {
|
|
|
345
323
|
timeout: 10000,
|
|
346
324
|
maxRedirects: 10,
|
|
347
325
|
headers: {
|
|
348
|
-
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
|
|
326
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
|
327
|
+
'Referer': 'https://www.baidu.com/',
|
|
328
|
+
'Cookie': 'xhsTrackerId=xxx; xhs_sessionId=xxx'
|
|
349
329
|
}
|
|
350
330
|
});
|
|
351
331
|
return cleanUrl(res.request.res?.responseUrl || url);
|
|
@@ -373,7 +353,7 @@ function formatDuration(input) {
|
|
|
373
353
|
: `${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
|
|
374
354
|
}
|
|
375
355
|
function getNestedValue(obj, path) {
|
|
376
|
-
if (!obj || typeof obj !== 'object')
|
|
356
|
+
if (!obj || typeof obj !== 'object' || !path)
|
|
377
357
|
return undefined;
|
|
378
358
|
const keys = path.split('.');
|
|
379
359
|
let value = obj;
|
|
@@ -385,142 +365,86 @@ function getNestedValue(obj, path) {
|
|
|
385
365
|
return value;
|
|
386
366
|
}
|
|
387
367
|
function findValueInObject(obj, keys) {
|
|
388
|
-
if (!obj || typeof obj !== 'object')
|
|
368
|
+
if (!obj || typeof obj !== 'object' || !keys || keys.length === 0)
|
|
389
369
|
return undefined;
|
|
390
370
|
for (const key of keys) {
|
|
391
371
|
if (key.includes('.')) {
|
|
392
372
|
const value = getNestedValue(obj, key);
|
|
393
|
-
if (value !== undefined && value !== null && value !== '')
|
|
373
|
+
if (value !== undefined && value !== null && value !== '' && value !== 0)
|
|
394
374
|
return value;
|
|
395
375
|
}
|
|
396
376
|
else {
|
|
397
|
-
if (obj[key] !== undefined && obj[key] !== null && obj[key] !== '')
|
|
377
|
+
if (obj[key] !== undefined && obj[key] !== null && obj[key] !== '' && obj[key] !== 0)
|
|
398
378
|
return obj[key];
|
|
399
379
|
const lowerKey = key.toLowerCase();
|
|
400
380
|
for (const objKey of Object.keys(obj)) {
|
|
401
381
|
if (objKey.toLowerCase() === lowerKey) {
|
|
402
|
-
|
|
382
|
+
const val = obj[objKey];
|
|
383
|
+
if (val !== undefined && val !== null && val !== '' && val !== 0)
|
|
384
|
+
return val;
|
|
403
385
|
}
|
|
404
386
|
}
|
|
405
387
|
}
|
|
406
388
|
}
|
|
407
389
|
return undefined;
|
|
408
390
|
}
|
|
409
|
-
function parseData(rawResponse, maxDescLength
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
data = rawResponse.data;
|
|
413
|
-
}
|
|
414
|
-
else if (platform === 'douyin' && rawResponse.data) {
|
|
415
|
-
data = rawResponse.data;
|
|
416
|
-
}
|
|
417
|
-
else if (data.data) {
|
|
418
|
-
data = data.data;
|
|
419
|
-
}
|
|
391
|
+
function parseData(rawResponse, maxDescLength) {
|
|
392
|
+
const rootData = rawResponse || {};
|
|
393
|
+
const data = rootData.data || rootData.result || rootData || {};
|
|
420
394
|
const stat = {};
|
|
421
395
|
Object.entries(VARIABLE_MAPPING).forEach(([varName, keys]) => {
|
|
422
|
-
const value = findValueInObject(data, keys);
|
|
423
|
-
if (value !== undefined)
|
|
396
|
+
const value = findValueInObject(data, keys) || findValueInObject(rootData, keys);
|
|
397
|
+
if (value !== undefined)
|
|
424
398
|
stat[varName] = value;
|
|
425
|
-
}
|
|
426
399
|
});
|
|
427
400
|
let type = 'video';
|
|
428
|
-
if (
|
|
401
|
+
if (data.jx?.type)
|
|
429
402
|
type = data.jx.type;
|
|
430
|
-
|
|
431
|
-
else if (data.type) {
|
|
403
|
+
else if (data.type)
|
|
432
404
|
type = data.type;
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
if (
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
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
|
-
}
|
|
405
|
+
else if (data.images && data.images.length > 0)
|
|
406
|
+
type = 'image';
|
|
407
|
+
else if (data.pics && data.pics.length > 0)
|
|
408
|
+
type = '图集';
|
|
409
|
+
const title = findValueInObject(data, VARIABLE_MAPPING['标题']) || findValueInObject(rootData, VARIABLE_MAPPING['标题']) || '无标题';
|
|
410
|
+
const author = findValueInObject(data, VARIABLE_MAPPING['作者']) || findValueInObject(rootData, VARIABLE_MAPPING['作者']) || '未知作者';
|
|
411
|
+
let desc = findValueInObject(data, VARIABLE_MAPPING['简介']) || findValueInObject(rootData, VARIABLE_MAPPING['简介']) || title;
|
|
464
412
|
desc = desc.toString().slice(0, maxDescLength);
|
|
465
|
-
|
|
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
|
-
}
|
|
413
|
+
const cover = findValueInObject(data, ['cover', 'imgurl', 'pic', 'thumbnail', 'cover_url']) || findValueInObject(rootData, ['cover', 'imgurl', 'pic', 'thumbnail', 'cover_url']) || '';
|
|
475
414
|
let images = [];
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
else
|
|
480
|
-
images =
|
|
481
|
-
}
|
|
482
|
-
if (!Array.isArray(images))
|
|
483
|
-
images = [images];
|
|
415
|
+
const imgList = findValueInObject(data, ['images', 'pics', 'pic_urls', 'image_list']) || findValueInObject(rootData, ['images', 'pics', 'pic_urls', 'image_list']) || [];
|
|
416
|
+
if (Array.isArray(imgList))
|
|
417
|
+
images = imgList.filter(img => img && typeof img === 'string');
|
|
418
|
+
else if (imgList && typeof imgList === 'string')
|
|
419
|
+
images = [imgList];
|
|
484
420
|
let video = '';
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
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
|
-
}
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
const durationValue = findValueInObject(data, ['duration']);
|
|
421
|
+
const videoUrls = [
|
|
422
|
+
findValueInObject(data, ['url', 'video_url', 'download_url', 'playUrl', 'mp4_url']),
|
|
423
|
+
findValueInObject(rootData, ['url', 'video_url', 'download_url', 'playUrl', 'mp4_url']),
|
|
424
|
+
data.video?.url,
|
|
425
|
+
data.item?.url,
|
|
426
|
+
rootData.video?.url,
|
|
427
|
+
rootData.item?.url
|
|
428
|
+
];
|
|
429
|
+
for (const url of videoUrls) {
|
|
430
|
+
if (url && typeof url === 'string' && url.startsWith('http')) {
|
|
431
|
+
video = url;
|
|
432
|
+
break;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
const durationValue = findValueInObject(data, VARIABLE_MAPPING['视频时长']) || findValueInObject(rootData, VARIABLE_MAPPING['视频时长']);
|
|
512
436
|
const duration = typeof durationValue === 'number' ? durationValue : parseInt(durationValue) || 0;
|
|
513
437
|
const durationFormatted = formatDuration(durationValue || 0);
|
|
514
|
-
const live_photo = data.live_photo || [];
|
|
515
|
-
const music = findValueInObject(data, ['
|
|
516
|
-
const 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 =
|
|
521
|
-
const reposts_count =
|
|
522
|
-
const attitudes_count =
|
|
523
|
-
const comments_count =
|
|
438
|
+
const live_photo = data.live_photo || rootData.live_photo || [];
|
|
439
|
+
const music = findValueInObject(data, VARIABLE_MAPPING['音乐名']) || findValueInObject(rootData, VARIABLE_MAPPING['音乐名']) || '';
|
|
440
|
+
const h_w = data.item?.h_w || rootData.item?.h_w || [];
|
|
441
|
+
const quality_urls = data.quality_urls || rootData.quality_urls || {};
|
|
442
|
+
const default_quality = data.default_quality || rootData.default_quality || '';
|
|
443
|
+
const download_url = data.download_url || rootData.download_url || video;
|
|
444
|
+
const play_count = stat['播放数'] || '';
|
|
445
|
+
const reposts_count = stat['转发数'] || 0;
|
|
446
|
+
const attitudes_count = stat['点赞数'] || 0;
|
|
447
|
+
const comments_count = stat['评论数'] || 0;
|
|
524
448
|
return {
|
|
525
449
|
type: type,
|
|
526
450
|
rawData: rawResponse,
|
|
@@ -536,7 +460,7 @@ function parseData(rawResponse, maxDescLength, platform) {
|
|
|
536
460
|
live_photo,
|
|
537
461
|
music,
|
|
538
462
|
h_w,
|
|
539
|
-
jx: data.jx || null,
|
|
463
|
+
jx: data.jx || rootData.jx || null,
|
|
540
464
|
quality_urls,
|
|
541
465
|
default_quality,
|
|
542
466
|
download_url,
|
|
@@ -546,18 +470,22 @@ function parseData(rawResponse, maxDescLength, platform) {
|
|
|
546
470
|
comments_count
|
|
547
471
|
};
|
|
548
472
|
}
|
|
549
|
-
function generateFormattedText(
|
|
473
|
+
function generateFormattedText(parseData, config) {
|
|
550
474
|
let format = config.unifiedMessageFormat;
|
|
551
|
-
if (
|
|
552
|
-
format =
|
|
553
|
-
|
|
554
|
-
let result = format;
|
|
555
|
-
const formatLines = result.split('\n');
|
|
475
|
+
if (!format)
|
|
476
|
+
format = '标题:${标题}\n作者:${作者}\n简介:${简介}';
|
|
477
|
+
const formatLines = format.split('\n');
|
|
556
478
|
const validLines = [];
|
|
557
479
|
formatLines.forEach((line) => {
|
|
480
|
+
if (!line.trim())
|
|
481
|
+
return;
|
|
558
482
|
let isValid = true;
|
|
559
483
|
let processedLine = line;
|
|
560
484
|
const varMatches = line.match(/\$\{([^}]+)\}/g) || [];
|
|
485
|
+
if (varMatches.length === 0) {
|
|
486
|
+
validLines.push(line);
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
561
489
|
varMatches.forEach((varMatch) => {
|
|
562
490
|
const varName = varMatch.replace(/\$\{|\}/g, '');
|
|
563
491
|
const value = parseData.stat[varName];
|
|
@@ -572,7 +500,7 @@ function generateFormattedText(platform, parseData, config) {
|
|
|
572
500
|
validLines.push(processedLine);
|
|
573
501
|
}
|
|
574
502
|
});
|
|
575
|
-
result = validLines.join('\n').trim();
|
|
503
|
+
let result = validLines.join('\n').trim();
|
|
576
504
|
if (!result) {
|
|
577
505
|
result = `标题:${parseData.title}\n作者:${parseData.author}\n简介:${parseData.desc}`;
|
|
578
506
|
}
|
|
@@ -616,23 +544,31 @@ function apply(ctx, config) {
|
|
|
616
544
|
clearAllCache();
|
|
617
545
|
const http = axios_1.default.create({
|
|
618
546
|
timeout: config.timeout,
|
|
619
|
-
headers: {
|
|
547
|
+
headers: {
|
|
548
|
+
'User-Agent': config.userAgent || 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
|
|
549
|
+
'Referer': 'https://www.baidu.com/',
|
|
550
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
551
|
+
}
|
|
620
552
|
});
|
|
621
553
|
async function parseWithRetry(url, platform, retryTimes) {
|
|
622
554
|
let lastError = null;
|
|
623
555
|
for (let i = 0; i <= retryTimes; i++) {
|
|
624
556
|
try {
|
|
625
|
-
const params = { url };
|
|
557
|
+
const params = { url, proxyurl: '' };
|
|
626
558
|
const res = await http.get(API_CONFIG[platform], {
|
|
627
559
|
params,
|
|
628
|
-
timeout: config.timeout
|
|
560
|
+
timeout: config.timeout,
|
|
561
|
+
headers: {
|
|
562
|
+
'X-Requested-With': 'XMLHttpRequest',
|
|
563
|
+
'Origin': platform === 'xiaohongshu' ? 'https://api.bugpk.com' : 'https://www.baidu.com'
|
|
564
|
+
}
|
|
629
565
|
});
|
|
630
566
|
return res.data;
|
|
631
567
|
}
|
|
632
568
|
catch (error) {
|
|
633
569
|
lastError = error;
|
|
634
570
|
if (i < retryTimes) {
|
|
635
|
-
await delay(config.retryInterval);
|
|
571
|
+
await delay(config.retryInterval * (i + 1));
|
|
636
572
|
}
|
|
637
573
|
}
|
|
638
574
|
}
|
|
@@ -657,28 +593,24 @@ function apply(ctx, config) {
|
|
|
657
593
|
}
|
|
658
594
|
try {
|
|
659
595
|
const resData = await parseWithRetry(realUrl, platform, config.retryTimes);
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
isSuccess = resData.code === 200 || resData.code === 0 ||
|
|
666
|
-
(resData.msg && resData.msg.includes('解析成功'));
|
|
596
|
+
if (!resData || Object.keys(resData).length === 0) {
|
|
597
|
+
const code = ErrorCode.API_EMPTY_RESPONSE;
|
|
598
|
+
const msg = getErrorInfo(code, 'API返回空数据');
|
|
599
|
+
logger.error(`[${code}] ${url}`);
|
|
600
|
+
return { data: null, code, msg };
|
|
667
601
|
}
|
|
602
|
+
const isSuccess = resData.code === 0 || resData.code === 200 || resData.code === 1 ||
|
|
603
|
+
(resData.msg && (resData.msg.includes('解析成功') || resData.msg.includes('success'))) ||
|
|
604
|
+
!!resData.data || !!resData.result || !!resData.video || !!resData.images;
|
|
668
605
|
if (!isSuccess) {
|
|
669
|
-
const apiErrorMsg = resData.msg || '解析失败';
|
|
670
|
-
const
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
detailedMsg = `链接格式不支持或内容已失效:${apiErrorMsg}`;
|
|
674
|
-
}
|
|
675
|
-
const code = platformCode;
|
|
676
|
-
const msg = getErrorInfo(code, detailedMsg);
|
|
677
|
-
logger.error(`[${code}] API返回错误: ${platform}, URL: ${url}, 错误: ${apiErrorMsg}`);
|
|
606
|
+
const apiErrorMsg = resData.msg || resData.error || '解析失败';
|
|
607
|
+
const code = ErrorCode.API_RETURN_ERROR;
|
|
608
|
+
const msg = getErrorInfo(code, apiErrorMsg);
|
|
609
|
+
logger.error(`[${code}] API返回错误: ${url}, 错误: ${apiErrorMsg}`);
|
|
678
610
|
return { data: null, code, msg };
|
|
679
611
|
}
|
|
680
612
|
try {
|
|
681
|
-
const parseResult = parseData(resData, config.maxDescLength
|
|
613
|
+
const parseResult = parseData(resData, config.maxDescLength);
|
|
682
614
|
const hasValidContent = parseResult.video ||
|
|
683
615
|
(parseResult.images && parseResult.images.length > 0) ||
|
|
684
616
|
(parseResult.live_photo && parseResult.live_photo.length > 0) ||
|
|
@@ -686,11 +618,11 @@ function apply(ctx, config) {
|
|
|
686
618
|
parseResult.type === '图集';
|
|
687
619
|
if (!hasValidContent) {
|
|
688
620
|
const code = ErrorCode.NO_VIDEO_FOUND;
|
|
689
|
-
const msg = getErrorInfo(code, '
|
|
690
|
-
logger.warn(`[${code}] 解析成功但无有效内容: ${
|
|
621
|
+
const msg = getErrorInfo(code, '链接有效但未获取到有效视频/图片内容,可能是私密/需要登录/已删除内容');
|
|
622
|
+
logger.warn(`[${code}] 解析成功但无有效内容: ${url}`);
|
|
691
623
|
return { data: null, code, msg };
|
|
692
624
|
}
|
|
693
|
-
logger.info(`[${ErrorCode.SUCCESS}]
|
|
625
|
+
logger.info(`[${ErrorCode.SUCCESS}] 解析成功: ${url}`);
|
|
694
626
|
return {
|
|
695
627
|
data: parseResult,
|
|
696
628
|
code: ErrorCode.SUCCESS,
|
|
@@ -701,7 +633,7 @@ function apply(ctx, config) {
|
|
|
701
633
|
const errorMsg = getErrorMessage(parseError);
|
|
702
634
|
const code = ErrorCode.API_DATA_PARSE_FAILED;
|
|
703
635
|
const msg = getErrorInfo(code, errorMsg);
|
|
704
|
-
logger.error(`[${code}] 解析数据失败: ${
|
|
636
|
+
logger.error(`[${code}] 解析数据失败: ${url}, 错误: ${errorMsg}`);
|
|
705
637
|
return { data: null, code, msg };
|
|
706
638
|
}
|
|
707
639
|
}
|
|
@@ -711,14 +643,14 @@ function apply(ctx, config) {
|
|
|
711
643
|
if (errorMsg.includes('timeout')) {
|
|
712
644
|
code = ErrorCode.REQUEST_TIMEOUT;
|
|
713
645
|
}
|
|
714
|
-
else if (errorMsg.includes('Network') || errorMsg.includes('network')) {
|
|
646
|
+
else if (errorMsg.includes('Network') || errorMsg.includes('network') || errorMsg.includes('404') || errorMsg.includes('500')) {
|
|
715
647
|
code = ErrorCode.NETWORK_ERROR;
|
|
716
648
|
}
|
|
717
649
|
else {
|
|
718
650
|
code = ErrorCode.NETWORK_ERROR;
|
|
719
651
|
}
|
|
720
652
|
const msg = getErrorInfo(code, errorMsg);
|
|
721
|
-
logger.error(`[${code}] 解析请求失败: ${
|
|
653
|
+
logger.error(`[${code}] 解析请求失败: ${url}, 错误: ${errorMsg}`);
|
|
722
654
|
return { data: null, code, msg };
|
|
723
655
|
}
|
|
724
656
|
}
|
|
@@ -735,8 +667,7 @@ function apply(ctx, config) {
|
|
|
735
667
|
if (!result.data)
|
|
736
668
|
return { data: null, code: result.code, msg: result.msg };
|
|
737
669
|
const parseData = result.data;
|
|
738
|
-
const
|
|
739
|
-
const text = generateFormattedText(platform, parseData, config);
|
|
670
|
+
const text = generateFormattedText(parseData, config);
|
|
740
671
|
return {
|
|
741
672
|
data: {
|
|
742
673
|
text,
|
|
@@ -798,14 +729,14 @@ function apply(ctx, config) {
|
|
|
798
729
|
}
|
|
799
730
|
}
|
|
800
731
|
if (errors.length > 0) {
|
|
801
|
-
const errorLines = errors.map(err => `【${err.url}】: ${err.msg}`);
|
|
732
|
+
const errorLines = errors.map(err => `【${err.url.slice(0, 50)}${err.url.length > 50 ? '...' : ''}】: ${err.msg}`);
|
|
802
733
|
const errorMsg = `❌ 解析失败列表(共${errors.length}个链接):\n${errorLines.join('\n')}`;
|
|
803
734
|
logger.error(`解析失败数量: ${errors.length}, 错误码列表: ${errors.map(e => e.code).join(', ')}`);
|
|
804
735
|
await sendTimeout(session, errorMsg);
|
|
805
736
|
await delay(500);
|
|
806
737
|
}
|
|
807
738
|
if (items.length === 0) {
|
|
808
|
-
const failMsg = getErrorInfo(ErrorCode.UNKNOWN_ERROR, '
|
|
739
|
+
const failMsg = getErrorInfo(ErrorCode.UNKNOWN_ERROR, '所有链接均解析失败,请检查链接是否有效/是否需要登录,或稍后重试');
|
|
809
740
|
await sendTimeout(session, `⚠ ${failMsg}`);
|
|
810
741
|
return;
|
|
811
742
|
}
|
|
@@ -932,8 +863,7 @@ function apply(ctx, config) {
|
|
|
932
863
|
const filename = crypto_1.default.createHash('md5').update(item.video).digest('hex');
|
|
933
864
|
const downloadResult = await downloadVideo(item.video, filename, config.userAgent, config.maxVideoSize, config.downloadThreads);
|
|
934
865
|
if (downloadResult.code !== ErrorCode.SUCCESS) {
|
|
935
|
-
|
|
936
|
-
await sendTimeout(session, koishi_1.h.text(`${errorMsg}\n链接:${item.video}`));
|
|
866
|
+
await sendTimeout(session, koishi_1.h.text(`${getErrorInfo(downloadResult.code)}\n链接:${item.video}`));
|
|
937
867
|
continue;
|
|
938
868
|
}
|
|
939
869
|
else {
|
package/package.json
CHANGED