koishi-plugin-video-parser-all 1.3.0 → 1.3.1

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
@@ -23,7 +23,7 @@ export declare const Config: Schema<{
23
23
  userAgent?: string | null | undefined;
24
24
  proxy?: ({
25
25
  enabled?: boolean | null | undefined;
26
- protocol?: string | null | undefined;
26
+ protocol?: "http" | "https" | null | undefined;
27
27
  host?: string | null | undefined;
28
28
  port?: number | null | undefined;
29
29
  auth?: ({
@@ -65,11 +65,12 @@ export declare const Config: Schema<{
65
65
  twitter?: boolean | null | undefined;
66
66
  instagram?: boolean | null | undefined;
67
67
  doubao?: boolean | null | undefined;
68
+ doubao_chat?: boolean | null | undefined;
68
69
  oasis?: boolean | null | undefined;
69
70
  wechat_channel?: boolean | null | undefined;
70
71
  } & import("cosmokit").Dict) | null | undefined;
71
72
  customApis?: ({
72
- platform?: "bilibili" | "douyin" | "kuaishou" | "xiaohongshu" | "weibo" | "xigua" | "youtube" | "tiktok" | "acfun" | "zhihu" | "weishi" | "huya" | "haokan" | "meipai" | "twitter" | "instagram" | "doubao" | "oasis" | "wechat_channel" | null | undefined;
73
+ platform?: "bilibili" | "douyin" | "kuaishou" | "xiaohongshu" | "weibo" | "xigua" | "youtube" | "tiktok" | "acfun" | "zhihu" | "weishi" | "huya" | "haokan" | "meipai" | "twitter" | "instagram" | "doubao" | "doubao_chat" | "oasis" | "wechat_channel" | null | undefined;
73
74
  apiUrl?: string | null | undefined;
74
75
  apiKey?: string | null | undefined;
75
76
  authHeaderType?: "Bearer" | "X-API-Key" | "Custom" | null | undefined;
@@ -106,7 +107,7 @@ export declare const Config: Schema<{
106
107
  userAgent: string;
107
108
  proxy: Schemastery.ObjectT<{
108
109
  enabled: Schema<boolean, boolean>;
109
- protocol: Schema<string, string>;
110
+ protocol: Schema<"http" | "https", "http" | "https">;
110
111
  host: Schema<string, string>;
111
112
  port: Schema<number, number>;
112
113
  auth: Schema<Schemastery.ObjectS<{
@@ -151,11 +152,12 @@ export declare const Config: Schema<{
151
152
  twitter: Schema<boolean, boolean>;
152
153
  instagram: Schema<boolean, boolean>;
153
154
  doubao: Schema<boolean, boolean>;
155
+ doubao_chat: Schema<boolean, boolean>;
154
156
  oasis: Schema<boolean, boolean>;
155
157
  wechat_channel: Schema<boolean, boolean>;
156
158
  }>;
157
159
  customApis: Schemastery.ObjectT<{
158
- platform: Schema<"bilibili" | "douyin" | "kuaishou" | "xiaohongshu" | "weibo" | "xigua" | "youtube" | "tiktok" | "acfun" | "zhihu" | "weishi" | "huya" | "haokan" | "meipai" | "twitter" | "instagram" | "doubao" | "oasis" | "wechat_channel", "bilibili" | "douyin" | "kuaishou" | "xiaohongshu" | "weibo" | "xigua" | "youtube" | "tiktok" | "acfun" | "zhihu" | "weishi" | "huya" | "haokan" | "meipai" | "twitter" | "instagram" | "doubao" | "oasis" | "wechat_channel">;
160
+ platform: Schema<"bilibili" | "douyin" | "kuaishou" | "xiaohongshu" | "weibo" | "xigua" | "youtube" | "tiktok" | "acfun" | "zhihu" | "weishi" | "huya" | "haokan" | "meipai" | "twitter" | "instagram" | "doubao" | "doubao_chat" | "oasis" | "wechat_channel", "bilibili" | "douyin" | "kuaishou" | "xiaohongshu" | "weibo" | "xigua" | "youtube" | "tiktok" | "acfun" | "zhihu" | "weishi" | "huya" | "haokan" | "meipai" | "twitter" | "instagram" | "doubao" | "doubao_chat" | "oasis" | "wechat_channel">;
159
161
  apiUrl: Schema<string, string>;
160
162
  apiKey: Schema<string, string>;
161
163
  authHeaderType: Schema<"Bearer" | "X-API-Key" | "Custom", "Bearer" | "X-API-Key" | "Custom">;
package/lib/index.js CHANGED
@@ -76,7 +76,7 @@ exports.Config = koishi_1.Schema.intersect([
76
76
  debug: koishi_1.Schema.boolean().default(false).description('开启调试模式,在控制台输出详细日志'),
77
77
  }).description('基础设置'),
78
78
  koishi_1.Schema.object({
79
- unifiedMessageFormat: koishi_1.Schema.string().role('textarea').default('标题:${标题}\n作者:${作者}\n简介:${简介}\n点赞:${点赞数}\n收藏:${收藏数}\n转发:${转发数}\n播放:${播放数}\n评论:${评论数}\n图片数量:${图片数量}').description('统一消息格式,可用变量:${标题} ${作者} ${简介} ${点赞数} ${收藏数} ${转发数} ${播放数} ${评论数} ${视频时长} ${发布时间} ${图片数量} ${作者ID}'),
79
+ unifiedMessageFormat: koishi_1.Schema.string().role('textarea').default('标题:${标题}\n作者:${作者}\n简介:${简介}\n点赞:${点赞数}\n收藏:${收藏数}\n转发:${转发数}\n播放:${播放数}\n评论:${评论数}\n图片数量:${图片数量}').description('统一消息格式,可用变量:${标题} ${作者} ${简介} ${点赞数} ${收藏数} ${转发数} ${播放数} ${评论数} ${视频时长} ${发布时间} ${图片数量} ${作者ID} ${音乐标题} ${音乐作者} ${音乐封面} ${音乐链接}'),
80
80
  }).description('消息格式设置'),
81
81
  koishi_1.Schema.object({
82
82
  showImageText: koishi_1.Schema.boolean().default(true).description('是否发送解析后的文字内容'),
@@ -95,7 +95,10 @@ exports.Config = koishi_1.Schema.intersect([
95
95
  userAgent: koishi_1.Schema.string().default('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36').description('API 请求 UA'),
96
96
  proxy: koishi_1.Schema.object({
97
97
  enabled: koishi_1.Schema.boolean().default(false).description('是否启用 HTTP/HTTPS 代理'),
98
- protocol: koishi_1.Schema.string().default('http').description('代理协议 (http 或 https)'),
98
+ protocol: koishi_1.Schema.union([
99
+ koishi_1.Schema.const('http').description('HTTP'),
100
+ koishi_1.Schema.const('https').description('HTTPS'),
101
+ ]).default('http').description('代理协议'),
99
102
  host: koishi_1.Schema.string().default('127.0.0.1').description('代理地址'),
100
103
  port: koishi_1.Schema.number().default(7890).description('代理端口'),
101
104
  auth: koishi_1.Schema.object({
@@ -141,6 +144,7 @@ exports.Config = koishi_1.Schema.intersect([
141
144
  twitter: koishi_1.Schema.boolean().default(false).description('Twitter/X - 优先使用专属 API'),
142
145
  instagram: koishi_1.Schema.boolean().default(false).description('Instagram - 优先使用专属 API'),
143
146
  doubao: koishi_1.Schema.boolean().default(false).description('豆包 - 优先使用专属 API'),
147
+ doubao_chat: koishi_1.Schema.boolean().default(false).description('豆包图片 - 优先使用专属 API'),
144
148
  oasis: koishi_1.Schema.boolean().default(false).description('绿洲 - 优先使用专属 API'),
145
149
  wechat_channel: koishi_1.Schema.boolean().default(false).description('视频号 - 优先使用专属 API'),
146
150
  }).description('各平台独立开关:是否优先使用专属 API'),
@@ -163,6 +167,7 @@ exports.Config = koishi_1.Schema.intersect([
163
167
  koishi_1.Schema.const('twitter').description('Twitter/X'),
164
168
  koishi_1.Schema.const('instagram').description('Instagram'),
165
169
  koishi_1.Schema.const('doubao').description('豆包'),
170
+ koishi_1.Schema.const('doubao_chat').description('豆包图片'),
166
171
  koishi_1.Schema.const('oasis').description('绿洲'),
167
172
  koishi_1.Schema.const('wechat_channel').description('视频号'),
168
173
  ]).description('选择平台'),
@@ -242,6 +247,7 @@ const LINK_RULES = [
242
247
  { pattern: /https?:\/\/x\.com\/\w+\/status\/\d{10,}/gi, type: 'twitter' },
243
248
  { pattern: /https?:\/\/(?:www\.)?instagram\.com\/p\/[0-9a-zA-Z_-]{10,}/gi, type: 'instagram' },
244
249
  { pattern: /https?:\/\/(?:www\.)?doubao\.com\/video\/\d{10,}/gi, type: 'doubao' },
250
+ { pattern: /https?:\/\/(?:www\.)?doubao\.com\/thread\/[0-9a-zA-Z_-]+/gi, type: 'doubao_chat' },
245
251
  { pattern: /https?:\/\/(?:www\.)?oasis\.weibo\.com\/v\/[0-9a-zA-Z_-]+/gi, type: 'oasis' },
246
252
  { pattern: /https?:\/\/channels\.weixin\.qq\.com\/[0-9a-zA-Z_-]+/gi, type: 'wechat_channel' },
247
253
  { pattern: /https?:\/\/weixin\.qq\.com\/sph\/[0-9a-zA-Z_-]+/gi, type: 'wechat_channel' },
@@ -365,6 +371,23 @@ function getNestedValue(obj, path) {
365
371
  }
366
372
  return current;
367
373
  }
374
+ function parseCount(val) {
375
+ if (val === undefined || val === null)
376
+ return 0;
377
+ if (typeof val === 'number')
378
+ return val;
379
+ const str = String(val).trim();
380
+ if (str.includes('万')) {
381
+ const num = parseFloat(str);
382
+ return isNaN(num) ? 0 : Math.round(num * 10000);
383
+ }
384
+ if (str.includes('亿')) {
385
+ const num = parseFloat(str);
386
+ return isNaN(num) ? 0 : Math.round(num * 100000000);
387
+ }
388
+ const num = parseInt(str, 10);
389
+ return isNaN(num) ? 0 : num;
390
+ }
368
391
  function parseApiResponse(raw, maxDescLen, fieldMapping) {
369
392
  debugLog('DEBUG', 'API raw response', raw);
370
393
  const data = raw?.data || {};
@@ -378,7 +401,7 @@ function parseApiResponse(raw, maxDescLen, fieldMapping) {
378
401
  return fallback();
379
402
  };
380
403
  let type = mapField('type', () => {
381
- let t = data.type || '';
404
+ let t = data.type || data.videoType || '';
382
405
  if (!t) {
383
406
  if (data.images?.length > 0 && !data.url)
384
407
  t = 'image';
@@ -391,16 +414,16 @@ function parseApiResponse(raw, maxDescLen, fieldMapping) {
391
414
  }
392
415
  return t;
393
416
  });
394
- const authorObj = mapField('author', () => data.author);
417
+ let authorObj = mapField('author', () => data.author || data.user);
395
418
  let author = '', uid = '', avatar = '';
396
419
  if (authorObj && typeof authorObj === 'object') {
397
420
  author = authorObj.name || authorObj.author || '';
398
- uid = String(authorObj.id || data.uid || '');
421
+ uid = String(authorObj.id || authorObj.userID || data.uid || data.userID || data.author_id || '');
399
422
  avatar = authorObj.avatar || data.avatar || '';
400
423
  }
401
424
  else {
402
425
  author = mapField('author', () => data.author || data.auther || '');
403
- uid = String(mapField('uid', () => data.uid || ''));
426
+ uid = String(mapField('uid', () => data.uid || data.userID || data.author_id || ''));
404
427
  avatar = mapField('avatar', () => data.avatar || '');
405
428
  }
406
429
  const title = mapField('title', () => data.title || '');
@@ -425,37 +448,53 @@ function parseApiResponse(raw, maxDescLen, fieldMapping) {
425
448
  }
426
449
  }
427
450
  }
451
+ if (!video && data.quality_urls && typeof data.quality_urls === 'object') {
452
+ const entries = Object.entries(data.quality_urls);
453
+ videos = entries.map(([label, url]) => ({ quality: label, url: String(url) }));
454
+ if (videos.length)
455
+ video = videos[0].url;
456
+ }
428
457
  if (!video)
429
458
  video = mapField('video', () => data.url || '');
430
459
  if (video && !video.startsWith('http'))
431
460
  video = 'https:' + video;
432
- const images = Array.isArray(data.images) ? data.images.filter((img) => img && typeof img === 'string').map((img) => img.startsWith('http') ? img : 'https:' + img) : [];
461
+ let images = [];
462
+ const directImages = mapField('images', () => data.images);
463
+ if (Array.isArray(directImages)) {
464
+ images = directImages.filter((img) => img && typeof img === 'string').map((img) => img.startsWith('http') ? img : 'https:' + img);
465
+ }
466
+ else if (Array.isArray(data.imgurl)) {
467
+ images = data.imgurl.filter((img) => img && typeof img === 'string').map((img) => img.startsWith('http') ? img : 'https:' + img);
468
+ }
433
469
  const live_photo = Array.isArray(data.live_photo) ? data.live_photo.filter((lp) => lp && lp.image).map((lp) => ({
434
470
  image: lp.image.startsWith('http') ? lp.image : 'https:' + lp.image,
435
471
  video: lp.video ? (lp.video.startsWith('http') ? lp.video : 'https:' + lp.video) : ''
436
472
  })) : [];
473
+ const musicCoverRaw = mapField('music_cover', () => data.music?.cover || data.music?.albumCover?.url || '');
474
+ const musicUrlRaw = mapField('music_url', () => data.music?.url || data.music?.playURL || '');
437
475
  const music = {
438
476
  title: mapField('music_title', () => data.music?.title || data.music?.name || ''),
439
477
  author: mapField('music_author', () => data.music?.author || data.music?.artist || ''),
440
- cover: mapField('music_cover', () => data.music?.cover || ''),
441
- url: mapField('music_url', () => data.music?.url || ''),
478
+ cover: musicCoverRaw ? (String(musicCoverRaw).startsWith('http') ? String(musicCoverRaw) : 'https:' + musicCoverRaw) : '',
479
+ url: musicUrlRaw ? (String(musicUrlRaw).startsWith('http') ? String(musicUrlRaw) : 'https:' + musicUrlRaw) : '',
442
480
  };
443
- const stats = { ...(data.statistics || {}), ...(extra.statistics || {}) };
444
- const like = Number(mapField('like', () => data.like ?? stats.digg_count ?? stats.like_count ?? stats.likes ?? 0));
445
- const comment = Number(mapField('comment', () => data.comment ?? stats.comment_count ?? stats.comments ?? stats.comment ?? 0));
446
- const collect = Number(mapField('collect', () => data.collect ?? stats.collect_count ?? stats.favorite_count ?? stats.favorites ?? 0));
447
- const share = Number(mapField('share', () => data.share ?? stats.share_count ?? stats.forward_count ?? stats.shares ?? 0));
448
- const play = Number(mapField('play', () => data.play ?? stats.play_count ?? stats.view_count ?? stats.plays ?? 0));
481
+ const like = parseCount(mapField('like', () => data.like ?? data.statistics?.digg_count ?? data.statistics?.like_count ?? data.statistics?.likes ?? extra.statistics?.digg_count ?? extra.statistics?.like_count ?? extra.statistics?.likes ?? data.attitudes_count ?? 0));
482
+ const comment = parseCount(mapField('comment', () => data.comment ?? data.statistics?.comment_count ?? data.statistics?.comments ?? extra.statistics?.comment_count ?? extra.statistics?.comments ?? data.comments_count ?? 0));
483
+ const collect = parseCount(mapField('collect', () => data.collect ?? data.statistics?.collect_count ?? data.statistics?.favorite_count ?? data.statistics?.favorites ?? extra.statistics?.collect_count ?? extra.statistics?.favorite_count ?? extra.statistics?.favorites ?? data.favorites_count ?? 0));
484
+ const share = parseCount(mapField('share', () => data.share ?? data.statistics?.share_count ?? data.statistics?.forward_count ?? data.statistics?.shares ?? extra.statistics?.share_count ?? extra.statistics?.forward_count ?? extra.statistics?.shares ?? data.reposts_count ?? 0));
485
+ const play = parseCount(mapField('play', () => data.play ?? data.statistics?.play_count ?? data.statistics?.view_count ?? data.statistics?.plays ?? extra.statistics?.play_count ?? extra.statistics?.view_count ?? extra.statistics?.plays ?? data.play_count ?? data.view_count ?? 0));
449
486
  let duration = 0;
450
- const durRaw = mapField('duration', () => data.duration);
451
- if (durRaw) {
452
- duration = typeof durRaw === 'string' ? parseInt(durRaw, 10) : Number(durRaw);
453
- if (duration > 1000000)
454
- duration = Math.floor(duration / 1000);
455
- }
456
- else if (extra.duration_ms) {
487
+ if (extra.duration_ms) {
457
488
  duration = Math.floor(Number(extra.duration_ms) / 1000);
458
489
  }
490
+ else {
491
+ const durRaw = mapField('duration', () => data.duration);
492
+ if (durRaw) {
493
+ duration = typeof durRaw === 'string' ? parseInt(durRaw, 10) : Number(durRaw);
494
+ if (duration > 3600)
495
+ duration = Math.floor(duration / 1000);
496
+ }
497
+ }
459
498
  let publishTime = 0;
460
499
  const timeRaw = mapField('publishTime', () => data.time);
461
500
  if (timeRaw) {
@@ -486,17 +525,32 @@ function generateFormattedText(p, format) {
486
525
  '作者ID': p.uid,
487
526
  '封面': p.cover,
488
527
  '视频链接': p.video,
528
+ '音乐标题': p.music.title || '',
529
+ '音乐作者': p.music.author || '',
530
+ '音乐封面': p.music.cover || '',
531
+ '音乐链接': p.music.url || '',
489
532
  };
490
533
  const lines = format.split('\n');
491
534
  const resultLines = [];
492
535
  for (const line of lines) {
536
+ const varMatches = line.match(formatVarRegex);
537
+ if (varMatches && varMatches.length > 0) {
538
+ let allEmptyOrZero = true;
539
+ for (const match of varMatches) {
540
+ const varName = match.slice(2, -1);
541
+ const val = vars[varName];
542
+ if (val && val !== '0') {
543
+ allEmptyOrZero = false;
544
+ break;
545
+ }
546
+ }
547
+ if (allEmptyOrZero)
548
+ continue;
549
+ }
493
550
  let newLine = line;
494
551
  for (const [key, val] of Object.entries(vars)) {
495
552
  newLine = newLine.replace(new RegExp(`\\$\\{${key}\\}`, 'g'), val);
496
553
  }
497
- const stripped = newLine.replace(/[\s::,,。.、;;!!??【】\[\]「」『』()()《》""''""·—\-_/\\|@#$%^&*+=~`]/g, '').trim();
498
- if (stripped.length === 0)
499
- continue;
500
554
  resultLines.push(newLine);
501
555
  }
502
556
  return resultLines.join('\n').trim();
@@ -570,6 +624,7 @@ function apply(ctx, config) {
570
624
  bilibili: 'https://api.bugpk.com/api/bilibili',
571
625
  douyin: 'https://api.bugpk.com/api/douyin',
572
626
  doubao: 'https://api.bugpk.com/api/dbvideos',
627
+ doubao_chat: 'https://api.bugpk.com/api/dbduihua',
573
628
  kuaishou: 'https://api.bugpk.com/api/kuaishou',
574
629
  xiaohongshu: 'https://api.bugpk.com/api/xhs',
575
630
  jimeng: 'https://api.bugpk.com/api/jimengai',
@@ -735,7 +790,7 @@ function apply(ctx, config) {
735
790
  for (const candidate of candidates) {
736
791
  try {
737
792
  const info = await fetchApi(candidate, type, fieldMapping);
738
- if (info.video || info.images.length > 0)
793
+ if (info.video || info.images.length > 0 || info.live_photo.length > 0)
739
794
  return { success: true, data: info };
740
795
  debugLog('WARN', `解析成功但无内容: ${candidate}`);
741
796
  }
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.3.0",
4
+ "version": "1.3.1",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [
package/readme.md CHANGED
@@ -31,15 +31,7 @@ This is a **multi-platform video/image parsing plugin** developed for the Koishi
31
31
  ### 统一消息格式 (Unified Message Format)
32
32
  | 配置项 | 类型 | 默认值 | 说明 |
33
33
  |--------|------|--------|------|
34
- | `unifiedMessageFormat` | string | `标题:${标题}
35
- 作者:${作者}
36
- 简介:${简介}
37
- 点赞:${点赞数}
38
- 收藏:${收藏数}
39
- 转发:${转发数}
40
- 播放:${播放数}
41
- 评论:${评论数}
42
- 图片数量:${图片数量}` | 自定义解析结果的输出格式,支持变量替换。某行所有变量均为空(或为"0")时自动隐藏该行 |
34
+ | `unifiedMessageFormat` | string | `标题:${标题}\n作者:${作者}\n简介:${简介}\n点赞:${点赞数}\n收藏:${收藏数}\n转发:${转发数}\n播放:${播放数}\n评论:${评论数}\n图片数量:${图片数量}` | 自定义解析结果的输出格式,支持变量替换。某行所有变量均为空(或为"0")时自动隐藏该行 |
43
35
 
44
36
  ### 内容显示设置 (Content Display Settings)
45
37
  | 配置项 | 类型 | 默认值 | 说明 |
@@ -60,7 +52,7 @@ This is a **multi-platform video/image parsing plugin** developed for the Koishi
60
52
  | `timeout` | number | 180000 | API 请求超时时间(毫秒) |
61
53
  | `videoSendTimeout` | number | 60000 | 视频消息发送超时时间(毫秒,0 为不限制) |
62
54
  | `userAgent` | string | `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36` | API 请求使用的 User-Agent |
63
- | `proxy` | object | `{ enabled: false, protocol: "http", host: "127.0.0.1", port: 7890, auth: { username: "", password: "" } }` | HTTP/HTTPS 代理设置。`enabled` 开关(默认关闭),`protocol` 支持 `http` 或 `https`。需开启 `enabled` 并填写 `host` 后生效 |
55
+ | `proxy` | object | `{ enabled: false, protocol: "http", host: "127.0.0.1", port: 7890, auth: { username: "", password: "" } }` | HTTP/HTTPS 代理设置。`enabled` 开关(默认关闭),`protocol` 下拉选择 `http` 或 `https`。需开启 `enabled` 并填写 `host` 后生效 |
64
56
  | `customHeaders` | array | [] | 自定义请求头,会附加到所有 API 请求中。每项包含 `name`(头名称)和 `value`(头值) |
65
57
 
66
58
  ### API 选择与回退设置 (API Selection & Fallback)
@@ -68,7 +60,7 @@ This is a **multi-platform video/image parsing plugin** developed for the Koishi
68
60
  |--------|------|--------|------|
69
61
  | `primaryApiUrl` | string | `https://api.bugpk.com/api/short_videos` | 主 API 地址,解析时优先使用 |
70
62
  | `backupApiUrl` | string | `https://api.bugpk.com/api/svparse` | 备用主 API 地址,仅支持抖音、小红书、Instagram、即梦平台解析 |
71
- | `platformDedicatedFirst` | object | 各平台均为 `false` | 各平台独立开关:是否优先使用平台专属 API。对象键为平台标识(英文),值为布尔值。支持的键:`bilibili`、`douyin`、`kuaishou`、`xiaohongshu`、`weibo`、`xigua`、`youtube`、`tiktok`、`acfun`、`zhihu`、`weishi`、`huya`、`haokan`、`meipai`、`twitter`、`instagram`、`doubao`、`oasis`、`wechat_channel` |
63
+ | `platformDedicatedFirst` | object | 各平台均为 `false` | 各平台独立开关:是否优先使用平台专属 API。对象键为平台标识(英文),值为布尔值。支持的键:`bilibili`、`douyin`、`kuaishou`、`xiaohongshu`、`weibo`、`xigua`、`youtube`、`tiktok`、`acfun`、`zhihu`、`weishi`、`huya`、`haokan`、`meipai`、`twitter`、`instagram`、`doubao`、`doubao_chat`、`oasis`、`wechat_channel` |
72
64
  | `customApis` | array | [] | 自定义平台专属 API 列表。每项包含:`platform`(平台类型)、`apiUrl`(API 地址)、`apiKey`(API Key,可选)、`authHeaderType`(认证头类型,可选:`Bearer` / `X-API-Key` / `Custom`)、`customHeaderName`(自定义 Header 名称,仅当 `authHeaderType` 为 `Custom` 时有效)、`fieldMapping`(字段映射 JSON 字符串,用于适配非标准 API 响应,支持点号路径) |
73
65
  | `globalFieldMapping` | string | 预设完整字段映射JSON(见下方示例) | 全局字段映射 JSON,优先级低于专属 API 映射。用于统一适配所有平台的 API 响应格式,默认已包含常用路径示例 |
74
66
 
@@ -117,6 +109,10 @@ This is a **multi-platform video/image parsing plugin** developed for the Koishi
117
109
  | `${图片数量}` | 图集/实况图片数量 | 图集/实况 |
118
110
  | `${作者ID}` | 作者唯一标识ID | 部分平台 |
119
111
  | `${视频链接}` | 视频原始链接 | 视频 |
112
+ | `${音乐标题}` | 音乐标题 | 部分平台 |
113
+ | `${音乐作者}` | 音乐作者 | 部分平台 |
114
+ | `${音乐封面}` | 音乐封面图片地址 | 部分平台 |
115
+ | `${音乐链接}` | 音乐原始链接 | 部分平台 |
120
116
 
121
117
  > 注:部分变量可能因平台API返回数据不同而显示为空,某行所有变量为空(或为"0")时该行会自动隐藏。
122
118
 
@@ -142,7 +138,8 @@ This is a **multi-platform video/image parsing plugin** developed for the Koishi
142
138
  | 全民直播 | quanmin (quanmin.tv) | 直播 |
143
139
  | Twitter / X | twitter, x.com | 视频、图文 |
144
140
  | Instagram | instagram, instagram.com | 图文、Reels |
145
- | 豆包 | doubao (doubao.com) | 视频 |
141
+ | 豆包 | doubao (doubao.com/video) | 视频 |
142
+ | 豆包图片 | doubao (doubao.com/thread) | 图片 |
146
143
  | 皮皮搞笑 | pipigx, h5.pipigx.com | 短视频 |
147
144
  | 皮皮虾 | pipixia, h5.pipix.com | 短视频 |
148
145
  | 最右 | zuiyou, xiaochuankeji.cn | 短视频 |