koishi-plugin-video-parser-all 0.5.3 → 0.5.4

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 +74 -19
  2. package/package.json +1 -1
package/lib/index.js CHANGED
@@ -38,8 +38,8 @@ exports.Config = koishi_1.Schema.intersect([
38
38
  }).description('网络与API设置'),
39
39
  koishi_1.Schema.object({
40
40
  ignoreSendError: koishi_1.Schema.boolean().default(true).description('忽略发送失败错误'),
41
- retryTimes: koishi_1.Schema.number().min(0).default(0).description('API请求重试次数'),
42
- retryInterval: koishi_1.Schema.number().min(0).default(0).description('重试间隔时间(毫秒)'),
41
+ retryTimes: koishi_1.Schema.number().min(0).default(3).description('API请求重试次数'),
42
+ retryInterval: koishi_1.Schema.number().min(0).default(1000).description('重试间隔时间(毫秒)'),
43
43
  }).description('错误与重试设置'),
44
44
  koishi_1.Schema.object({
45
45
  enableForward: koishi_1.Schema.boolean().default(false).description('启用合并转发(仅OneBot平台)'),
@@ -115,7 +115,7 @@ const logger = new koishi_1.Logger(exports.name);
115
115
  const PLATFORM_KEYWORDS = {
116
116
  bilibili: ['bilibili', 'b23', 'B站', 'www.bilibili.com', 'm.bilibili.com', '哔哩哔哩', 'bilibili.com/opus', 'bilibili.com/video', 'b23.tv', 't.bilibili.com', 'bilibili.com/bangumi'],
117
117
  kuaishou: ['kuaishou', '快手', 'v.kuaishou.com', 'www.kuaishou.com', 'kwimgs.com', 'kuaishou.com/app'],
118
- xiaohongshu: ['xiaohongshu', '小红书', 'xhslink.com', 'xiaohongshu.com', 'xhscdn.com', 'xiaohongshu.com/explore', 'xhslink.com/'],
118
+ xiaohongshu: ['xiaohongshu', '小红书', 'xhslink.com', 'xiaohongshu.com', 'xhscdn.com', 'xiaohongshu.com/explore', 'xhslink.com/', 'xiaohongshu.com/discovery/item'],
119
119
  weibo: ['weibo', '微博', 'weibo.com', 'video.weibo.com', 'm.weibo.cn', 'weibo.com/tv/show', 'weibo.com/feed'],
120
120
  toutiao: ['toutiao', '今日头条', 'm.toutiao.com', 'toutiao.com', 'ixigua.com', 'toutiao.com/video', 'ixigua.com/i'],
121
121
  pipigx: ['pipigx', '皮皮搞笑', 'h5.pipigx.com', 'ippzone.com', 'pipigx.com/share'],
@@ -139,7 +139,11 @@ const PLATFORM_ERROR_CODE_MAP = {
139
139
  xiaohongshu: ErrorCode.XIAOHONGSHU_PARSE_FAILED,
140
140
  bilibili: ErrorCode.BILIBILI_PARSE_FAILED,
141
141
  kuaishou: ErrorCode.KUAISHOU_PARSE_FAILED,
142
- weibo: ErrorCode.WEIBO_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
143
147
  };
144
148
  const VARIABLE_MAPPING = {
145
149
  '标题': ['title', 'Title', 'TITLE'],
@@ -315,19 +319,41 @@ function getPlatformType(url) {
315
319
  return 'zuiyou';
316
320
  return null;
317
321
  }
322
+ function cleanUrl(url) {
323
+ try {
324
+ // 处理HTML实体编码
325
+ url = url.replace(/&/g, '&');
326
+ const urlObj = new URL(url);
327
+ if (urlObj.hostname.includes('xiaohongshu.com')) {
328
+ urlObj.searchParams.delete('source');
329
+ urlObj.searchParams.delete('xhsshare');
330
+ urlObj.searchParams.delete('xsec_token');
331
+ urlObj.searchParams.delete('xsec_source');
332
+ return urlObj.origin + urlObj.pathname + urlObj.search;
333
+ }
334
+ if (urlObj.hostname.includes('douyin.com') || urlObj.hostname.includes('v.douyin.com')) {
335
+ return urlObj.origin + urlObj.pathname;
336
+ }
337
+ return url;
338
+ }
339
+ catch (e) {
340
+ // 处理HTML实体编码
341
+ return url.replace(/&/g, '&');
342
+ }
343
+ }
318
344
  async function resolveShortUrl(url) {
319
345
  try {
320
346
  const res = await axios_1.default.head(url, {
321
- timeout: 5000,
322
- maxRedirects: 5,
347
+ timeout: 10000,
348
+ maxRedirects: 10,
323
349
  headers: {
324
350
  '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'
325
351
  }
326
352
  });
327
- return res.request.res?.responseUrl || url;
353
+ return cleanUrl(res.request.res?.responseUrl || url);
328
354
  }
329
355
  catch (e) {
330
- return url;
356
+ return cleanUrl(url);
331
357
  }
332
358
  }
333
359
  function formatDuration(input) {
@@ -512,8 +538,28 @@ function apply(ctx, config) {
512
538
  timeout: config.timeout,
513
539
  headers: { 'User-Agent': config.userAgent || 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' }
514
540
  });
541
+ async function parseWithRetry(url, platform, retryTimes) {
542
+ let lastError = null;
543
+ for (let i = 0; i <= retryTimes; i++) {
544
+ try {
545
+ const res = await http.get(API_CONFIG[platform], {
546
+ params: { url },
547
+ timeout: config.timeout
548
+ });
549
+ return res.data;
550
+ }
551
+ catch (error) {
552
+ lastError = error;
553
+ if (i < retryTimes) {
554
+ await delay(config.retryInterval);
555
+ }
556
+ }
557
+ }
558
+ throw lastError;
559
+ }
515
560
  async function parse(url) {
516
- const realUrl = await resolveShortUrl(url);
561
+ let realUrl = await resolveShortUrl(url);
562
+ realUrl = cleanUrl(realUrl);
517
563
  const platform = getPlatformType(realUrl);
518
564
  if (!platform) {
519
565
  const code = ErrorCode.UNSUPPORTED_PLATFORM;
@@ -529,23 +575,32 @@ function apply(ctx, config) {
529
575
  return { data: null, code, msg };
530
576
  }
531
577
  try {
532
- const res = await http.get(apiUrl, {
533
- params: { url: realUrl },
534
- timeout: config.timeout
535
- });
536
- if (res.data.code !== 200 && res.data.code !== 0) {
537
- const apiErrorMsg = res.data.msg || '解析失败';
578
+ const resData = await parseWithRetry(realUrl, platform, config.retryTimes);
579
+ // 正确的成功判断逻辑:code为200或0,或者msg包含"解析成功"
580
+ const isSuccess = resData.code === 200 || resData.code === 0 ||
581
+ (resData.msg && resData.msg.includes('解析成功'));
582
+ if (!isSuccess) {
583
+ const apiErrorMsg = resData.msg || '解析失败';
538
584
  const platformCode = PLATFORM_ERROR_CODE_MAP[platform] || ErrorCode.API_RETURN_ERROR;
585
+ let detailedMsg = apiErrorMsg;
586
+ if (apiErrorMsg.includes('无法识别解析类型') || apiErrorMsg.includes('未找到有效内容')) {
587
+ detailedMsg = `链接格式不支持或内容已失效:${apiErrorMsg}`;
588
+ }
539
589
  const code = platformCode;
540
- const msg = getErrorInfo(code, apiErrorMsg);
590
+ const msg = getErrorInfo(code, detailedMsg);
541
591
  logger.error(`[${code}] API返回错误: ${platform}, URL: ${url}, 错误: ${apiErrorMsg}`);
542
592
  return { data: null, code, msg };
543
593
  }
544
594
  try {
545
- const parseResult = parseData(res.data, config.maxDescLength, platform);
546
- if (!parseResult.video && !parseResult.images.length && !parseResult.live_photo?.length) {
595
+ const parseResult = parseData(resData, config.maxDescLength, platform);
596
+ // 修正内容判断逻辑:支持live类型和live_photo
597
+ const hasValidContent = parseResult.video ||
598
+ (parseResult.images && parseResult.images.length > 0) ||
599
+ (parseResult.live_photo && parseResult.live_photo.length > 0) ||
600
+ parseResult.type === 'live';
601
+ if (!hasValidContent) {
547
602
  const code = ErrorCode.NO_VIDEO_FOUND;
548
- const msg = getErrorInfo(code, '未找到视频或图片内容');
603
+ const msg = getErrorInfo(code, '链接有效但未找到视频/图片内容(可能是直播、私密内容或已删除)');
549
604
  logger.warn(`[${code}] 解析成功但无有效内容: ${platform}, URL: ${url}`);
550
605
  return { data: null, code, msg };
551
606
  }
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.5.3",
4
+ "version": "0.5.4",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [