koishi-plugin-video-parser-all 1.4.7 → 1.4.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 CHANGED
@@ -1,12 +1,4 @@
1
1
  import { Context, Schema } from 'koishi';
2
- declare module 'koishi' {
3
- interface Context {
4
- downloads?: {
5
- download(url: string, dest: string, options?: Record<string, unknown>): Promise<string>;
6
- };
7
- aria2?: any;
8
- }
9
- }
10
2
  export declare const name = "video-parser-all";
11
3
  export declare const Config: Schema<{
12
4
  enable?: boolean | null | undefined;
@@ -45,10 +37,17 @@ export declare const Config: Schema<{
45
37
  } & {
46
38
  showImageText?: boolean | null | undefined;
47
39
  showCoverImage?: boolean | null | undefined;
40
+ showCoverFile?: boolean | null | undefined;
41
+ showImageFileNew?: boolean | null | undefined;
42
+ showAuthorAvatar?: boolean | null | undefined;
43
+ showAuthorAvatarFile?: boolean | null | undefined;
44
+ showAuthorAvatarText?: boolean | null | undefined;
45
+ authorAvatarText?: string | null | undefined;
48
46
  showMusicCover?: boolean | null | undefined;
49
- showImageFile?: boolean | null | undefined;
50
47
  showVideoFile?: boolean | null | undefined;
51
- forceDownloadImage?: boolean | null | undefined;
48
+ forceDownloadCover?: boolean | null | undefined;
49
+ forceDownloadImageNew?: boolean | null | undefined;
50
+ forceDownloadAuthorAvatar?: boolean | null | undefined;
52
51
  forceDownloadVideo?: boolean | null | undefined;
53
52
  } & {
54
53
  showMusicVoice?: boolean | null | undefined;
@@ -190,10 +189,17 @@ export declare const Config: Schema<{
190
189
  } & {
191
190
  showImageText: boolean;
192
191
  showCoverImage: boolean;
192
+ showCoverFile: boolean;
193
+ showImageFileNew: boolean;
194
+ showAuthorAvatar: boolean;
195
+ showAuthorAvatarFile: boolean;
196
+ showAuthorAvatarText: boolean;
197
+ authorAvatarText: string;
193
198
  showMusicCover: boolean;
194
- showImageFile: boolean;
195
199
  showVideoFile: boolean;
196
- forceDownloadImage: boolean;
200
+ forceDownloadCover: boolean;
201
+ forceDownloadImageNew: boolean;
202
+ forceDownloadAuthorAvatar: boolean;
197
203
  forceDownloadVideo: boolean;
198
204
  } & {
199
205
  showMusicVoice: boolean;
@@ -318,6 +324,3 @@ export declare const Config: Schema<{
318
324
  parseErrorItemFormat: string;
319
325
  }>;
320
326
  export declare function apply(ctx: Context, config: any): void;
321
- export declare namespace apply {
322
- var inject: string[];
323
- }
package/lib/index.js CHANGED
@@ -103,15 +103,22 @@ exports.Config = koishi_1.Schema.intersect([
103
103
  }).description('各平台解析开关'),
104
104
  }).description('基本设置'),
105
105
  koishi_1.Schema.object({
106
- unifiedMessageFormat: koishi_1.Schema.string().role('textarea').default('标题:${标题}\n作者:${作者}\n简介:${简介}\n音乐标题:${音乐标题}\n音乐作者:${音乐作者}\n点赞:${点赞数}\n收藏:${收藏数}\n转发:${转发数}\n播放:${播放数}\n评论:${评论数}\n图片数量:${图片数量}').description('文字格式,支持变量,空行自动隐藏'),
106
+ unifiedMessageFormat: koishi_1.Schema.string().role('textarea').default('标题:${标题}\n作者:${作者}\n简介:${简介}\n音乐标题:${音乐标题}\n音乐作者:${音乐作者}\n点赞:${点赞数}\n收藏:${收藏数}\n转发:${转发数}\n播放:${播放数}\n评论:${评论数}\n图片数量:${图片数量}').description('文字格式,支持变量:${标题} ${作者} ${author-name} ${简介} ${视频时长} ${点赞数} ${收藏数} ${转发数} ${播放数} ${评论数} ${发布时间} ${图片数量} ${作者ID} ${author-id} ${音乐标题} ${music-title} ${音乐作者} ${music-author},空行自动隐藏'),
107
107
  }).description('消息格式'),
108
108
  koishi_1.Schema.object({
109
109
  showImageText: koishi_1.Schema.boolean().default(true).description('发送文字内容'),
110
110
  showCoverImage: koishi_1.Schema.boolean().default(true).description('发送封面图片'),
111
+ showCoverFile: koishi_1.Schema.boolean().default(true).description('封面是否以图片形式发送(关闭则只发送链接)'),
112
+ showImageFileNew: koishi_1.Schema.boolean().default(true).description('图片是否以图片形式发送(关闭则只发送链接)'),
113
+ showAuthorAvatar: koishi_1.Schema.boolean().default(true).description('发送作者头像图片'),
114
+ showAuthorAvatarFile: koishi_1.Schema.boolean().default(true).description('作者头像图片是否以图片形式发送(关闭则只发送链接)'),
115
+ showAuthorAvatarText: koishi_1.Schema.boolean().default(true).description('发送作者头像前显示文字提示'),
116
+ authorAvatarText: koishi_1.Schema.string().default('作者头像:').description('作者头像前显示的文字'),
111
117
  showMusicCover: koishi_1.Schema.boolean().default(true).description('发送音乐封面图片'),
112
- showImageFile: koishi_1.Schema.boolean().default(true).description('封面/图片是否以图片形式发送(关闭则只发送链接)'),
113
118
  showVideoFile: koishi_1.Schema.boolean().default(true).description('视频是否以视频形式发送(关闭则只发送链接)'),
114
- forceDownloadImage: koishi_1.Schema.boolean().default(false).description('强制下载封面/图片'),
119
+ forceDownloadCover: koishi_1.Schema.boolean().default(false).description('强制下载封面'),
120
+ forceDownloadImageNew: koishi_1.Schema.boolean().default(false).description('强制下载图片'),
121
+ forceDownloadAuthorAvatar: koishi_1.Schema.boolean().default(false).description('强制下载作者头像'),
115
122
  forceDownloadVideo: koishi_1.Schema.boolean().default(false).description('强制下载视频'),
116
123
  }).description('媒体发送'),
117
124
  koishi_1.Schema.object({
@@ -543,8 +550,8 @@ function parseApiResponse(raw, maxDescLen, fieldMapping) {
543
550
  uid = String(mapField('uid', () => data.uid || data.userID || data.author_id || ''));
544
551
  avatar = mapField('avatar', () => data.avatar || '');
545
552
  }
546
- const title = mapField('title', () => data.title || '');
547
- const desc = mapField('desc', () => data.desc || data.description || '').slice(0, maxDescLen).trim();
553
+ let title = mapField('title', () => data.title || '');
554
+ let desc = mapField('desc', () => data.desc || data.description || '').slice(0, maxDescLen).trim();
548
555
  const coverRaw = mapField('cover', () => data.cover || '');
549
556
  const cover = coverRaw ? (String(coverRaw).startsWith('http') ? String(coverRaw) : 'https:' + coverRaw) : '';
550
557
  let video = '';
@@ -620,7 +627,19 @@ function parseApiResponse(raw, maxDescLen, fieldMapping) {
620
627
  else if (extra.create_time) {
621
628
  publishTime = Number(extra.create_time) * 1000;
622
629
  }
623
- return { type, title, desc, author, uid, avatar, cover, video, videos, images, live_photo, music, like, comment, collect, share, play, duration, publishTime };
630
+ const author_followers = parseCount(mapField('author_followers', () => extra.author_extra?.follower_count ?? data.author_extra?.follower_count ?? 0));
631
+ const author_signature = String(mapField('author_signature', () => extra.author_extra?.signature ?? data.author_extra?.signature ?? ''));
632
+ const admire = parseCount(mapField('admire', () => extra.statistics?.admire_count ?? data.statistics?.admire_count ?? 0));
633
+ title = title.replace(/\[话题\]/g, '');
634
+ desc = desc.replace(/\[话题\]/g, '');
635
+ if (title && desc && title.trim() === desc.trim()) {
636
+ desc = '';
637
+ }
638
+ if (title.trim().startsWith('#'))
639
+ title = '';
640
+ if (desc.trim().startsWith('#'))
641
+ desc = '';
642
+ return { type, title, desc, author, uid, avatar, cover, video, videos, images, live_photo, music, like, comment, collect, share, play, duration, publishTime, author_followers, author_signature, admire };
624
643
  }
625
644
  const formatVarRegex = /\$\{([^}]+)\}/g;
626
645
  function generateFormattedText(p, format) {
@@ -628,6 +647,7 @@ function generateFormattedText(p, format) {
628
647
  const vars = {
629
648
  '标题': p.title,
630
649
  '作者': p.author,
650
+ 'author-name': p.author,
631
651
  '简介': p.desc,
632
652
  '视频时长': p.duration > 0 ? formatDuration(p.duration) : '',
633
653
  '点赞数': String(p.like),
@@ -638,10 +658,11 @@ function generateFormattedText(p, format) {
638
658
  '发布时间': p.publishTime ? formatPublishTime(p.publishTime) : '',
639
659
  '图片数量': String(imageCount),
640
660
  '作者ID': p.uid,
641
- '视频链接': p.video,
661
+ 'author-id': p.uid,
642
662
  '音乐标题': p.music.title || '',
663
+ 'music-title': p.music.title || '',
643
664
  '音乐作者': p.music.author || '',
644
- '音乐封面': p.music.cover || '',
665
+ 'music-author': p.music.author || '',
645
666
  };
646
667
  const varReplacements = Object.entries(vars).map(([key, val]) => ({
647
668
  regex: new RegExp(`\\$\\{${key}\\}`, 'g'),
@@ -736,9 +757,14 @@ function apply(ctx, config) {
736
757
  const mediaDownloadTimeout = config.mediaDownloadTimeout ?? 120000;
737
758
  const maxMediaSize = config.maxMediaSize ?? 0;
738
759
  const downloadEngine = config.downloadEngine || 'internal';
739
- if (downloadEngine === 'aria2' && !ctx.aria2) {
760
+ const aria2Service = ctx.get('aria2');
761
+ const downloadsService = ctx.get('downloads');
762
+ if (downloadEngine === 'aria2' && !aria2Service) {
740
763
  logger.warn('选择了 aria2 下载引擎,但未检测到 koishi-plugin-aria2-plus 服务,将回退到内置下载');
741
764
  }
765
+ if (downloadEngine === 'downloads' && !downloadsService) {
766
+ logger.warn('选择了 downloads 下载引擎,但未检测到 koishi-plugin-downloads 服务,将回退到内置下载');
767
+ }
742
768
  function getPlatformConfig(type) {
743
769
  if (type.startsWith('custom_')) {
744
770
  const name = type.slice(7);
@@ -806,7 +832,7 @@ function apply(ctx, config) {
806
832
  return {};
807
833
  }
808
834
  const extRegexCache = {};
809
- async function downloadFile(url, timeout, maxSize, filePrefix, fileExts) {
835
+ async function downloadFile(url, timeout, maxSize, filePrefix, fileExts, onProgress) {
810
836
  if (!url)
811
837
  throw new Error('链接为空');
812
838
  await promises_1.default.mkdir(cacheDir, { recursive: true });
@@ -816,26 +842,24 @@ function apply(ctx, config) {
816
842
  }) || fileExts[0];
817
843
  const fileName = `${filePrefix}_${Date.now()}_${(0, crypto_1.randomBytes)(4).toString('hex')}.${ext}`;
818
844
  const filePath = path_1.default.resolve(cacheDir, fileName);
819
- if (downloadEngine === 'downloads' && ctx.downloads) {
820
- try {
821
- const dest = await ctx.downloads.download(url, path_1.default.join(cacheDir, fileName), {
822
- headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' },
823
- timeout
824
- });
825
- const stat = await promises_1.default.stat(dest);
826
- if (maxSize > 0 && stat.size > maxSize * 1024 * 1024) {
827
- await promises_1.default.unlink(dest).catch(() => { });
828
- throw new Error(`文件过大(${Math.round(stat.size / 1024 / 1024)}MB),超过限制(${maxSize}MB)`);
829
- }
830
- return dest;
831
- }
832
- catch (e) {
833
- debugLog('ERROR', `downloads 服务下载失败,回退内置下载: ${getErrorMessage(e)}`);
845
+ if (downloadEngine === 'downloads' && downloadsService) {
846
+ logger.info(`开始下载: ${url}`);
847
+ const dest = await downloadsService.download(url, path_1.default.join(cacheDir, fileName), {
848
+ headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' },
849
+ timeout
850
+ });
851
+ const stat = await promises_1.default.stat(dest);
852
+ const sizeMB = (stat.size / (1024 * 1024)).toFixed(2);
853
+ logger.info(`下载完成: ${dest} (${sizeMB} MB)`);
854
+ if (maxSize > 0 && stat.size > maxSize * 1024 * 1024) {
855
+ await promises_1.default.unlink(dest).catch(() => { });
856
+ throw new Error(`文件过大(${Math.round(stat.size / 1024 / 1024)}MB),超过限制(${maxSize}MB)`);
834
857
  }
858
+ return dest;
835
859
  }
836
- else if (downloadEngine === 'aria2' && ctx.aria2) {
860
+ if (downloadEngine === 'aria2' && aria2Service) {
837
861
  try {
838
- const gid = await ctx.aria2.addUri([url], {
862
+ const gid = await aria2Service.addUri([url], {
839
863
  dir: cacheDir,
840
864
  out: fileName,
841
865
  split: 4,
@@ -848,20 +872,31 @@ function apply(ctx, config) {
848
872
  header: [`User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36`, `Referer: https://www.baidu.com/`]
849
873
  });
850
874
  let completed = false;
851
- const ariaStartTime = Date.now();
875
+ const startTime = Date.now();
876
+ let lastProgressTime = 0;
852
877
  while (!completed) {
853
- if (Date.now() - ariaStartTime > timeout) {
854
- await ctx.aria2.remove(gid).catch(() => { });
878
+ if (Date.now() - startTime > timeout) {
879
+ await aria2Service.remove(gid).catch(() => { });
855
880
  throw new Error('aria2下载超时');
856
881
  }
857
- const status = await ctx.aria2.tellStatus(gid);
882
+ const status = await aria2Service.tellStatus(gid);
858
883
  if (status.status === 'complete') {
859
884
  completed = true;
885
+ if (onProgress)
886
+ onProgress(100, '');
860
887
  }
861
888
  else if (status.status === 'error' || status.status === 'removed') {
862
889
  throw new Error('aria2下载失败');
863
890
  }
864
891
  else {
892
+ if (onProgress && Date.now() - lastProgressTime > 500) {
893
+ lastProgressTime = Date.now();
894
+ const total = parseInt(status.totalLength) || 0;
895
+ const completed = parseInt(status.completedLength) || 0;
896
+ const percent = total > 0 ? Math.round((completed / total) * 100) : 0;
897
+ const speed = formatSpeed(parseInt(status.downloadSpeed) || 0);
898
+ onProgress(percent, speed);
899
+ }
865
900
  await delay(1000);
866
901
  }
867
902
  }
@@ -901,8 +936,20 @@ function apply(ctx, config) {
901
936
  await promises_1.default.unlink(filePath).catch(() => { });
902
937
  throw new Error(`文件过大(${Math.round(contentLength / 1024 / 1024)}MB),超过限制(${maxSize}MB)`);
903
938
  }
939
+ let downloaded = 0;
940
+ let lastProgressTime = 0;
941
+ response.data.on('data', (chunk) => {
942
+ downloaded += chunk.length;
943
+ if (onProgress && contentLength > 0 && Date.now() - lastProgressTime > 500) {
944
+ lastProgressTime = Date.now();
945
+ const percent = Math.round((downloaded / contentLength) * 100);
946
+ onProgress(percent, '');
947
+ }
948
+ });
904
949
  try {
905
950
  await (0, promises_2.pipeline)(response.data, writer);
951
+ if (onProgress)
952
+ onProgress(100, '');
906
953
  return filePath;
907
954
  }
908
955
  catch (e) {
@@ -910,6 +957,18 @@ function apply(ctx, config) {
910
957
  throw new Error(`写入文件失败: ${getErrorMessage(e)}`);
911
958
  }
912
959
  }
960
+ function formatSpeed(bytesPerSec) {
961
+ if (bytesPerSec <= 0)
962
+ return '';
963
+ const units = ['B/s', 'KB/s', 'MB/s', 'GB/s'];
964
+ let i = 0;
965
+ let speed = bytesPerSec;
966
+ while (speed >= 1024 && i < units.length - 1) {
967
+ speed /= 1024;
968
+ i++;
969
+ }
970
+ return `${speed.toFixed(1)}${units[i]}`;
971
+ }
913
972
  async function sendMedia(session, url, type, forceDownload, showFile) {
914
973
  if (!url)
915
974
  return;
@@ -923,9 +982,13 @@ function apply(ctx, config) {
923
982
  };
924
983
  const prefixMap = { image: 'img', video: 'video', audio: 'music' };
925
984
  const sendFunc = type === 'audio' ? koishi_1.h.audio : type === 'video' ? koishi_1.h.video : koishi_1.h.image;
985
+ const onProgress = forceDownload ? (percent, speed) => {
986
+ const text = `下载进度: ${percent}%` + (speed ? ` (${speed})` : '');
987
+ logger.info(text);
988
+ } : undefined;
926
989
  if (forceDownload) {
927
990
  try {
928
- const localPath = await downloadFile(url, mediaDownloadTimeout, maxMediaSize, prefixMap[type], extMap[type]);
991
+ const localPath = await downloadFile(url, mediaDownloadTimeout, maxMediaSize, prefixMap[type], extMap[type], onProgress);
929
992
  try {
930
993
  await sendWithTimeout(session, sendFunc(`file://${localPath}`));
931
994
  }
@@ -1033,7 +1096,12 @@ function apply(ctx, config) {
1033
1096
  const text = item.text;
1034
1097
  if (text && config.showImageText)
1035
1098
  forwardMessages.push(buildForwardNode(session, text, botName));
1036
- if (p.cover && config.showCoverImage && p.type !== 'live_photo' && !(p.type === 'live' && (p.live_photo?.length || p.images?.length))) {
1099
+ if (config.showAuthorAvatar && p.avatar) {
1100
+ if (config.showAuthorAvatarText)
1101
+ forwardMessages.push(buildForwardNode(session, config.authorAvatarText || '作者头像:', botName));
1102
+ forwardMessages.push(buildForwardNode(session, koishi_1.h.image(p.avatar), botName));
1103
+ }
1104
+ if (p.cover && config.showCoverImage && p.type !== 'live_photo' && p.type !== 'image' && !(p.type === 'live' && (p.live_photo?.length || p.images?.length))) {
1037
1105
  forwardMessages.push(buildForwardNode(session, koishi_1.h.image(p.cover), botName));
1038
1106
  }
1039
1107
  if (config.showMusicCover && p.music.cover) {
@@ -1071,12 +1139,18 @@ function apply(ctx, config) {
1071
1139
  await sendWithTimeout(session, text);
1072
1140
  await delay(300);
1073
1141
  }
1074
- if (p.cover && config.showCoverImage && p.type !== 'live_photo' && !(p.type === 'live' && (p.live_photo?.length || p.images?.length))) {
1075
- await sendMedia(session, p.cover, 'image', config.forceDownloadImage, config.showImageFile).catch(() => { });
1142
+ if (config.showAuthorAvatar && p.avatar) {
1143
+ if (config.showAuthorAvatarText)
1144
+ await sendWithTimeout(session, config.authorAvatarText || '作者头像:');
1145
+ await sendMedia(session, p.avatar, 'image', config.forceDownloadAuthorAvatar, config.showAuthorAvatarFile).catch(() => { });
1146
+ await delay(300);
1147
+ }
1148
+ if (p.cover && config.showCoverImage && p.type !== 'live_photo' && p.type !== 'image' && !(p.type === 'live' && (p.live_photo?.length || p.images?.length))) {
1149
+ await sendMedia(session, p.cover, 'image', config.forceDownloadCover, config.showCoverFile).catch(() => { });
1076
1150
  await delay(300);
1077
1151
  }
1078
1152
  if (config.showMusicCover && p.music.cover) {
1079
- await sendMedia(session, p.music.cover, 'image', config.forceDownloadImage, config.showImageFile).catch(() => { });
1153
+ await sendMedia(session, p.music.cover, 'image', false, true).catch(() => { });
1080
1154
  await delay(300);
1081
1155
  }
1082
1156
  if (p.video && (p.type === 'video' || (p.type === 'live' && !p.live_photo?.length && !p.images?.length))) {
@@ -1086,7 +1160,7 @@ function apply(ctx, config) {
1086
1160
  if (p.type === 'image' || p.type === 'live_photo' || (p.type === 'live' && (p.live_photo?.length || p.images?.length))) {
1087
1161
  const imageUrls = p.images?.length ? p.images : (p.live_photo?.map(lp => lp.image) ?? []);
1088
1162
  for (const imgUrl of imageUrls) {
1089
- await sendMedia(session, imgUrl, 'image', config.forceDownloadImage, config.showImageFile).catch(() => { });
1163
+ await sendMedia(session, imgUrl, 'image', config.forceDownloadImageNew, config.showImageFileNew).catch(() => { });
1090
1164
  await delay(200);
1091
1165
  }
1092
1166
  }
@@ -1342,4 +1416,3 @@ function apply(ctx, config) {
1342
1416
  });
1343
1417
  debugLog('INFO', '插件初始化完成');
1344
1418
  }
1345
- apply.inject = ['aria2', 'downloads'];
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.4.7",
4
+ "version": "1.4.9",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [
package/readme.md CHANGED
@@ -39,11 +39,20 @@ This is a **multi-platform video/image parsing plugin** developed for the Koishi
39
39
  |--------|------|--------|------|
40
40
  | `showImageText` | boolean | true | 发送文字内容 |
41
41
  | `showCoverImage` | boolean | true | 发送封面图片 |
42
- | `showMusicCover` | boolean | true | 发送音乐封面 |
43
- | `showImageFile` | boolean | true | 封面/图片是否以图片形式发送(关闭则只发送链接) |
42
+ | `showCoverFile` | boolean | true | 封面是否以图片形式发送(关闭则只发送链接) |
43
+ | `showImageFileNew` | boolean | true | 图片是否以图片形式发送(关闭则只发送链接) |
44
+ | `showAuthorAvatar` | boolean | true | 发送作者头像图片 |
45
+ | `showAuthorAvatarFile` | boolean | true | 作者头像图片是否以图片形式发送(关闭则只发送链接) |
46
+ | `showMusicCover` | boolean | true | 发送音乐封面图片 |
44
47
  | `showVideoFile` | boolean | true | 视频是否以视频形式发送(关闭则只发送链接) |
45
- | `forceDownloadImage` | boolean | false | 强制下载封面/图片 |
48
+ | `forceDownloadCover` | boolean | false | 强制下载封面 |
49
+ | `forceDownloadImageNew` | boolean | false | 强制下载图片 |
50
+ | `forceDownloadAuthorAvatar` | boolean | false | 强制下载作者头像 |
46
51
  | `forceDownloadVideo` | boolean | false | 强制下载视频 |
52
+ | `forceDownloadAuthorAvatar` | boolean | false | 强制下载作者头像 |
53
+ | `forceDownloadVideo` | boolean | false | 强制下载视频 |
54
+ | `showAuthorAvatarText` | boolean | true | 发送作者头像前显示文字提示 |
55
+ | `authorAvatarText` | string | 作者头像: | 作者头像前显示的文字 |
47
56
 
48
57
  ### 音乐语音(需 silk 和 ffmpeg)
49
58
  | 配置项 | 类型 | 默认值 | 说明 |
@@ -109,7 +118,7 @@ This is a **multi-platform video/image parsing plugin** developed for the Koishi
109
118
  | 变量名 | 说明 |
110
119
  |--------|------|
111
120
  | `${标题}` | 视频/图集标题 |
112
- | `${作者}` | 作者名称 |
121
+ | `${作者}` 或 `${author-name}` | 作者名称 |
113
122
  | `${简介}` | 内容简介 |
114
123
  | `${视频时长}` | 视频时长(时:分:秒) |
115
124
  | `${点赞数}` | 点赞数量 |
@@ -119,10 +128,9 @@ This is a **multi-platform video/image parsing plugin** developed for the Koishi
119
128
  | `${评论数}` | 评论数量 |
120
129
  | `${发布时间}` | 发布时间(格式化) |
121
130
  | `${图片数量}` | 图集/实况图片数量 |
122
- | `${作者ID}` | 作者唯一标识ID |
123
- | `${视频链接}` | 视频原始链接 |
124
- | `${音乐标题}` | 音乐标题 |
125
- | `${音乐作者}` | 音乐作者 |
131
+ | `${作者ID}` 或 `${author-id}` | 作者唯一标识ID |
132
+ | `${音乐标题}` 或 `${music-title}` | 音乐标题 |
133
+ | `${音乐作者}` 或 `${music-author}` | 音乐作者 |
126
134
 
127
135
  ## 依赖说明 (Dependencies)
128
136
  ### 音乐语音(可选)
@@ -166,7 +174,7 @@ This is a **multi-platform video/image parsing plugin** developed for the Koishi
166
174
  | 皮皮搞笑 | `h5.pipigx.com/pp/post/`, `ippzone.com` | 短视频 |
167
175
  | 皮皮虾 | `pipix.com`, `pipixia.com` | 短视频 |
168
176
  | 最右 | `share.xiaochuankeji.cn/hybrid/share/post`, `izuiyou.com` | 短视频 |
169
- | 🔧 自定义平台 | 通过 `customPlatforms` 配置添加 | 取决于提供的 API |
177
+ | 自定义平台 | 通过 `customPlatforms` 配置添加 | 取决于提供的 API |
170
178
 
171
179
  ## 项目贡献者 (Contributors)
172
180
 
@@ -179,6 +187,7 @@ This is a **multi-platform video/image parsing plugin** developed for the Koishi
179
187
  | dzt2008 + Apricityx | 提交Bug-会对非支持视频平台URL进行误解析-已修复 |
180
188
  | JH-Ahua | BugPk-Api 支持 |
181
189
  | shangxue | 灵感来源 |
190
+ | linyves | 提交Bug-小红书图集重复发送封面-已修复<br>提交Bug-话题显示异常 #**[话题]#-已修复<br>提交建议-Live Photo 全部按普通图片处理-已采纳<br>提交Bug-解析后会把作者头像一起发送-已修复 |
182
191
 
183
192
  (欢迎通过 Issues 或 PR 加入贡献者列表)
184
193