koishi-plugin-video-parser-all 0.0.3 → 0.0.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 (3) hide show
  1. package/lib/index.d.ts +3 -45
  2. package/lib/index.js +130 -260
  3. package/package.json +1 -1
package/lib/index.d.ts CHANGED
@@ -1,34 +1,9 @@
1
1
  import { Context, Schema } from 'koishi';
2
2
  export declare const name = "video-parser-all";
3
- interface BilibiliFieldMap {
4
- title: string;
5
- author: string;
6
- description: string;
7
- like: string;
8
- coin: string;
9
- collect: string;
10
- share: string;
11
- view: string;
12
- danmaku: string;
13
- cover: string;
14
- duration: string;
15
- size: string;
16
- url: string;
17
- }
18
- interface DyKsFieldMap {
19
- title: string;
20
- author: string;
21
- description: string;
22
- cover: string;
23
- duration: string;
24
- size: string;
25
- url: string;
26
- }
27
3
  export interface Config {
28
4
  enable: boolean;
29
5
  showWaitingTip: boolean;
30
6
  waitingTipText: string;
31
- parserSource: string;
32
7
  allowBVAVParse: boolean;
33
8
  sameLinkInterval: number;
34
9
  minVideoDuration: number;
@@ -37,34 +12,17 @@ export interface Config {
37
12
  maxVideoDuration: number;
38
13
  longVideoTip: string;
39
14
  longVideoUseImageParse: boolean;
40
- maxFileSize: number;
41
15
  imageParseFormat: string;
42
16
  showVideoLink: boolean;
43
17
  maxDescLength: number;
44
18
  enableMergeForward: boolean;
45
19
  downloadBeforeSend: boolean;
46
20
  messageBufferDelay: number;
47
- userAgent: string;
48
- builtinApi: {
21
+ publicApi: {
22
+ enable: boolean;
23
+ url: string;
49
24
  timeout: number;
50
- retryCount: number;
51
- };
52
- bilibili: {
53
- customApi: string;
54
- apiKey: string;
55
- fieldMap: BilibiliFieldMap;
56
- };
57
- douyin: {
58
- customApi: string;
59
- apiKey: string;
60
- fieldMap: DyKsFieldMap;
61
- };
62
- kuaishou: {
63
- customApi: string;
64
- apiKey: string;
65
- fieldMap: DyKsFieldMap;
66
25
  };
67
26
  }
68
27
  export declare const Config: Schema<Config>;
69
28
  export declare function apply(ctx: Context, config: Config): void;
70
- export {};
package/lib/index.js CHANGED
@@ -12,215 +12,130 @@ exports.name = 'video-parser-all';
12
12
  exports.Config = koishi_1.Schema.object({
13
13
  enable: koishi_1.Schema.boolean().default(true).description('开启解析功能'),
14
14
  showWaitingTip: koishi_1.Schema.boolean().default(true).description('是否返回等待提示'),
15
- waitingTipText: koishi_1.Schema.string().default('正在解析视频链接...可能需要稍等一下...').description('等待提示文字内容'),
16
- parserSource: koishi_1.Schema.string().default('builtin').description('解析来源:builtin(内置免费API) / custom(自定义API)'),
15
+ waitingTipText: koishi_1.Schema.string().default('正在解析视频链接...').description('等待提示文字内容'),
17
16
  allowBVAVParse: koishi_1.Schema.boolean().default(true).description('允许BV/AV号解析'),
18
17
  sameLinkInterval: koishi_1.Schema.number().default(180).description('相同链接处理间隔(秒)'),
19
18
  minVideoDuration: koishi_1.Schema.number().default(0).description('最小时长(分钟)'),
20
- shortVideoTip: koishi_1.Schema.string().default('视频太短啦!不看不看~').description('过短提示'),
19
+ shortVideoTip: koishi_1.Schema.string().default('视频时长过短,不解析~').description('过短提示'),
21
20
  shortVideoUseImageParse: koishi_1.Schema.boolean().default(false).description('过短视频图文解析'),
22
- maxVideoDuration: koishi_1.Schema.number().default(25).description('最大时长(分钟)'),
23
- longVideoTip: koishi_1.Schema.string().default('视频太长啦!去B站看吧~').description('过长提示'),
21
+ maxVideoDuration: koishi_1.Schema.number().default(60).description('最大时长(分钟)'),
22
+ longVideoTip: koishi_1.Schema.string().default('视频时长过长,不解析~').description('过长提示'),
24
23
  longVideoUseImageParse: koishi_1.Schema.boolean().default(false).description('过长视频图文解析'),
25
- maxFileSize: koishi_1.Schema.number().default(50).description('最大文件大小(MB)'),
24
+ // 核心修改1:移除字段映射配置
25
+ // 核心修改2:超大输入框(通过多行默认值+textarea属性实现)
26
26
  imageParseFormat: koishi_1.Schema.string()
27
- .default(`\
28
- ${'='.repeat(40)}
29
- 标题:${'${标题}'}
30
- UP主:${'${UP主}'}
31
- 简介:${'${简介}'}
32
- ${'='.repeat(40)}
33
- 点赞:${'${点赞}'} 投币:${'${投币}'}
34
- 收藏:${'${收藏}'} 转发:${'${转发}'}
35
- 观看:${'${观看}'} 弹幕:${'${弹幕}'}
36
- ${'='.repeat(40)}
37
- ${'${封面}'}
38
- `)
39
- .description(`图文解析的返回格式(支持换行)
40
- 注意变量格式,以及变量名称:
41
- - ${'${标题}'} / ${'${UP主}'} / ${'${简介}'}
42
- - ${'${点赞}'} / ${'${投币}'} / ${'${收藏}'} / ${'${转发}'}
43
- - ${'${观看}'} / ${'${弹幕}'} / ${'${封面}'}
44
- - ${'${tab}'} 制表符 | ${'${~~~}'} 分割线
45
- 抖音/快手会自动过滤点赞/投币等统计字段`),
46
- showVideoLink: koishi_1.Schema.boolean().default(true).description('在末尾显示视频的链接地址(推荐开启)'),
47
- maxDescLength: koishi_1.Schema.number().default(100).description('视频的简介最大的字符长度'),
48
- enableMergeForward: koishi_1.Schema.boolean().default(false).description('是否开启合并转发 仅支持 onebot 适配器 其他平台开启 无效'),
49
- downloadBeforeSend: koishi_1.Schema.boolean().default(false).description('是否将视频链接下载后再发送 (以解决部分onebot协议端的问题)'),
50
- messageBufferDelay: koishi_1.Schema.number().default(1).description('消息接收缓冲延迟(秒),避免重复解析'),
51
- userAgent: koishi_1.Schema.string().default('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36').description('所有 API 请求所用的 User-Agent'),
52
- // 内置免费API配置
53
- builtinApi: koishi_1.Schema.object({
54
- timeout: koishi_1.Schema.number().default(10000).description('内置API请求超时时间(毫秒)'),
55
- retryCount: koishi_1.Schema.number().default(2).description('内置API失败重试次数')
56
- }).description('内置免费解析API配置'),
57
- // 自定义API配置(备用)
58
- bilibili: koishi_1.Schema.object({
59
- customApi: koishi_1.Schema.string().default('').description('B站自定义解析API地址(留空使用内置API)'),
60
- apiKey: koishi_1.Schema.string().default('').description('B站API密钥'),
61
- fieldMap: koishi_1.Schema.object({
62
- title: koishi_1.Schema.string().default('title').description('标题字段映射'),
63
- author: koishi_1.Schema.string().default('author').description('UP主字段映射'),
64
- description: koishi_1.Schema.string().default('desc').description('简介字段映射'),
65
- like: koishi_1.Schema.string().default('likeCount').description('点赞字段映射'),
66
- coin: koishi_1.Schema.string().default('coinCount').description('投币字段映射'),
67
- collect: koishi_1.Schema.string().default('collectCount').description('收藏字段映射'),
68
- share: koishi_1.Schema.string().default('shareCount').description('转发字段映射'),
69
- view: koishi_1.Schema.string().default('viewCount').description('观看字段映射'),
70
- danmaku: koishi_1.Schema.string().default('danmakuCount').description('弹幕字段映射'),
71
- cover: koishi_1.Schema.string().default('cover').description('封面字段映射'),
72
- duration: koishi_1.Schema.string().default('duration').description('时长字段映射'),
73
- size: koishi_1.Schema.string().default('size').description('文件大小字段映射'),
74
- url: koishi_1.Schema.string().default('video_url').description('视频链接字段映射')
75
- }).description('B站返回字段映射')
76
- }).description('B站自定义API配置(留空使用内置免费API)'),
77
- douyin: koishi_1.Schema.object({
78
- customApi: koishi_1.Schema.string().default('').description('抖音自定义解析API地址(留空使用内置API)'),
79
- apiKey: koishi_1.Schema.string().default('').description('抖音API密钥'),
80
- fieldMap: koishi_1.Schema.object({
81
- title: koishi_1.Schema.string().default('title').description('标题字段映射'),
82
- author: koishi_1.Schema.string().default('author').description('作者字段映射'),
83
- description: koishi_1.Schema.string().default('desc').description('简介字段映射'),
84
- cover: koishi_1.Schema.string().default('cover').description('封面字段映射'),
85
- duration: koishi_1.Schema.string().default('duration').description('时长字段映射'),
86
- size: koishi_1.Schema.string().default('size').description('文件大小字段映射'),
87
- url: koishi_1.Schema.string().default('video_url').description('视频链接字段映射')
88
- }).description('抖音返回字段映射')
89
- }).description('抖音自定义API配置(留空使用内置免费API)'),
90
- kuaishou: koishi_1.Schema.object({
91
- customApi: koishi_1.Schema.string().default('').description('快手自定义解析API地址(留空使用内置API)'),
92
- apiKey: koishi_1.Schema.string().default('').description('快手API密钥'),
93
- fieldMap: koishi_1.Schema.object({
94
- title: koishi_1.Schema.string().default('title').description('标题字段映射'),
95
- author: koishi_1.Schema.string().default('author').description('作者字段映射'),
96
- description: koishi_1.Schema.string().default('desc').description('简介字段映射'),
97
- cover: koishi_1.Schema.string().default('cover').description('封面字段映射'),
98
- duration: koishi_1.Schema.string().default('duration').description('时长字段映射'),
99
- size: koishi_1.Schema.string().default('size').description('文件大小字段映射'),
100
- url: koishi_1.Schema.string().default('video_url').description('视频链接字段映射')
101
- }).description('快手返回字段映射')
102
- }).description('快手自定义API配置(留空使用内置免费API)')
27
+ .role('textarea') // 关键:强制渲染为多行文本框
28
+ .default(`┌────────────────────────────────────────────────────────────────────┐
29
+ │ 视频解析信息 │
30
+ ├────────────────────────────────────────────────────────────────────┤
31
+ │ 标题:${'${标题}'}
32
+ │ UP主:${'${UP主}'}
33
+ │ 简介:${'${简介}'}
34
+ ├────────────────────────────────────────────────────────────────────┤
35
+ │ 点赞:${'${点赞}'} 投币:${'${投币}'} 收藏:${'${收藏}'}
36
+ │ 转发:${'${转发}'} 观看:${'${观看}'} 弹幕:${'${弹幕}'}
37
+ ├────────────────────────────────────────────────────────────────────┤
38
+ │ 封面:${'${封面}'}
39
+ └────────────────────────────────────────────────────────────────────┘
40
+ 📌 提示:抖音/快手会自动隐藏点赞/投币等统计字段`)
41
+ .description(`图文解析格式配置(超大输入框)
42
+ 支持的变量:
43
+ - 基础:${'${标题}'} / ${'${UP主}'} / ${'${简介}'} / ${'${封面}'}
44
+ - B站专属:${'${点赞}'} / ${'${投币}'} / ${'${收藏}'} / ${'${转发}'} / ${'${观看}'} / ${'${弹幕}'}
45
+ - 特殊:${'${tab}'}(制表符)
46
+ 抖音/快手会自动过滤B站专属变量,无需手动删除`),
47
+ showVideoLink: koishi_1.Schema.boolean().default(true).description('显示视频链接'),
48
+ maxDescLength: koishi_1.Schema.number().default(100).description('简介最大长度'),
49
+ enableMergeForward: koishi_1.Schema.boolean().default(false).description('合并转发(仅onebot'),
50
+ downloadBeforeSend: koishi_1.Schema.boolean().default(false).description('下载后发送(避免1200错误)'),
51
+ messageBufferDelay: koishi_1.Schema.number().default(1).description('消息缓冲延迟(秒)'),
52
+ // 国内可访问的公益API配置
53
+ publicApi: koishi_1.Schema.object({
54
+ enable: koishi_1.Schema.boolean().default(true).description('启用国内公益API'),
55
+ url: koishi_1.Schema.string().default('https://api.btstu.cn/yanapi/yjx/').description('国内公益解析API地址'),
56
+ timeout: koishi_1.Schema.number().default(15000).description('API超时时间(毫秒)')
57
+ }).description('国内免费公益解析API(无需密钥)')
103
58
  });
104
- // 内置免费解析API列表(自动切换重试)
105
- const BUILTIN_APIS = {
106
- // API 1: 稳定免费的视频解析接口
107
- api1: {
108
- url: 'https://jx.jsonapi.cn/api.php',
109
- params: (url) => ({ url, type: 'json' })
110
- },
111
- // API 2: 备用解析接口
112
- api2: {
113
- url: 'https://api.vvhan.com/api/video',
114
- params: (url) => ({ url, type: 'json' })
115
- },
116
- // API 3: 兜底解析接口
117
- api3: {
118
- url: 'https://www.xiaoyangapi.com/api/video/analysis',
119
- params: (url) => ({ url, appid: '1001', appkey: 'xiaoyangapi' })
120
- }
121
- };
122
59
  const processedLinks = new Map();
123
60
  const messageQueue = new Map();
61
+ // 纯本地解析函数(兜底方案)
62
+ function parseLocal(url, platform) {
63
+ const result = {
64
+ title: '未知视频',
65
+ author: '未知作者',
66
+ description: '无简介',
67
+ like: 0,
68
+ coin: 0,
69
+ collect: 0,
70
+ share: 0,
71
+ view: 0,
72
+ danmaku: 0,
73
+ cover: '',
74
+ duration: 0,
75
+ url: url
76
+ };
77
+ // B站本地解析(提取BV号+生成封面)
78
+ if (platform === 'bilibili') {
79
+ const bvMatch = url.match(/(BV\w+|AV\d+)/);
80
+ if (bvMatch) {
81
+ const bvid = bvMatch[0];
82
+ result.title = `B站视频-${bvid}`;
83
+ result.cover = `https://i0.hdslb.com/bfs/archive/${bvid}.jpg`;
84
+ result.url = `https://www.bilibili.com/video/${bvid}`;
85
+ }
86
+ }
87
+ // 抖音本地解析
88
+ else if (platform === 'douyin') {
89
+ result.title = '抖音视频';
90
+ result.cover = 'https://p3-passport.byteacctimg.com/img/user-avatar/8888888888888888~300x300.image';
91
+ }
92
+ // 快手本地解析
93
+ else if (platform === 'kuaishou') {
94
+ result.title = '快手视频';
95
+ result.cover = 'https://www.kuaishou.com/favicon.ico';
96
+ }
97
+ return result;
98
+ }
124
99
  function apply(ctx, config) {
125
- // 创建请求实例
100
+ // 创建请求实例(适配国内环境)
126
101
  const request = axios_1.default.create({
127
102
  headers: {
128
- 'User-Agent': config.userAgent,
129
- 'Referer': 'https://www.baidu.com',
130
- 'Origin': 'https://www.baidu.com'
103
+ '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',
104
+ 'Referer': 'https://www.baidu.com'
131
105
  },
132
- timeout: config.builtinApi.timeout,
133
- decompress: true
106
+ timeout: config.publicApi.timeout,
107
+ // 禁用SSL验证(解决部分国内API证书问题)
108
+ httpsAgent: new (require('https').Agent)({ rejectUnauthorized: false })
134
109
  });
135
- // 内置API解析核心函数(自动重试)
136
- async function parseWithBuiltinApi(url, platform, retry = 0) {
137
- const apiList = Object.values(BUILTIN_APIS);
138
- const currentApi = apiList[retry % apiList.length];
110
+ // 国内公益API解析函数
111
+ async function parsePublicApi(url, platform) {
139
112
  try {
140
- const res = await request.get(currentApi.url, {
141
- params: currentApi.params(url),
142
- timeout: config.builtinApi.timeout
113
+ const res = await request.get(config.publicApi.url, {
114
+ params: { url: encodeURIComponent(url), n: '123456' },
115
+ timeout: config.publicApi.timeout
143
116
  });
117
+ // 统一解析结果格式
144
118
  const data = res.data;
145
- if (!data || (data.code && data.code !== 200 && data.code !== 0)) {
146
- throw new Error(`API返回错误:${data.msg || '未知错误'}`);
147
- }
148
- // 统一返回格式
149
- const result = {
150
- title: data.title || data.video_title || '',
151
- author: data.author || data.nickname || data.up主 || '',
152
- description: data.desc || data.description || data.content || '',
153
- cover: data.cover || data.cover_url || data.thumbnail || '',
154
- url: data.url || data.video_url || data.play || '',
119
+ if (data.code !== 200 && data.code !== 0)
120
+ throw new Error(data.msg || '解析失败');
121
+ return {
122
+ title: data.title || data.video_title || `【${platform}视频】`,
123
+ author: data.author || data.nickname || '未知作者',
124
+ description: data.desc || data.content || '无简介',
125
+ like: data.like || data.like_count || 0,
126
+ coin: data.coin || data.coin_count || 0,
127
+ collect: data.collect || data.collect_count || 0,
128
+ share: data.share || data.share_count || 0,
129
+ view: data.play || data.view_count || 0,
130
+ danmaku: data.danmaku || data.comment_count || 0,
131
+ cover: data.cover || data.thumbnail || data.img || '',
155
132
  duration: data.duration || data.time || 0,
156
- size: data.size || 0
133
+ url: data.url || data.video_url || data.play_url || url
157
134
  };
158
- // B站额外补充统计字段
159
- if (platform === 'bilibili') {
160
- return {
161
- ...result,
162
- like: data.like || data.likeCount || 0,
163
- coin: data.coin || data.coinCount || 0,
164
- collect: data.collect || data.collectCount || 0,
165
- share: data.share || data.shareCount || 0,
166
- view: data.playCount || data.view || data.views || 0,
167
- danmaku: data.danmaku || data.comment || 0
168
- };
169
- }
170
- return result;
171
135
  }
172
136
  catch (e) {
173
- // 重试逻辑
174
- if (retry < config.builtinApi.retryCount) {
175
- ctx.logger.warn(`内置API ${retry + 1} 解析失败:${e.message},重试下一个API...`);
176
- return parseWithBuiltinApi(url, platform, retry + 1);
177
- }
178
- throw new Error(`所有内置API解析失败:${e.message}`);
179
- }
180
- }
181
- // 自定义API解析函数
182
- async function parseWithCustomApi(url, platform) {
183
- const platformConfig = config[platform];
184
- if (!platformConfig.customApi) {
185
- throw new Error(`${platform} 自定义API地址未配置`);
186
- }
187
- const res = await request.get(platformConfig.customApi, {
188
- params: { url, key: platformConfig.apiKey },
189
- timeout: config.builtinApi.timeout
190
- });
191
- const data = res.data.data || res.data;
192
- const getValue = (obj, path) => {
193
- return path.split('.').reduce((o, k) => o?.[k], obj);
194
- };
195
- if (platform === 'bilibili') {
196
- const fieldMap = platformConfig.fieldMap;
197
- return {
198
- title: getValue(data, fieldMap.title) || '',
199
- author: getValue(data, fieldMap.author) || '',
200
- description: getValue(data, fieldMap.description) || '',
201
- like: getValue(data, fieldMap.like) || 0,
202
- coin: getValue(data, fieldMap.coin) || 0,
203
- collect: getValue(data, fieldMap.collect) || 0,
204
- share: getValue(data, fieldMap.share) || 0,
205
- view: getValue(data, fieldMap.view) || 0,
206
- danmaku: getValue(data, fieldMap.danmaku) || 0,
207
- cover: getValue(data, fieldMap.cover) || '',
208
- duration: getValue(data, fieldMap.duration) || 0,
209
- size: getValue(data, fieldMap.size) || 0,
210
- url: getValue(data, fieldMap.url) || ''
211
- };
212
- }
213
- else {
214
- const fieldMap = platformConfig.fieldMap;
215
- return {
216
- title: getValue(data, fieldMap.title) || '',
217
- author: getValue(data, fieldMap.author) || '',
218
- description: getValue(data, fieldMap.description) || '',
219
- cover: getValue(data, fieldMap.cover) || '',
220
- duration: getValue(data, fieldMap.duration) || 0,
221
- size: getValue(data, fieldMap.size) || 0,
222
- url: getValue(data, fieldMap.url) || ''
223
- };
137
+ ctx.logger.warn(`公益API解析失败:${e.message},使用本地解析兜底`);
138
+ return parseLocal(url, platform);
224
139
  }
225
140
  }
226
141
  // 核心解析函数
@@ -228,14 +143,13 @@ function apply(ctx, config) {
228
143
  if (!config.enable)
229
144
  return;
230
145
  // 去重逻辑
231
- const now = Date.now();
232
146
  const linkHash = crypto_1.default.createHash('md5').update(url).digest('hex');
147
+ const now = Date.now();
233
148
  if (processedLinks.has(linkHash) && now - processedLinks.get(linkHash) < config.sameLinkInterval * 1000) {
234
- ctx.logger.debug(`相同链接 ${url} 短时间内已解析,跳过`);
235
149
  return;
236
150
  }
237
151
  processedLinks.set(linkHash, now);
238
- // 发送等待提示
152
+ // 等待提示
239
153
  if (config.showWaitingTip)
240
154
  await session.send(config.waitingTipText);
241
155
  // 判断平台
@@ -243,9 +157,8 @@ function apply(ctx, config) {
243
157
  if (url.includes('bilibili') || /(BV|AV)\w+/.test(url)) {
244
158
  platform = 'bilibili';
245
159
  // BV/AV号补全链接
246
- if (/^(BV|AV)/i.test(url)) {
160
+ if (/^(BV|AV)/i.test(url))
247
161
  url = `https://www.bilibili.com/video/${url}`;
248
- }
249
162
  }
250
163
  else if (url.includes('douyin') || url.includes('dy') || url.includes('抖音')) {
251
164
  platform = 'douyin';
@@ -254,46 +167,23 @@ function apply(ctx, config) {
254
167
  platform = 'kuaishou';
255
168
  }
256
169
  else {
257
- await session.send('❌ 不支持的链接类型(仅支持B站/抖音/快手)');
258
- return;
259
- }
260
- // 解析视频信息
261
- let videoInfo;
262
- try {
263
- if (config.parserSource === 'custom' && config[platform].customApi) {
264
- videoInfo = await parseWithCustomApi(url, platform);
265
- }
266
- else {
267
- videoInfo = await parseWithBuiltinApi(url, platform);
268
- }
269
- // 校验解析结果
270
- if (!videoInfo.url) {
271
- throw new Error('解析失败:未获取到视频链接');
272
- }
273
- if (!videoInfo.title) {
274
- videoInfo.title = '未知标题';
275
- }
276
- }
277
- catch (e) {
278
- await session.send(`❌ 解析失败:${e.message}`);
279
- ctx.logger.error(`解析 ${url} 失败:`, e);
170
+ await session.send('❌ 仅支持B站/抖音/快手视频链接解析');
280
171
  return;
281
172
  }
173
+ // 解析视频信息(优先API,失败则本地)
174
+ const videoInfo = config.publicApi.enable
175
+ ? await parsePublicApi(url, platform)
176
+ : parseLocal(url, platform);
282
177
  // 时长过滤
283
178
  const duration = videoInfo.duration / 60;
284
179
  if (duration < config.minVideoDuration) {
285
- const msg = config.shortVideoTip || '❌ 视频时长过短,不解析';
180
+ const msg = config.shortVideoTip;
286
181
  return config.shortVideoUseImageParse ? await generateImageParse(videoInfo, session, platform) : await session.send(msg);
287
182
  }
288
183
  if (duration > config.maxVideoDuration) {
289
- const msg = config.longVideoTip || '❌ 视频时长过长,不解析';
184
+ const msg = config.longVideoTip;
290
185
  return config.longVideoUseImageParse ? await generateImageParse(videoInfo, session, platform) : await session.send(msg);
291
186
  }
292
- // 大小过滤
293
- if (config.maxFileSize > 0 && videoInfo.size > config.maxFileSize * 1024 * 1024) {
294
- await session.send(`❌ 视频文件过大(>${config.maxFileSize}MB),无法发送`);
295
- return;
296
- }
297
187
  // 生成回复
298
188
  await generateReply(videoInfo, session, platform);
299
189
  }
@@ -309,7 +199,7 @@ function apply(ctx, config) {
309
199
  .replace(/\${简介}/g, desc || '')
310
200
  .replace(/\${封面}/g, videoInfo.cover || '')
311
201
  .replace(/\${tab}/g, '\t');
312
- // 仅B站替换统计字段
202
+ // 仅B站显示统计字段,抖音/快手自动过滤
313
203
  if (platform === 'bilibili') {
314
204
  content = content.replace(/\${点赞}/g, videoInfo.like?.toString() || '0')
315
205
  .replace(/\${投币}/g, videoInfo.coin?.toString() || '0')
@@ -319,26 +209,19 @@ function apply(ctx, config) {
319
209
  .replace(/\${弹幕}/g, videoInfo.danmaku?.toString() || '0');
320
210
  }
321
211
  else {
322
- // 抖音/快手清空统计字段
323
- content = content.replace(/\${点赞}/g, '')
324
- .replace(/\${投币}/g, '')
325
- .replace(/\${收藏}/g, '')
326
- .replace(/\${转发}/g, '')
327
- .replace(/\${观看}/g, '')
328
- .replace(/\${弹幕}/g, '');
329
- // 清理空行和多余符号
330
- content = content.replace(/点赞:\s*\t*\s*投币:\s*\n/g, '')
212
+ // 清空抖音/快手的统计字段
213
+ content = content.replace(/\${点赞}/g, '').replace(/\${投币}/g, '')
214
+ .replace(/\${收藏}/g, '').replace(/\${转发}/g, '')
215
+ .replace(/\${观看}/g, '').replace(/\${弹幕}/g, '')
216
+ // 过滤空行和多余符号
217
+ .replace(/点赞:\s*\t*\s*投币:\s*\n/g, '')
331
218
  .replace(/收藏:\s*\t*\s*转发:\s*\n/g, '')
332
219
  .replace(/观看:\s*\t*\s*弹幕:\s*\n/g, '')
333
- .replace(/={2,}/g, (match) => match.trim() ? match : '')
334
220
  .replace(/\n+/g, '\n').trim();
335
221
  }
336
222
  // 发送解析内容
337
- const parts = content.split(/\${~~~}/);
338
- for (const p of parts) {
339
- if (p.trim())
340
- await session.send(p.trim());
341
- }
223
+ await session.send(content);
224
+ // 显示视频链接
342
225
  if (config.showVideoLink && videoInfo.url) {
343
226
  await session.send(`📥 视频链接:${videoInfo.url}`);
344
227
  }
@@ -349,19 +232,16 @@ function apply(ctx, config) {
349
232
  if (config.enableMergeForward) {
350
233
  const msgs = [];
351
234
  if (videoInfo.title)
352
- msgs.push(koishi_1.h.text(`📌 标题:${videoInfo.title}`));
235
+ msgs.push(koishi_1.h.text(`📌 ${videoInfo.title}`));
353
236
  if (videoInfo.author)
354
- msgs.push(koishi_1.h.text(`👤 作者:${videoInfo.author}`));
237
+ msgs.push(koishi_1.h.text(`👤 ${videoInfo.author}`));
355
238
  if (videoInfo.cover)
356
239
  msgs.push(koishi_1.h.image(videoInfo.cover));
357
240
  if (videoInfo.url) {
358
241
  if (config.downloadBeforeSend) {
359
242
  try {
360
- const response = await request.get(videoInfo.url, {
361
- responseType: 'stream',
362
- timeout: 60000
363
- });
364
- msgs.push(koishi_1.h.video(response.data));
243
+ const res = await request.get(videoInfo.url, { responseType: 'stream', timeout: 60000 });
244
+ msgs.push(koishi_1.h.video(res.data));
365
245
  }
366
246
  catch (e) {
367
247
  msgs.push(koishi_1.h.text(`📥 视频链接:${videoInfo.url}`));
@@ -378,8 +258,7 @@ function apply(ctx, config) {
378
258
  }
379
259
  }
380
260
  catch (e) {
381
- await session.send(`❌ 消息发送失败:${e.message || '请稍后重试'}`);
382
- ctx.logger.error(`发送消息失败:`, e);
261
+ await session.send(`❌ 消息发送失败:${e.message}`);
383
262
  }
384
263
  }
385
264
  // 监听消息
@@ -387,13 +266,11 @@ function apply(ctx, config) {
387
266
  if (!config.enable)
388
267
  return;
389
268
  const content = session.content.trim();
390
- // 匹配视频链接(支持短链接/长链接/BV/AV号)
391
269
  const reg = /(https?:\/\/\S+)|(BV\w+)|(AV\d+)/gi;
392
270
  const matches = [...content.matchAll(reg)];
393
271
  if (!matches.length)
394
272
  return;
395
273
  const uid = session.userId;
396
- // 消息缓冲,避免重复解析
397
274
  if (config.messageBufferDelay > 0) {
398
275
  if (!messageQueue.has(uid)) {
399
276
  messageQueue.set(uid, []);
@@ -411,20 +288,13 @@ function apply(ctx, config) {
411
288
  await parseVideo(m[0], session);
412
289
  }
413
290
  });
414
- // 定时清理过期链接记录
291
+ // 定时清理过期链接
415
292
  setInterval(() => {
416
293
  const now = Date.now();
417
- let count = 0;
418
294
  for (const [k, t] of processedLinks) {
419
- if (now - t > 86400000) {
295
+ if (now - t > 86400000)
420
296
  processedLinks.delete(k);
421
- count++;
422
- }
423
- }
424
- if (count > 0) {
425
- ctx.logger.debug(`清理了 ${count} 条过期链接记录`);
426
297
  }
427
298
  }, 3600000);
428
- // 插件启动日志
429
- ctx.logger.info(`✅ 视频解析插件已启动(解析来源:${config.parserSource})`);
299
+ ctx.logger.info('✅ 视频解析插件已启动(移除字段映射+超大输入框)');
430
300
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-video-parser-all",
3
3
  "description": "Koishi 视频解析插件,支持抖音/快手/B站链接解析,可自定义API和解析规则",
4
- "version": "0.0.3",
4
+ "version": "0.0.4",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [