koishi-plugin-video-parser-all 1.0.8 → 1.1.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.d.ts CHANGED
@@ -32,6 +32,7 @@ export declare const Config: Schema<{
32
32
  customApis?: ({
33
33
  platform?: "bilibili" | "douyin" | "kuaishou" | "xiaohongshu" | "weibo" | "xigua" | "youtube" | "tiktok" | "acfun" | "zhihu" | "weishi" | "huya" | "haokan" | "meipai" | "twitter" | "instagram" | "doubao" | null | undefined;
34
34
  apiUrl?: string | null | undefined;
35
+ useDedicatedFirst?: boolean | null | undefined;
35
36
  } & import("cosmokit").Dict)[] | null | undefined;
36
37
  } & {
37
38
  waitingTipText?: string | null | undefined;
@@ -71,6 +72,7 @@ export declare const Config: Schema<{
71
72
  customApis: Schemastery.ObjectT<{
72
73
  platform: Schema<"bilibili" | "douyin" | "kuaishou" | "xiaohongshu" | "weibo" | "xigua" | "youtube" | "tiktok" | "acfun" | "zhihu" | "weishi" | "huya" | "haokan" | "meipai" | "twitter" | "instagram" | "doubao", "bilibili" | "douyin" | "kuaishou" | "xiaohongshu" | "weibo" | "xigua" | "youtube" | "tiktok" | "acfun" | "zhihu" | "weishi" | "huya" | "haokan" | "meipai" | "twitter" | "instagram" | "doubao">;
73
74
  apiUrl: Schema<string, string>;
75
+ useDedicatedFirst: Schema<boolean, boolean>;
74
76
  }>[];
75
77
  } & {
76
78
  waitingTipText: string;
package/lib/index.js CHANGED
@@ -48,29 +48,30 @@ exports.Config = koishi_1.Schema.intersect([
48
48
  koishi_1.Schema.object({
49
49
  primaryApiUrl: koishi_1.Schema.string().default('https://api.bugpk.com/api/short_videos').description('主 API 地址'),
50
50
  backupApiUrl: koishi_1.Schema.string().default('https://api.bugpk.com/api/svparse').description('备用主 API 地址(仅支持抖音/小红书/ins/即梦)'),
51
- useDedicatedApiFirst: koishi_1.Schema.boolean().default(false).description('优先使用平台专属 API,失败后回退到通用 API'),
51
+ useDedicatedApiFirst: koishi_1.Schema.boolean().default(false).description('全局默认:是否优先使用平台专属 API(各平台可单独覆盖)'),
52
52
  customApis: koishi_1.Schema.array(koishi_1.Schema.object({
53
53
  platform: koishi_1.Schema.union([
54
- 'bilibili',
55
- 'douyin',
56
- 'kuaishou',
57
- 'xiaohongshu',
58
- 'weibo',
59
- 'xigua',
60
- 'youtube',
61
- 'tiktok',
62
- 'acfun',
63
- 'zhihu',
64
- 'weishi',
65
- 'huya',
66
- 'haokan',
67
- 'meipai',
68
- 'twitter',
69
- 'instagram',
70
- 'doubao',
54
+ koishi_1.Schema.const('bilibili').description('哔哩哔哩'),
55
+ koishi_1.Schema.const('douyin').description('抖音'),
56
+ koishi_1.Schema.const('kuaishou').description('快手'),
57
+ koishi_1.Schema.const('xiaohongshu').description('小红书'),
58
+ koishi_1.Schema.const('weibo').description('微博'),
59
+ koishi_1.Schema.const('xigua').description('西瓜视频'),
60
+ koishi_1.Schema.const('youtube').description('YouTube'),
61
+ koishi_1.Schema.const('tiktok').description('TikTok'),
62
+ koishi_1.Schema.const('acfun').description('AcFun'),
63
+ koishi_1.Schema.const('zhihu').description('知乎'),
64
+ koishi_1.Schema.const('weishi').description('微视'),
65
+ koishi_1.Schema.const('huya').description('虎牙'),
66
+ koishi_1.Schema.const('haokan').description('好看视频'),
67
+ koishi_1.Schema.const('meipai').description('美拍'),
68
+ koishi_1.Schema.const('twitter').description('Twitter/X'),
69
+ koishi_1.Schema.const('instagram').description('Instagram'),
70
+ koishi_1.Schema.const('doubao').description('豆包'),
71
71
  ]).description('选择平台'),
72
72
  apiUrl: koishi_1.Schema.string().description('API 地址'),
73
- })).default([]).description('自定义平台专属 API,可覆盖默认专属 API'),
73
+ useDedicatedFirst: koishi_1.Schema.boolean().default(false).description('该平台优先使用此专属 API'),
74
+ })).default([]).description('自定义平台专属 API,可覆盖默认专属 API,并可单独设定优先策略'),
74
75
  }).description('API 选择设置'),
75
76
  koishi_1.Schema.object({
76
77
  waitingTipText: koishi_1.Schema.string().default('正在解析视频,请稍候...').description('解析等待提示'),
@@ -99,6 +100,11 @@ function debugLog(level, ...args) {
99
100
  }).join(' ')}`;
100
101
  logger.info(message);
101
102
  }
103
+ const urlCache = new lru_cache_1.LRUCache({
104
+ max: 500,
105
+ ttl: 10 * 60 * 1000,
106
+ updateAgeOnGet: false,
107
+ });
102
108
  function linkTypeParser(content) {
103
109
  content = content.replace(/\\\//g, '/');
104
110
  const rules = [
@@ -147,7 +153,46 @@ function linkTypeParser(content) {
147
153
  }
148
154
  function extractAllUrlsFromMessage(session) {
149
155
  const content = session.content?.trim() || '';
150
- return linkTypeParser(content);
156
+ const matchedLinks = linkTypeParser(content);
157
+ const cardsContent = [];
158
+ if (session.elements) {
159
+ for (const elem of session.elements) {
160
+ if (elem.type === 'xml' && elem.data) {
161
+ cardsContent.push(elem.data);
162
+ }
163
+ else if (elem.type === 'json' && elem.data) {
164
+ try {
165
+ const json = JSON.parse(elem.data);
166
+ const extract = (obj) => {
167
+ if (!obj || typeof obj !== 'object')
168
+ return;
169
+ for (const val of Object.values(obj)) {
170
+ if (typeof val === 'string') {
171
+ cardsContent.push(val);
172
+ }
173
+ else if (typeof val === 'object')
174
+ extract(val);
175
+ }
176
+ };
177
+ extract(json);
178
+ }
179
+ catch { }
180
+ }
181
+ }
182
+ }
183
+ for (const cardContent of cardsContent) {
184
+ const cardLinks = linkTypeParser(cardContent);
185
+ matchedLinks.push(...cardLinks);
186
+ }
187
+ const seen = new Set();
188
+ const result = [];
189
+ for (const link of matchedLinks) {
190
+ if (!seen.has(link.url)) {
191
+ seen.add(link.url);
192
+ result.push(link);
193
+ }
194
+ }
195
+ return result;
151
196
  }
152
197
  function cleanUrl(url) {
153
198
  try {
@@ -385,11 +430,6 @@ function buildForwardNode(session, content, botName) {
385
430
  }
386
431
  }, messageContent);
387
432
  }
388
- const urlCache = new lru_cache_1.LRUCache({
389
- max: 500,
390
- ttl: 10 * 60 * 1000,
391
- updateAgeOnGet: false,
392
- });
393
433
  async function downloadVideoFile(videoUrl, tempDir, timeout, maxSizeMB) {
394
434
  if (!videoUrl)
395
435
  throw new Error('视频链接为空');
@@ -483,13 +523,13 @@ function apply(ctx, config) {
483
523
  zuiyou: 'https://api.bugpk.com/api/zuiyou',
484
524
  };
485
525
  const backupSupportedPlatforms = new Set(['douyin', 'xiaohongshu', 'instagram', 'jimeng']);
486
- function getDedicatedApiUrl(type) {
487
- if (config.customApis && Array.isArray(config.customApis)) {
488
- const found = config.customApis.find((item) => item.platform === type);
489
- if (found && found.apiUrl)
490
- return found.apiUrl;
526
+ function getPlatformConfig(type) {
527
+ const custom = config.customApis?.find((item) => item.platform === type);
528
+ if (custom && custom.apiUrl) {
529
+ return { apiUrl: custom.apiUrl, dedicatedFirst: custom.useDedicatedFirst ?? false };
491
530
  }
492
- return defaultDedicatedApis[type] || null;
531
+ const defaultUrl = defaultDedicatedApis[type] || null;
532
+ return { apiUrl: defaultUrl, dedicatedFirst: config.useDedicatedApiFirst ?? false };
493
533
  }
494
534
  async function fetchApi(url, type) {
495
535
  const cacheKey = url;
@@ -498,14 +538,13 @@ function apply(ctx, config) {
498
538
  debugLog('DEBUG', `使用缓存: ${url}`);
499
539
  return cached.data;
500
540
  }
501
- const dedicatedApiUrl = getDedicatedApiUrl(type);
541
+ const { apiUrl: dedicatedUrl, dedicatedFirst } = getPlatformConfig(type);
502
542
  const primaryApi = config.primaryApiUrl || 'https://api.bugpk.com/api/short_videos';
503
543
  const backupApi = config.backupApiUrl || 'https://api.bugpk.com/api/svparse';
504
544
  const backupAllowed = backupSupportedPlatforms.has(type);
505
545
  const apiList = [];
506
- if (config.useDedicatedApiFirst) {
507
- if (dedicatedApiUrl)
508
- apiList.push({ url: dedicatedApiUrl, label: `专属API(${type})` });
546
+ if (dedicatedFirst && dedicatedUrl) {
547
+ apiList.push({ url: dedicatedUrl, label: `专属API(${type})` });
509
548
  apiList.push({ url: primaryApi, label: '默认主API' });
510
549
  if (backupAllowed)
511
550
  apiList.push({ url: backupApi, label: '备用主API' });
@@ -514,8 +553,8 @@ function apply(ctx, config) {
514
553
  apiList.push({ url: primaryApi, label: '默认主API' });
515
554
  if (backupAllowed)
516
555
  apiList.push({ url: backupApi, label: '备用主API' });
517
- if (dedicatedApiUrl)
518
- apiList.push({ url: dedicatedApiUrl, label: `专属API(${type})` });
556
+ if (dedicatedUrl)
557
+ apiList.push({ url: dedicatedUrl, label: `专属API(${type})` });
519
558
  }
520
559
  let lastError = null;
521
560
  for (const api of apiList) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-video-parser-all",
3
3
  "description": "Koishi 全平台视频解析插件,支持抖音/快手/B站/微博/小红书/剪映/YouTube/TikTok等20+平台",
4
- "version": "1.0.8",
4
+ "version": "1.1.0",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [
package/readme.md CHANGED
@@ -56,8 +56,8 @@ This is a **multi-platform video/image parsing plugin** developed for the Koishi
56
56
  |--------|------|--------|------|
57
57
  | `primaryApiUrl` | string | `https://api.bugpk.com/api/short_videos` | 主 API 地址,解析时优先使用 |
58
58
  | `backupApiUrl` | string | `https://api.bugpk.com/api/svparse` | 备用主 API 地址,仅支持抖音、小红书、Instagram、即梦平台解析 |
59
- | `useDedicatedApiFirst` | boolean | false | 是否优先使用平台专属 API,失败后依次回退到主 API、备用主 API |
60
- | `customApis` | array | [] | 自定义平台专属 API 列表,每项需选择平台并填写 API 地址,可覆盖内置的默认专属 API |
59
+ | `useDedicatedApiFirst` | boolean | false | 全局默认值:是否优先使用平台专属 API,各平台可单独覆盖该行为 |
60
+ | `customApis` | array | [] | 自定义平台专属 API 列表。每项包含:`platform`(平台类型)、`apiUrl`(API 地址)、`useDedicatedFirst`(该平台是否优先使用此专属 API)。可覆盖内置默认专属 API,并独立控制专属 API 的优先策略 |
61
61
 
62
62
  ### 错误与重试设置
63
63
  | 配置项 | 类型 | 默认值 | 说明 |