koishi-plugin-video-parser-all 1.4.8 → 1.5.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
@@ -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;
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('文字格式,支持变量:${标题} ${作者} ${简介} ${视频时长} ${点赞数} ${收藏数} ${转发数} ${播放数} ${评论数} ${发布时间} ${图片数量} ${作者ID} ${音乐标题} ${音乐作者},空行自动隐藏'),
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({
@@ -623,17 +630,6 @@ function parseApiResponse(raw, maxDescLen, fieldMapping) {
623
630
  const author_followers = parseCount(mapField('author_followers', () => extra.author_extra?.follower_count ?? data.author_extra?.follower_count ?? 0));
624
631
  const author_signature = String(mapField('author_signature', () => extra.author_extra?.signature ?? data.author_extra?.signature ?? ''));
625
632
  const admire = parseCount(mapField('admire', () => extra.statistics?.admire_count ?? data.statistics?.admire_count ?? 0));
626
- let hashtags = '';
627
- const tagsArray = mapField('hashtags', () => extra.hashtags ?? data.hashtags);
628
- if (Array.isArray(tagsArray) && tagsArray.length > 0) {
629
- const tagNames = tagsArray.map((t) => t?.name ? t.name : '').filter(Boolean);
630
- hashtags = tagNames.map(name => `#${name}`).join(' ');
631
- if (tagNames.length > 0 && desc) {
632
- const escapedTags = tagNames.map((t) => t.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
633
- const tagRegex = new RegExp(`[##](${escapedTags.join('|')})\\b`, 'gi');
634
- desc = desc.replace(tagRegex, '').replace(/\s{2,}/g, ' ').trim();
635
- }
636
- }
637
633
  title = title.replace(/\[话题\]/g, '');
638
634
  desc = desc.replace(/\[话题\]/g, '');
639
635
  if (title && desc && title.trim() === desc.trim()) {
@@ -643,7 +639,7 @@ function parseApiResponse(raw, maxDescLen, fieldMapping) {
643
639
  title = '';
644
640
  if (desc.trim().startsWith('#'))
645
641
  desc = '';
646
- 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, hashtags };
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 };
647
643
  }
648
644
  const formatVarRegex = /\$\{([^}]+)\}/g;
649
645
  function generateFormattedText(p, format) {
@@ -651,8 +647,6 @@ function generateFormattedText(p, format) {
651
647
  const vars = {
652
648
  '标题': p.title,
653
649
  '作者': p.author,
654
- '作者名称': p.author,
655
- 'author-name': p.author,
656
650
  '简介': p.desc,
657
651
  '视频时长': p.duration > 0 ? formatDuration(p.duration) : '',
658
652
  '点赞数': String(p.like),
@@ -663,22 +657,8 @@ function generateFormattedText(p, format) {
663
657
  '发布时间': p.publishTime ? formatPublishTime(p.publishTime) : '',
664
658
  '图片数量': String(imageCount),
665
659
  '作者ID': p.uid,
666
- 'author-id': p.uid,
667
- '视频链接': p.video,
668
660
  '音乐标题': p.music.title || '',
669
- 'music-title': p.music.title || '',
670
661
  '音乐作者': p.music.author || '',
671
- 'music-author': p.music.author || '',
672
- '音乐封面': p.music.cover || '',
673
- 'author-avatar': p.avatar,
674
- '作者粉丝数': String(p.author_followers),
675
- 'author-followers': String(p.author_followers),
676
- '作者签名': p.author_signature,
677
- 'author-signature': p.author_signature,
678
- '赞赏数': String(p.admire),
679
- 'admire-count': String(p.admire),
680
- '话题标签': p.hashtags,
681
- 'hashtags': p.hashtags,
682
662
  };
683
663
  const varReplacements = Object.entries(vars).map(([key, val]) => ({
684
664
  regex: new RegExp(`\\$\\{${key}\\}`, 'g'),
@@ -773,9 +753,14 @@ function apply(ctx, config) {
773
753
  const mediaDownloadTimeout = config.mediaDownloadTimeout ?? 120000;
774
754
  const maxMediaSize = config.maxMediaSize ?? 0;
775
755
  const downloadEngine = config.downloadEngine || 'internal';
776
- if (downloadEngine === 'aria2' && !ctx.aria2) {
756
+ const aria2Service = ctx.get('aria2');
757
+ const downloadsService = ctx.get('downloads');
758
+ if (downloadEngine === 'aria2' && !aria2Service) {
777
759
  logger.warn('选择了 aria2 下载引擎,但未检测到 koishi-plugin-aria2-plus 服务,将回退到内置下载');
778
760
  }
761
+ if (downloadEngine === 'downloads' && !downloadsService) {
762
+ logger.warn('选择了 downloads 下载引擎,但未检测到 koishi-plugin-downloads 服务,将回退到内置下载');
763
+ }
779
764
  function getPlatformConfig(type) {
780
765
  if (type.startsWith('custom_')) {
781
766
  const name = type.slice(7);
@@ -853,9 +838,9 @@ function apply(ctx, config) {
853
838
  }) || fileExts[0];
854
839
  const fileName = `${filePrefix}_${Date.now()}_${(0, crypto_1.randomBytes)(4).toString('hex')}.${ext}`;
855
840
  const filePath = path_1.default.resolve(cacheDir, fileName);
856
- if (downloadEngine === 'downloads' && ctx.downloads) {
841
+ if (downloadEngine === 'downloads' && downloadsService) {
857
842
  logger.info(`开始下载: ${url}`);
858
- const dest = await ctx.downloads.download(url, path_1.default.join(cacheDir, fileName), {
843
+ const dest = await downloadsService.download(url, path_1.default.join(cacheDir, fileName), {
859
844
  headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' },
860
845
  timeout
861
846
  });
@@ -868,9 +853,9 @@ function apply(ctx, config) {
868
853
  }
869
854
  return dest;
870
855
  }
871
- if (downloadEngine === 'aria2' && ctx.aria2) {
856
+ if (downloadEngine === 'aria2' && aria2Service) {
872
857
  try {
873
- const gid = await ctx.aria2.addUri([url], {
858
+ const gid = await aria2Service.addUri([url], {
874
859
  dir: cacheDir,
875
860
  out: fileName,
876
861
  split: 4,
@@ -887,10 +872,10 @@ function apply(ctx, config) {
887
872
  let lastProgressTime = 0;
888
873
  while (!completed) {
889
874
  if (Date.now() - startTime > timeout) {
890
- await ctx.aria2.remove(gid).catch(() => { });
875
+ await aria2Service.remove(gid).catch(() => { });
891
876
  throw new Error('aria2下载超时');
892
877
  }
893
- const status = await ctx.aria2.tellStatus(gid);
878
+ const status = await aria2Service.tellStatus(gid);
894
879
  if (status.status === 'complete') {
895
880
  completed = true;
896
881
  if (onProgress)
@@ -904,7 +889,7 @@ function apply(ctx, config) {
904
889
  lastProgressTime = Date.now();
905
890
  const total = parseInt(status.totalLength) || 0;
906
891
  const completed = parseInt(status.completedLength) || 0;
907
- const percent = total > 0 ? Math.round((completed / total) * 100) : 0;
892
+ const percent = total > 0 ? Math.round((completed / total) * 100) : -1;
908
893
  const speed = formatSpeed(parseInt(status.downloadSpeed) || 0);
909
894
  onProgress(percent, speed);
910
895
  }
@@ -951,16 +936,27 @@ function apply(ctx, config) {
951
936
  let lastProgressTime = 0;
952
937
  response.data.on('data', (chunk) => {
953
938
  downloaded += chunk.length;
954
- if (onProgress && contentLength > 0 && Date.now() - lastProgressTime > 500) {
939
+ if (onProgress && Date.now() - lastProgressTime > 500) {
955
940
  lastProgressTime = Date.now();
956
- const percent = Math.round((downloaded / contentLength) * 100);
957
- onProgress(percent, '');
941
+ if (contentLength > 0) {
942
+ const percent = Math.round((downloaded / contentLength) * 100);
943
+ onProgress(percent, '');
944
+ }
945
+ else {
946
+ onProgress(-1, formatSpeed(downloaded / ((Date.now() - lastProgressTime) / 1000)));
947
+ }
958
948
  }
959
949
  });
960
950
  try {
961
951
  await (0, promises_2.pipeline)(response.data, writer);
962
- if (onProgress)
963
- onProgress(100, '');
952
+ if (onProgress) {
953
+ if (contentLength > 0) {
954
+ onProgress(100, '');
955
+ }
956
+ else {
957
+ onProgress(-1, formatSpeed(downloaded / ((Date.now() - lastProgressTime) / 1000)));
958
+ }
959
+ }
964
960
  return filePath;
965
961
  }
966
962
  catch (e) {
@@ -994,8 +990,13 @@ function apply(ctx, config) {
994
990
  const prefixMap = { image: 'img', video: 'video', audio: 'music' };
995
991
  const sendFunc = type === 'audio' ? koishi_1.h.audio : type === 'video' ? koishi_1.h.video : koishi_1.h.image;
996
992
  const onProgress = forceDownload ? (percent, speed) => {
997
- const text = `下载进度: ${percent}%` + (speed ? ` (${speed})` : '');
998
- logger.info(text);
993
+ if (percent === -1) {
994
+ logger.info(`下载进度: 已下载 ${speed}`);
995
+ }
996
+ else {
997
+ const text = `下载进度: ${percent}%` + (speed ? ` (${speed})` : '');
998
+ logger.info(text);
999
+ }
999
1000
  } : undefined;
1000
1001
  if (forceDownload) {
1001
1002
  try {
@@ -1107,6 +1108,11 @@ function apply(ctx, config) {
1107
1108
  const text = item.text;
1108
1109
  if (text && config.showImageText)
1109
1110
  forwardMessages.push(buildForwardNode(session, text, botName));
1111
+ if (config.showAuthorAvatar && p.avatar) {
1112
+ if (config.showAuthorAvatarText)
1113
+ forwardMessages.push(buildForwardNode(session, config.authorAvatarText || '作者头像:', botName));
1114
+ forwardMessages.push(buildForwardNode(session, koishi_1.h.image(p.avatar), botName));
1115
+ }
1110
1116
  if (p.cover && config.showCoverImage && p.type !== 'live_photo' && p.type !== 'image' && !(p.type === 'live' && (p.live_photo?.length || p.images?.length))) {
1111
1117
  forwardMessages.push(buildForwardNode(session, koishi_1.h.image(p.cover), botName));
1112
1118
  }
@@ -1145,12 +1151,18 @@ function apply(ctx, config) {
1145
1151
  await sendWithTimeout(session, text);
1146
1152
  await delay(300);
1147
1153
  }
1154
+ if (config.showAuthorAvatar && p.avatar) {
1155
+ if (config.showAuthorAvatarText)
1156
+ await sendWithTimeout(session, config.authorAvatarText || '作者头像:');
1157
+ await sendMedia(session, p.avatar, 'image', config.forceDownloadAuthorAvatar, config.showAuthorAvatarFile).catch(() => { });
1158
+ await delay(300);
1159
+ }
1148
1160
  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.forceDownloadImage, config.showImageFile).catch(() => { });
1161
+ await sendMedia(session, p.cover, 'image', config.forceDownloadCover, config.showCoverFile).catch(() => { });
1150
1162
  await delay(300);
1151
1163
  }
1152
1164
  if (config.showMusicCover && p.music.cover) {
1153
- await sendMedia(session, p.music.cover, 'image', config.forceDownloadImage, config.showImageFile).catch(() => { });
1165
+ await sendMedia(session, p.music.cover, 'image', false, true).catch(() => { });
1154
1166
  await delay(300);
1155
1167
  }
1156
1168
  if (p.video && (p.type === 'video' || (p.type === 'live' && !p.live_photo?.length && !p.images?.length))) {
@@ -1160,7 +1172,7 @@ function apply(ctx, config) {
1160
1172
  if (p.type === 'image' || p.type === 'live_photo' || (p.type === 'live' && (p.live_photo?.length || p.images?.length))) {
1161
1173
  const imageUrls = p.images?.length ? p.images : (p.live_photo?.map(lp => lp.image) ?? []);
1162
1174
  for (const imgUrl of imageUrls) {
1163
- await sendMedia(session, imgUrl, 'image', config.forceDownloadImage, config.showImageFile).catch(() => { });
1175
+ await sendMedia(session, imgUrl, 'image', config.forceDownloadImageNew, config.showImageFileNew).catch(() => { });
1164
1176
  await delay(200);
1165
1177
  }
1166
1178
  }
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.8",
4
+ "version": "1.5.0",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [
package/readme.md CHANGED
@@ -20,176 +20,189 @@ This is a **multi-platform video/image parsing plugin** developed for the Koishi
20
20
 
21
21
  ## 配置项说明 (Configuration)
22
22
 
23
- ### 基本设置
24
- | 配置项 | 类型 | 默认值 | 说明 |
25
- |--------|------|--------|------|
26
- | `enable` | boolean | true | 启用插件 |
27
- | `botName` | string | 视频解析机器人 | 合并转发中的昵称 |
28
- | `showWaitingTip` | boolean | true | 显示等待提示 |
29
- | `debug` | boolean | false | Debug 日志 |
30
- | `platformEnabled` | object | 全开 | 各平台开关 |
31
-
32
- ### 消息格式
33
- | 配置项 | 类型 | 默认值 | 说明 |
34
- |--------|------|--------|------|
35
- | `unifiedMessageFormat` | string | 见预设 | 文字格式,支持变量,空行自动隐藏 |
36
-
37
- ### 媒体发送
38
- | 配置项 | 类型 | 默认值 | 说明 |
39
- |--------|------|--------|------|
40
- | `showImageText` | boolean | true | 发送文字内容 |
41
- | `showCoverImage` | boolean | true | 发送封面图片 |
42
- | `showMusicCover` | boolean | true | 发送音乐封面 |
43
- | `showImageFile` | boolean | true | 封面/图片是否以图片形式发送(关闭则只发送链接) |
44
- | `showVideoFile` | boolean | true | 视频是否以视频形式发送(关闭则只发送链接) |
45
- | `forceDownloadImage` | boolean | false | 强制下载封面/图片 |
46
- | `forceDownloadVideo` | boolean | false | 强制下载视频 |
47
-
48
- ### 音乐语音(需 silk ffmpeg)
49
- | 配置项 | 类型 | 默认值 | 说明 |
50
- |--------|------|--------|------|
51
- | `showMusicVoice` | boolean | false | 音乐链接以语音发送 |
52
- | `showMusicVoiceFile` | boolean | true | 音乐链接是否以语音形式发送(关闭则只发送链接) |
53
- | `forceDownloadMusicVoice` | boolean | false | 强制下载音乐语音 |
54
-
55
- ### 性能与限制
56
- | 配置项 | 类型 | 默认值 | 说明 |
57
- |--------|------|--------|------|
58
- | `maxDescLength` | number | 200 | 简介长度上限 |
59
- | `maxConcurrent` | number | 3 | 解析最大并发数 |
60
- | `downloadConcurrency` | number | 3 | 下载线程数 |
61
- | `mediaDownloadTimeout` | number | 120000 | 统一下载超时 (ms) |
62
- | `maxMediaSize` | number | 0 | 最大下载文件大小 (MB),0 为不限制 |
63
- | `downloadEngine` | string | internal | 下载引擎(internal / aria2 / downloads) |
64
-
65
- ### 网络与请求
66
- | 配置项 | 类型 | 默认值 | 说明 |
67
- |--------|------|--------|------|
68
- | `timeout` | number | 180000 | API 超时 (ms) |
69
- | `videoSendTimeout` | number | 180000 | 发送超时 (ms) |
23
+ ### 基本设置 (Basic Settings)
24
+ | 配置项 (Config) | 类型 (Type) | 默认值 (Default) | 说明 (Description) |
25
+ |----------------|-------------|-------------------|---------------------|
26
+ | `enable` | boolean | true | 启用插件 (Enable plugin) |
27
+ | `botName` | string | 视频解析机器人 | 合并转发中的昵称 (Nickname in forward messages) |
28
+ | `showWaitingTip` | boolean | true | 显示等待提示 (Show waiting tip) |
29
+ | `debug` | boolean | false | Debug 日志 (Debug logging) |
30
+ | `platformEnabled` | object | 全开 (All enabled) | 各平台开关 (Platform switches) |
31
+
32
+ ### 消息格式 (Message Format)
33
+ | 配置项 (Config) | 类型 (Type) | 默认值 (Default) | 说明 (Description) |
34
+ |----------------|-------------|-------------------|---------------------|
35
+ | `unifiedMessageFormat` | string | 见预设 (See preset) | 文字格式,支持变量,空行自动隐藏 (Text format, supports variables, auto-hide empty lines) |
36
+
37
+ ### 媒体发送 (Media Sending)
38
+ | 配置项 (Config) | 类型 (Type) | 默认值 (Default) | 说明 (Description) |
39
+ |----------------|-------------|-------------------|---------------------|
40
+ | `showImageText` | boolean | true | 发送文字内容 (Send text content) |
41
+ | `showCoverImage` | boolean | true | 发送封面图片 (Send cover image) |
42
+ | `showCoverFile` | boolean | true | 封面是否以图片形式发送(关闭则只发送链接)(Send cover as image, otherwise link only) |
43
+ | `showImageFileNew` | boolean | true | 图片是否以图片形式发送(关闭则只发送链接)(Send images as image, otherwise link only) |
44
+ | `showAuthorAvatar` | boolean | true | 发送作者头像图片 (Send author avatar image) |
45
+ | `showAuthorAvatarFile` | boolean | true | 作者头像图片是否以图片形式发送(关闭则只发送链接)(Send author avatar as image, otherwise link only) |
46
+ | `showAuthorAvatarText` | boolean | true | 发送作者头像前显示文字提示 (Show text hint before author avatar) |
47
+ | `authorAvatarText` | string | 作者头像: | 作者头像前显示的文字 (Text displayed before author avatar) |
48
+ | `showMusicCover` | boolean | true | 发送音乐封面图片 (Send music cover image) |
49
+ | `showVideoFile` | boolean | true | 视频是否以视频形式发送(关闭则只发送链接)(Send video as file, otherwise link only) |
50
+ | `forceDownloadCover` | boolean | false | 强制下载封面 (Force download cover) |
51
+ | `forceDownloadImageNew` | boolean | false | 强制下载图片 (Force download images) |
52
+ | `forceDownloadAuthorAvatar` | boolean | false | 强制下载作者头像 (Force download author avatar) |
53
+ | `forceDownloadVideo` | boolean | false | 强制下载视频 (Force download video) |
54
+
55
+ ### 音乐语音(需 silk 和 ffmpeg)(Music Voice - requires silk & ffmpeg)
56
+ | 配置项 (Config) | 类型 (Type) | 默认值 (Default) | 说明 (Description) |
57
+ |----------------|-------------|-------------------|---------------------|
58
+ | `showMusicVoice` | boolean | false | 音乐链接以语音发送 (Send music as voice) |
59
+ | `showMusicVoiceFile` | boolean | true | 音乐链接是否以语音形式发送(关闭则只发送链接)(Send as voice file, otherwise link only) |
60
+ | `forceDownloadMusicVoice` | boolean | false | 强制下载音乐语音 (Force download music voice) |
61
+
62
+ ### 性能与限制 (Performance & Limits)
63
+ | 配置项 (Config) | 类型 (Type) | 默认值 (Default) | 说明 (Description) |
64
+ |----------------|-------------|-------------------|---------------------|
65
+ | `maxDescLength` | number | 200 | 简介长度上限 (Max description length) |
66
+ | `maxConcurrent` | number | 3 | 解析最大并发数 (Max concurrent parsing) |
67
+ | `downloadConcurrency` | number | 3 | 下载线程数 (Download concurrency) |
68
+ | `mediaDownloadTimeout` | number | 120000 | 统一下载超时 (ms) (Download timeout) |
69
+ | `maxMediaSize` | number | 0 | 最大下载文件大小 (MB),0 为不限制 (Max file size, 0 = unlimited) |
70
+ | `downloadEngine` | string | internal | 下载引擎(internal / aria2 / downloads)(Download engine) |
71
+
72
+ ### 网络与请求 (Network & Request)
73
+ | 配置项 (Config) | 类型 (Type) | 默认值 (Default) | 说明 (Description) |
74
+ |----------------|-------------|-------------------|---------------------|
75
+ | `timeout` | number | 180000 | API 超时 (ms) (API timeout) |
76
+ | `videoSendTimeout` | number | 180000 | 发送超时 (ms) (Send timeout) |
70
77
  | `userAgent` | string | 见预设 | User-Agent |
71
- | `proxy` | object | ... | HTTP/HTTPS 代理 |
72
- | `customHeaders` | array | [] | 自定义请求头 |
73
-
74
- ### 发送与重试
75
- | 配置项 | 类型 | 默认值 | 说明 |
76
- |--------|------|--------|------|
77
- | `ignoreSendError` | boolean | true | 忽略发送失败 |
78
- | `retryTimes` | number | 3 | 重试次数 |
79
- | `retryInterval` | number | 1000 | 重试间隔 (ms) |
80
- | `enableForward` | boolean | false | 合并转发(OneBot/Satori) |
81
-
82
- ### 缓存与临时文件
83
- | 配置项 | 类型 | 默认值 | 说明 |
84
- |--------|------|--------|------|
85
- | `deduplicationInterval` | number | 180 | 去重间隔 (s) |
86
- | `cacheTTL` | number | 600 | 缓存时间 (s) |
87
- | `cacheDir` | string | ./temp_cache | 统一临时目录 |
88
-
89
- ### API 与平台
90
- | 配置项 | 类型 | 默认值 | 说明 |
91
- |--------|------|--------|------|
92
- | `platformDedicatedFirst` | object | 全关 | 优先专属 API |
93
- | `customApis` | array | [] | 覆盖内置平台 API |
94
- | `customPlatforms` | array | [] | 自定义新平台 |
95
- | `globalFieldMapping` | string | 预设 | 全局字段映射 JSON |
96
-
97
- ### 界面文本
98
- | 配置项 | 类型 | 默认值 | 说明 |
99
- |--------|------|--------|------|
100
- | `waitingTipText` | string | 正在解析... | 等待提示 |
101
- | `unsupportedPlatformText` | string | 不支持该平台 | 不支持提示 |
102
- | `invalidLinkText` | string | 无效链接 | 无效链接提示 |
103
- | `parseErrorPrefix` | string | ❌ 解析失败: | 错误前缀 |
104
- | `parseErrorItemFormat` | string | ... | 错误格式 |
78
+ | `proxy` | object | ... | HTTP/HTTPS 代理 (Proxy) |
79
+ | `customHeaders` | array | [] | 自定义请求头 (Custom headers) |
80
+
81
+ ### 发送与重试 (Send & Retry)
82
+ | 配置项 (Config) | 类型 (Type) | 默认值 (Default) | 说明 (Description) |
83
+ |----------------|-------------|-------------------|---------------------|
84
+ | `ignoreSendError` | boolean | true | 忽略发送失败 (Ignore send errors) |
85
+ | `retryTimes` | number | 3 | 重试次数 (Retry times) |
86
+ | `retryInterval` | number | 1000 | 重试间隔 (ms) (Retry interval) |
87
+ | `enableForward` | boolean | false | 合并转发(OneBot/Satori)(Enable forward message) |
88
+
89
+ ### 缓存与临时文件 (Cache & Temp Files)
90
+ | 配置项 (Config) | 类型 (Type) | 默认值 (Default) | 说明 (Description) |
91
+ |----------------|-------------|-------------------|---------------------|
92
+ | `deduplicationInterval` | number | 180 | 去重间隔 (s) (Deduplication interval) |
93
+ | `cacheTTL` | number | 600 | 缓存时间 (s) (Cache TTL) |
94
+ | `cacheDir` | string | ./temp_cache | 统一临时目录 (Temp directory) |
95
+
96
+ ### API 与平台 (API & Platforms)
97
+ | 配置项 (Config) | 类型 (Type) | 默认值 (Default) | 说明 (Description) |
98
+ |----------------|-------------|-------------------|---------------------|
99
+ | `platformDedicatedFirst` | object | 全关 (All off) | 优先专属 API (Prioritize dedicated APIs) |
100
+ | `customApis` | array | [] | 覆盖内置平台 API (Override built-in APIs) |
101
+ | `customPlatforms` | array | [] | 自定义新平台 (Custom platforms) |
102
+ | `globalFieldMapping` | string | 预设 (Preset) | 全局字段映射 JSON (Global field mapping JSON) |
103
+
104
+ ### 界面文本 (UI Text)
105
+ | 配置项 (Config) | 类型 (Type) | 默认值 (Default) | 说明 (Description) |
106
+ |----------------|-------------|-------------------|---------------------|
107
+ | `waitingTipText` | string | 正在解析... | 等待提示 (Waiting tip) |
108
+ | `unsupportedPlatformText` | string | 不支持该平台 | 不支持提示 (Unsupported platform tip) |
109
+ | `invalidLinkText` | string | 无效链接 | 无效链接提示 (Invalid link tip) |
110
+ | `parseErrorPrefix` | string | ❌ 解析失败: | 错误前缀 (Error prefix) |
111
+ | `parseErrorItemFormat` | string | ... | 错误格式 (Error format) |
105
112
 
106
113
  ## 支持的变量 (Supported Variables)
107
- 在 `unifiedMessageFormat` 中可使用以下变量,空行自动隐藏:
108
-
109
- | 变量名 | 说明 |
110
- |--------|------|
111
- | `${标题}` | 视频/图集标题 |
112
- | `${作者}` | 作者名称 |
113
- | `${简介}` | 内容简介 |
114
- | `${视频时长}` | 视频时长(时:分:秒) |
115
- | `${点赞数}` | 点赞数量 |
116
- | `${收藏数}` | 收藏数量 |
117
- | `${转发数}` | 转发/分享数量 |
118
- | `${播放数}` | 播放量 |
119
- | `${评论数}` | 评论数量 |
120
- | `${发布时间}` | 发布时间(格式化) |
121
- | `${图片数量}` | 图集/实况图片数量 |
122
- | `${作者ID}` | 作者唯一标识ID |
123
- | `${视频链接}` | 视频原始链接 |
124
- | `${音乐标题}` | 音乐标题 |
125
- | `${音乐作者}` | 音乐作者 |
114
+ 在 `unifiedMessageFormat` 中可使用以下变量,空行自动隐藏。
115
+ The following variables can be used in `unifiedMessageFormat`, empty lines are auto-hidden.
116
+
117
+ | 变量 (Variable) | 说明 (Description) |
118
+ |----------------|--------------------|
119
+ | `${标题}` | 视频/图集标题 (Title) |
120
+ | `${作者}` | 作者名称 (Author name) |
121
+ | `${简介}` | 内容简介 (Description) |
122
+ | `${视频时长}` | 视频时长(时:分:秒)(Duration, hh:mm:ss) |
123
+ | `${点赞数}` | 点赞数量 (Like count) |
124
+ | `${收藏数}` | 收藏数量 (Favorite count) |
125
+ | `${转发数}` | 转发/分享数量 (Share count) |
126
+ | `${播放数}` | 播放量 (Play count) |
127
+ | `${评论数}` | 评论数量 (Comment count) |
128
+ | `${发布时间}` | 发布时间(格式化)(Publish time, formatted) |
129
+ | `${图片数量}` | 图集/实况图片数量 (Image count) |
130
+ | `${作者ID}` | 作者唯一标识ID (Author ID) |
131
+ | `${音乐标题}` | 音乐标题 (Music title) |
132
+ | `${音乐作者}` | 音乐作者 (Music author) |
126
133
 
127
134
  ## 依赖说明 (Dependencies)
128
- ### 音乐语音(可选)
129
- 若启用 `showMusicVoice`,请安装:
130
- - `koishi-plugin-silk`:silk 编解码
131
- - `koishi-plugin-ffmpeg`:音频重采样
132
- ### aria2 下载引擎(可选)
133
- 若启用 `downloadEngine: 'aria2'`,请安装可选依赖 `koishi-plugin-aria2-plus` 并配置该插件连接 aria2 服务。插件启动时会自动检测该服务,未安装或不可用时将降级为内置下载,不影响正常使用。
134
- ### downloads 服务(可选)
135
- 若启用 `downloadEngine: 'downloads'`,请安装可选依赖 `koishi-plugin-downloads`,失败时回退到内置下载。
135
+ ### 音乐语音(可选)(Music Voice - Optional)
136
+ 若启用 `showMusicVoice`,请安装:
137
+ If you enable `showMusicVoice`, please install:
138
+ - `koishi-plugin-silk`:silk 编解码 (Silk codec)
139
+ - `koishi-plugin-ffmpeg`:音频重采样 (Audio resampling)
140
+
141
+ ### aria2 下载引擎(可选)(aria2 Download Engine - Optional)
142
+ 若启用 `downloadEngine: 'aria2'`,请安装可选依赖 `koishi-plugin-aria2-plus` 并配置该插件连接 aria2 服务。插件启动时会自动检测该服务,未安装或不可用时将降级为内置下载,不影响正常使用。
143
+ If you use `downloadEngine: 'aria2'`, please install the optional dependency `koishi-plugin-aria2-plus` and configure it to connect to an aria2 service. The plugin will auto-detect it; if not available, it will fall back to the built-in downloader without affecting normal usage.
144
+
145
+ ### downloads 服务(可选)(Downloads Service - Optional)
146
+ 若启用 `downloadEngine: 'downloads'`,请安装可选依赖 `koishi-plugin-downloads`,失败时回退到内置下载。
147
+ If you use `downloadEngine: 'downloads'`, please install the optional dependency `koishi-plugin-downloads`. It will fall back to the built-in downloader on failure.
136
148
 
137
149
  ## 支持的平台 (Supported Platforms)
138
150
 
139
- > 以下为插件内置链接匹配规则,可根据用户发送的链接自动识别。所有匹配规则同时支持 HTTP 和 HTTPS 协议,并兼容多级路径(如短链后带 `/` 子路径)。
140
-
141
- | 平台名称 | 关键词识别(匹配的域名/路径模式) | 解析能力 |
142
- |----------|----------------------------------|----------|
143
- | 哔哩哔哩 (B站) | `bilibili.com/video/`, `b23.tv`, `bili*.cn`, `b23.wtf`, `bili2233.cn` | 视频 |
144
- | 抖音 | `douyin.com/video/`, `v.douyin.com` | 短视频、图集、实况 |
145
- | 快手 | `kuaishou.com/short-video/`, `v.kuaishou.com`, `kuaishou.com/f/` | 短视频、图集 |
146
- | 小红书 | `xiaohongshu.com/discovery/item/`, `xhslink.com`(含多级路径), `xiaohongshu.com/explore/`, `xiaohongshu.com/board/` | 图文、视频 |
147
- | 微博 | `weibo.com/数字/`, `video.weibo.com/show`, `t.cn`, `m.weibo.cn` | 视频、图集 |
148
- | 西瓜视频 | `ixigua.com` | 短视频 |
149
- | YouTube | `youtube.com/watch`, `youtu.be`, `youtube.com/shorts/` | 视频 |
150
- | TikTok | `tiktok.com/@/video/`, `vm.tiktok.com`, `vt.tiktok.com` | 短视频 |
151
- | AcFun(A站) | `acfun.cn/v/ac` | 视频 |
152
- | 知乎 | `zhihu.com/video/`, `zhihu.com/question/xxx/answer/xxx`, `zhuanlan.zhihu.com/p/`, `zhihu.com/zvideo/` | 视频、回答中的视频 |
153
- | 微视 | `weishi.qq.com/weishi/feed/` | 短视频 |
154
- | 虎牙 | `huya.com/video/` | 直播回放、视频 |
155
- | 好看视频 | `haokan.baidu.com/v?vid=` | 短视频 |
156
- | 美拍 | `meipai.com/media/` | 短视频 |
157
- | Twitter / X | `twitter.com/用户名/status/`, `x.com/用户名/status/` | 视频、图文 |
158
- | Instagram | `instagram.com/p/`, `instagram.com/reel/`, `instagram.com/share/` | 图文、Reels |
159
- | 豆包(视频) | `doubao.com/video/`, `doubao.com/video-sharing` | 视频 |
160
- | 豆包(图集) | `doubao.com/thread/` | 图文 |
161
- | **即梦 (Jimeng)** | `jimeng.jianying.com`, `jimeng.cn`, `dreamina.jianying.com`, `dreamina.capcut.com` | AI视频、AI图片 |
162
- | 绿洲 (Oasis) | `oasis.weibo.com/v/` | 视频、图文 |
163
- | 视频号 (WeChat Channels) | `channels.weixin.qq.com`, `weixin.qq.com/sph/` | 短视频 |
164
- | 梨视频 | `pearvideo.com/video_`, `video.li` | 短视频 |
165
- | 全民直播 | `quanmin.tv`, `quanmintv.cn` | 直播 |
166
- | 皮皮搞笑 | `h5.pipigx.com/pp/post/`, `ippzone.com` | 短视频 |
167
- | 皮皮虾 | `pipix.com`, `pipixia.com` | 短视频 |
168
- | 最右 | `share.xiaochuankeji.cn/hybrid/share/post`, `izuiyou.com` | 短视频 |
169
- | 🔧 自定义平台 | 通过 `customPlatforms` 配置添加 | 取决于提供的 API |
151
+ > 以下为插件内置链接匹配规则,可根据用户发送的链接自动识别。所有匹配规则同时支持 HTTP 和 HTTPS 协议,并兼容多级路径(如短链后带 `/` 子路径)。
152
+ > The following are the built-in link matching rules, which can automatically identify links sent by users. All rules support both HTTP and HTTPS, and are compatible with multi-level paths (e.g., short links followed by `/` subpaths).
153
+
154
+ | 平台名称 (Platform) | 关键词识别(匹配的域名/路径模式)(Keyword/Domain Patterns) | 解析能力 (Supported Content) |
155
+ |---------------------|--------------------------------------------------------------|------------------------------|
156
+ | 哔哩哔哩 (B站) Bilibili | `bilibili.com/video/`, `b23.tv`, `bili*.cn`, `b23.wtf`, `bili2233.cn` | 视频 (Video) |
157
+ | 抖音 Douyin | `douyin.com/video/`, `v.douyin.com` | 短视频、图集、实况 (Short video, Image, Live photo) |
158
+ | 快手 Kuaishou | `kuaishou.com/short-video/`, `v.kuaishou.com`, `kuaishou.com/f/` | 短视频、图集 (Short video, Image) |
159
+ | 小红书 Xiaohongshu | `xiaohongshu.com/discovery/item/`, `xhslink.com`, `xiaohongshu.com/explore/`, `xiaohongshu.com/board/` | 图文、视频 (Image, Video) |
160
+ | 微博 Weibo | `weibo.com/数字/`, `video.weibo.com/show`, `t.cn`, `m.weibo.cn` | 视频、图集 (Video, Image) |
161
+ | 西瓜视频 Xigua | `ixigua.com` | 短视频 (Short video) |
162
+ | YouTube | `youtube.com/watch`, `youtu.be`, `youtube.com/shorts/` | 视频 (Video) |
163
+ | TikTok | `tiktok.com/@/video/`, `vm.tiktok.com`, `vt.tiktok.com` | 短视频 (Short video) |
164
+ | AcFun(A站) | `acfun.cn/v/ac` | 视频 (Video) |
165
+ | 知乎 Zhihu | `zhihu.com/video/`, `zhihu.com/question/xxx/answer/xxx`, `zhuanlan.zhihu.com/p/`, `zhihu.com/zvideo/` | 视频、回答中的视频 (Video, answer video) |
166
+ | 微视 Weishi | `weishi.qq.com/weishi/feed/` | 短视频 (Short video) |
167
+ | 虎牙 Huya | `huya.com/video/` | 直播回放、视频 (Live replay, Video) |
168
+ | 好看视频 Haokan | `haokan.baidu.com/v?vid=` | 短视频 (Short video) |
169
+ | 美拍 Meipai | `meipai.com/media/` | 短视频 (Short video) |
170
+ | Twitter / X | `twitter.com/用户名/status/`, `x.com/用户名/status/` | 视频、图文 (Video, Image) |
171
+ | Instagram | `instagram.com/p/`, `instagram.com/reel/`, `instagram.com/share/` | 图文、Reels (Image, Reels) |
172
+ | 豆包(视频)Doubao Video | `doubao.com/video/`, `doubao.com/video-sharing` | 视频 (Video) |
173
+ | 豆包(图集)Doubao Image | `doubao.com/thread/` | 图文 (Image) |
174
+ | **即梦 (Jimeng)** | `jimeng.jianying.com`, `jimeng.cn`, `dreamina.jianying.com`, `dreamina.capcut.com` | AI视频、AI图片 (AI video, AI image) |
175
+ | 绿洲 Oasis | `oasis.weibo.com/v/` | 视频、图文 (Video, Image) |
176
+ | 视频号 WeChat Channels | `channels.weixin.qq.com`, `weixin.qq.com/sph/` | 短视频 (Short video) |
177
+ | 梨视频 Lishi | `pearvideo.com/video_`, `video.li` | 短视频 (Short video) |
178
+ | 全民直播 Quanmin | `quanmin.tv`, `quanmintv.cn` | 直播 (Live) |
179
+ | 皮皮搞笑 Pipigx | `h5.pipigx.com/pp/post/`, `ippzone.com` | 短视频 (Short video) |
180
+ | 皮皮虾 Pipixia | `pipix.com`, `pipixia.com` | 短视频 (Short video) |
181
+ | 最右 Zuiyou | `share.xiaochuankeji.cn/hybrid/share/post`, `izuiyou.com` | 短视频 (Short video) |
182
+ | 自定义平台 Custom | 通过 `customPlatforms` 配置添加 (Add via `customPlatforms`) | 取决于提供的 API (Depends on API) |
170
183
 
171
184
  ## 项目贡献者 (Contributors)
172
185
 
173
186
  | 贡献者 (Contributor) | 贡献内容 (Contribution) |
174
187
  |----------------------|-------------------------|
175
188
  | Minecraft-1314 | 插件完整开发 (Complete plugin development) |
176
- | ShiraiKuroko003 | 修复消息格式设置问题并且PR-1.2.5版本已修复 |
177
- | cyavb | 提交功能建议-给自定义API添加KEY认证-已修复 |
178
- | Keep785 | 提交Bug-无法正常关闭发送封面-已修复<br>提交Bug-解析问题-已修复 |
179
- | dzt2008 + Apricityx | 提交Bug-会对非支持视频平台URL进行误解析-已修复 |
180
- | JH-Ahua | BugPk-Api 支持 |
181
- | shangxue | 灵感来源 |
189
+ | ShiraiKuroko003 | 修复消息格式设置问题并且PR-1.2.5版本已修复 (Fixed message format issue, PR-1.2.5) |
190
+ | cyavb | 提交功能建议-给自定义API添加KEY认证-已修复 (Suggested custom API key auth - fixed) |
191
+ | Keep785 | 提交Bug-无法正常关闭发送封面-已修复<br>提交Bug-解析问题-已修复 (Reported bugs - cannot disable cover sending and parsing issues - fixed) |
192
+ | dzt2008 + Apricityx | 提交Bug-会对非支持视频平台URL进行误解析-已修复 (Reported incorrect parsing of unsupported URLs - fixed) |
193
+ | linyves | 提交Bug-小红书图集重复发送封面-已修复<br>提交Bug-话题显示异常 #**[话题]#-已修复<br>提交建议-Live Photo 全部按普通图片处理-已采纳<br>提交Bug-解析后会把作者头像一起发送-已修复 |
194
+ | JH-Ahua | BugPk-Api 支持 (BugPk-Api support) |
195
+ | shangxue | 灵感来源 (Inspiration) |
182
196
 
183
- (欢迎通过 Issues 或 PR 加入贡献者列表)
197
+ (欢迎通过 Issues 或 PR 加入贡献者列表)
198
+ (Welcome to join the contributor list via Issues or PR)
184
199
 
185
200
  ## 许可协议 (License)
186
201
 
187
- 本项目采用 MIT 许可证,详情参见 [LICENSE](LICENSE) 文件。
188
-
202
+ 本项目采用 MIT 许可证,详情参见 [LICENSE](LICENSE) 文件。
189
203
  This project is licensed under the MIT License, see the [LICENSE](LICENSE) file for details.
190
204
 
191
205
  ## 支持我们 (Support Us)
192
206
 
193
- 如果这个项目对您有帮助,欢迎点亮右上角的 Star ⭐ 支持我们!
194
-
207
+ 如果这个项目对您有帮助,欢迎点亮右上角的 Star ⭐ 支持我们!
195
208
  If this project is helpful to you, please feel free to star it in the upper right corner ⭐ to support us!