koishi-plugin-video-parser-all 0.5.1 → 0.5.3

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
@@ -61,4 +61,32 @@ export declare const Config: Schema<{
61
61
  } & {
62
62
  autoClearCacheInterval: number;
63
63
  }>;
64
+ export declare enum ErrorCode {
65
+ SUCCESS = 0,
66
+ UNKNOWN_ERROR = 1000,
67
+ UNSUPPORTED_PLATFORM = 1001,
68
+ PLATFORM_API_NOT_CONFIGURED = 1002,
69
+ REQUEST_TIMEOUT = 1003,
70
+ NETWORK_ERROR = 1004,
71
+ DUPLICATE_PARSE = 1005,
72
+ INVALID_URL = 1006,
73
+ API_RETURN_ERROR = 2000,
74
+ API_DATA_PARSE_FAILED = 2001,
75
+ API_EMPTY_RESPONSE = 2002,
76
+ API_INVALID_RESPONSE = 2003,
77
+ VIDEO_DOWNLOAD_FAILED = 3000,
78
+ VIDEO_SIZE_EXCEEDED = 3001,
79
+ UNSUPPORTED_CONTENT_TYPE = 3002,
80
+ NO_VIDEO_FOUND = 3003,
81
+ NO_IMAGE_FOUND = 3004,
82
+ MESSAGE_SEND_FAILED = 4000,
83
+ MESSAGE_SEND_TIMEOUT = 4001,
84
+ FORWARD_MESSAGE_FAILED = 4002,
85
+ DOUYIN_PARSE_FAILED = 5001,
86
+ XIAOHONGSHU_PARSE_FAILED = 5002,
87
+ BILIBILI_PARSE_FAILED = 5003,
88
+ KUAISHOU_PARSE_FAILED = 5004,
89
+ WEIBO_PARSE_FAILED = 5005
90
+ }
91
+ export declare const ErrorMessageMap: Record<ErrorCode, string>;
64
92
  export declare function apply(ctx: Context, config: any): void;
package/lib/index.js CHANGED
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.Config = exports.name = void 0;
6
+ exports.ErrorMessageMap = exports.ErrorCode = exports.Config = exports.name = void 0;
7
7
  exports.apply = apply;
8
8
  const koishi_1 = require("koishi");
9
9
  const axios_1 = __importDefault(require("axios"));
@@ -22,7 +22,7 @@ exports.Config = koishi_1.Schema.intersect([
22
22
  sameLinkInterval: koishi_1.Schema.number().min(0).default(180).description('相同链接重复解析间隔(秒)'),
23
23
  }).description('基础设置'),
24
24
  koishi_1.Schema.object({
25
- unifiedMessageFormat: koishi_1.Schema.string().role('textarea').default('标题:${标题}\n作者:${作者}\n简介:${简介}\n时长:${视频时长}\n点赞:${点赞数}\n投币:${投币数}\n收藏:${收藏数}\n转发:${转发数}\n播放:${播放数}').description('统一消息格式(B站会显示投币,其他平台自动隐藏;无法获取的变量会自动隐藏)'),
25
+ unifiedMessageFormat: koishi_1.Schema.string().role('textarea').default('标题:${标题}\n作者:${作者}\n简介:${简介}\n时长:${视频时长}\n点赞:${点赞数}\n投币:${投币数}\n收藏:${收藏数}\n转发:${转发数}\n播放:${播放数}\n评论:${评论数}\n音乐:${音乐名}').description('统一消息格式(无法获取的变量会自动隐藏)\n变量介绍:\n${标题} - 内容标题\n${作者} - 作者名称\n${简介} - 内容简介\n${视频时长} - 视频时长\n${点赞数} - 点赞数量\n${投币数} - 投币数量(仅B站)\n${收藏数} - 收藏数量\n${转发数} - 转发/分享数量\n${播放数} - 播放数量\n${评论数} - 评论数量\n${音乐名} - 背景音乐名称'),
26
26
  }).description('统一消息格式'),
27
27
  koishi_1.Schema.object({
28
28
  showImageText: koishi_1.Schema.boolean().default(true).description('显示图文内容'),
@@ -43,23 +43,78 @@ exports.Config = koishi_1.Schema.intersect([
43
43
  }).description('错误与重试设置'),
44
44
  koishi_1.Schema.object({
45
45
  enableForward: koishi_1.Schema.boolean().default(false).description('启用合并转发(仅OneBot平台)'),
46
- downloadVideoBeforeSend: koishi_1.Schema.boolean().default(false).description('发送前先下载视频(避免链接失效)'),
46
+ downloadVideoBeforeSend: koishi_1.Schema.boolean().default(false).description('发送前先下载视频'),
47
47
  maxVideoSize: koishi_1.Schema.number().min(0).default(0).description('最大视频大小限制(MB,0为不限制)'),
48
48
  downloadThreads: koishi_1.Schema.number().min(0).max(10).default(0).description('多线程下载线程数(0为不使用多线程,1-10为启用对应线程数)'),
49
- }).description('发送方式设置(说明:视频大小超过限制将只发送链接;多线程下载可提升速度但可能增加服务器负载)'),
49
+ }).description('发送方式设置'),
50
50
  koishi_1.Schema.object({
51
51
  messageBufferDelay: koishi_1.Schema.number().min(0).default(0).description('消息缓冲延迟(毫秒,批量处理链接)'),
52
52
  }).description('消息处理设置'),
53
53
  koishi_1.Schema.object({
54
54
  autoClearCacheInterval: koishi_1.Schema.number().min(0).default(0).description('自动清理缓存间隔(分钟,0为关闭)'),
55
- }).description('缓存清理设置(说明:开启自动清理可定期删除过期的临时视频文件和解析缓存)'),
55
+ }).description('缓存清理设置'),
56
56
  ]);
57
+ var ErrorCode;
58
+ (function (ErrorCode) {
59
+ ErrorCode[ErrorCode["SUCCESS"] = 0] = "SUCCESS";
60
+ ErrorCode[ErrorCode["UNKNOWN_ERROR"] = 1000] = "UNKNOWN_ERROR";
61
+ ErrorCode[ErrorCode["UNSUPPORTED_PLATFORM"] = 1001] = "UNSUPPORTED_PLATFORM";
62
+ ErrorCode[ErrorCode["PLATFORM_API_NOT_CONFIGURED"] = 1002] = "PLATFORM_API_NOT_CONFIGURED";
63
+ ErrorCode[ErrorCode["REQUEST_TIMEOUT"] = 1003] = "REQUEST_TIMEOUT";
64
+ ErrorCode[ErrorCode["NETWORK_ERROR"] = 1004] = "NETWORK_ERROR";
65
+ ErrorCode[ErrorCode["DUPLICATE_PARSE"] = 1005] = "DUPLICATE_PARSE";
66
+ ErrorCode[ErrorCode["INVALID_URL"] = 1006] = "INVALID_URL";
67
+ ErrorCode[ErrorCode["API_RETURN_ERROR"] = 2000] = "API_RETURN_ERROR";
68
+ ErrorCode[ErrorCode["API_DATA_PARSE_FAILED"] = 2001] = "API_DATA_PARSE_FAILED";
69
+ ErrorCode[ErrorCode["API_EMPTY_RESPONSE"] = 2002] = "API_EMPTY_RESPONSE";
70
+ ErrorCode[ErrorCode["API_INVALID_RESPONSE"] = 2003] = "API_INVALID_RESPONSE";
71
+ ErrorCode[ErrorCode["VIDEO_DOWNLOAD_FAILED"] = 3000] = "VIDEO_DOWNLOAD_FAILED";
72
+ ErrorCode[ErrorCode["VIDEO_SIZE_EXCEEDED"] = 3001] = "VIDEO_SIZE_EXCEEDED";
73
+ ErrorCode[ErrorCode["UNSUPPORTED_CONTENT_TYPE"] = 3002] = "UNSUPPORTED_CONTENT_TYPE";
74
+ ErrorCode[ErrorCode["NO_VIDEO_FOUND"] = 3003] = "NO_VIDEO_FOUND";
75
+ ErrorCode[ErrorCode["NO_IMAGE_FOUND"] = 3004] = "NO_IMAGE_FOUND";
76
+ ErrorCode[ErrorCode["MESSAGE_SEND_FAILED"] = 4000] = "MESSAGE_SEND_FAILED";
77
+ ErrorCode[ErrorCode["MESSAGE_SEND_TIMEOUT"] = 4001] = "MESSAGE_SEND_TIMEOUT";
78
+ ErrorCode[ErrorCode["FORWARD_MESSAGE_FAILED"] = 4002] = "FORWARD_MESSAGE_FAILED";
79
+ ErrorCode[ErrorCode["DOUYIN_PARSE_FAILED"] = 5001] = "DOUYIN_PARSE_FAILED";
80
+ ErrorCode[ErrorCode["XIAOHONGSHU_PARSE_FAILED"] = 5002] = "XIAOHONGSHU_PARSE_FAILED";
81
+ ErrorCode[ErrorCode["BILIBILI_PARSE_FAILED"] = 5003] = "BILIBILI_PARSE_FAILED";
82
+ ErrorCode[ErrorCode["KUAISHOU_PARSE_FAILED"] = 5004] = "KUAISHOU_PARSE_FAILED";
83
+ ErrorCode[ErrorCode["WEIBO_PARSE_FAILED"] = 5005] = "WEIBO_PARSE_FAILED";
84
+ })(ErrorCode || (exports.ErrorCode = ErrorCode = {}));
85
+ exports.ErrorMessageMap = {
86
+ [ErrorCode.SUCCESS]: '操作成功',
87
+ [ErrorCode.UNKNOWN_ERROR]: '未知错误',
88
+ [ErrorCode.UNSUPPORTED_PLATFORM]: '不支持该平台链接',
89
+ [ErrorCode.PLATFORM_API_NOT_CONFIGURED]: '该平台暂未配置解析接口',
90
+ [ErrorCode.REQUEST_TIMEOUT]: '请求超时',
91
+ [ErrorCode.NETWORK_ERROR]: '网络请求失败',
92
+ [ErrorCode.DUPLICATE_PARSE]: '请勿重复解析(相同链接解析间隔未到)',
93
+ [ErrorCode.INVALID_URL]: '无效的链接格式',
94
+ [ErrorCode.API_RETURN_ERROR]: 'API返回错误',
95
+ [ErrorCode.API_DATA_PARSE_FAILED]: '数据解析异常',
96
+ [ErrorCode.API_EMPTY_RESPONSE]: 'API返回空数据',
97
+ [ErrorCode.API_INVALID_RESPONSE]: 'API返回无效格式',
98
+ [ErrorCode.VIDEO_DOWNLOAD_FAILED]: '视频下载失败',
99
+ [ErrorCode.VIDEO_SIZE_EXCEEDED]: '视频大小超过限制',
100
+ [ErrorCode.UNSUPPORTED_CONTENT_TYPE]: '不支持的内容类型',
101
+ [ErrorCode.NO_VIDEO_FOUND]: '未找到视频内容',
102
+ [ErrorCode.NO_IMAGE_FOUND]: '未找到图片内容',
103
+ [ErrorCode.MESSAGE_SEND_FAILED]: '消息发送失败',
104
+ [ErrorCode.MESSAGE_SEND_TIMEOUT]: '消息发送超时',
105
+ [ErrorCode.FORWARD_MESSAGE_FAILED]: '合并转发失败',
106
+ [ErrorCode.DOUYIN_PARSE_FAILED]: '抖音链接解析失败',
107
+ [ErrorCode.XIAOHONGSHU_PARSE_FAILED]: '小红书链接解析失败',
108
+ [ErrorCode.BILIBILI_PARSE_FAILED]: 'B站链接解析失败',
109
+ [ErrorCode.KUAISHOU_PARSE_FAILED]: '快手链接解析失败',
110
+ [ErrorCode.WEIBO_PARSE_FAILED]: '微博链接解析失败',
111
+ };
57
112
  const processed = new Map();
58
113
  const linkBuffer = new Map();
59
114
  const logger = new koishi_1.Logger(exports.name);
60
115
  const PLATFORM_KEYWORDS = {
61
116
  bilibili: ['bilibili', 'b23', 'B站', 'www.bilibili.com', 'm.bilibili.com', '哔哩哔哩', 'bilibili.com/opus', 'bilibili.com/video', 'b23.tv', 't.bilibili.com', 'bilibili.com/bangumi'],
62
- kuaishou: ['kuaishou', '快手', 'v.kuaishou.com', 'www.kuaishou.com', 'kwimgs.com', 'kuaishou.com/app', 'kuaishou.com/short-video'],
117
+ kuaishou: ['kuaishou', '快手', 'v.kuaishou.com', 'www.kuaishou.com', 'kwimgs.com', 'kuaishou.com/app'],
63
118
  xiaohongshu: ['xiaohongshu', '小红书', 'xhslink.com', 'xiaohongshu.com', 'xhscdn.com', 'xiaohongshu.com/explore', 'xhslink.com/'],
64
119
  weibo: ['weibo', '微博', 'weibo.com', 'video.weibo.com', 'm.weibo.cn', 'weibo.com/tv/show', 'weibo.com/feed'],
65
120
  toutiao: ['toutiao', '今日头条', 'm.toutiao.com', 'toutiao.com', 'ixigua.com', 'toutiao.com/video', 'ixigua.com/i'],
@@ -69,16 +124,40 @@ const PLATFORM_KEYWORDS = {
69
124
  zuiyou: ['zuiyou', '最右', 'xiaochuankeji.cn', 'izuiyou.com', 'izuiyou.com/topic']
70
125
  };
71
126
  const API_CONFIG = {
72
- bilibili: 'https://api.xingzhige.com/API/b_parse/',
73
- douyin: 'https://api.bugpk.com/api/short_videos',
74
- kuaishou: 'https://api.bugpk.com/api/short_videos',
75
- xiaohongshu: 'https://api.bugpk.com/api/short_videos',
127
+ bilibili: 'https://api.bugpk.com/api/bilibili',
128
+ douyin: 'https://api.bugpk.com/api/dyjx',
129
+ kuaishou: 'https://api.bugpk.com/api/ksjx',
130
+ xiaohongshu: 'https://api.bugpk.com/api/xhsjx',
76
131
  weibo: 'https://api.bugpk.com/api/weibo',
77
- zuiyou: 'https://api.bugpk.com/api/short_videos',
78
- pipixia: 'https://api.bugpk.com/api/short_videos',
79
- pipigx: 'https://api.bugpk.com/api/short_videos',
80
- toutiao: 'https://api.bugpk.com/api/toutiao'
132
+ toutiao: 'https://api.bugpk.com/api/toutiao',
133
+ pipigx: 'https://api.bugpk.com/api/pipigx',
134
+ pipixia: 'https://api.bugpk.com/api/ppx',
135
+ zuiyou: 'https://api.bugpk.com/api/zuiyou'
136
+ };
137
+ const PLATFORM_ERROR_CODE_MAP = {
138
+ douyin: ErrorCode.DOUYIN_PARSE_FAILED,
139
+ xiaohongshu: ErrorCode.XIAOHONGSHU_PARSE_FAILED,
140
+ bilibili: ErrorCode.BILIBILI_PARSE_FAILED,
141
+ kuaishou: ErrorCode.KUAISHOU_PARSE_FAILED,
142
+ weibo: ErrorCode.WEIBO_PARSE_FAILED
143
+ };
144
+ const VARIABLE_MAPPING = {
145
+ '标题': ['title', 'Title', 'TITLE'],
146
+ '作者': ['author.name', 'author', 'name', 'Author', 'Name'],
147
+ '简介': ['desc', 'description', 'Desc', 'Description', 'content', 'Content'],
148
+ '视频时长': ['duration', 'Duration', 'time', 'Time'],
149
+ '点赞数': ['like', 'Like', 'attitudes_count', 'digg_count', 'praise'],
150
+ '投币数': ['coin', 'Coin', 'bi', 'Bi'],
151
+ '收藏数': ['collect', 'Collect', 'favorite', 'Favorite', 'star', 'Star'],
152
+ '转发数': ['share', 'Share', 'forward', 'Forward', 'repost'],
153
+ '播放数': ['view', 'View', 'play_count', 'PlayCount', 'play'],
154
+ '评论数': ['comment', 'Comment', 'comments_count', 'comment_count', 'discuss'],
155
+ '音乐名': ['music.title', 'music_name', 'audio_name', 'sound_name']
81
156
  };
157
+ function getErrorInfo(code, detail) {
158
+ const baseMsg = exports.ErrorMessageMap[code] || exports.ErrorMessageMap[ErrorCode.UNKNOWN_ERROR];
159
+ return detail ? `[错误码: ${code}] ${baseMsg}:${detail}` : `[错误码: ${code}] ${baseMsg}`;
160
+ }
82
161
  function getErrorMessage(error) {
83
162
  if (error instanceof Error)
84
163
  return error.message;
@@ -143,11 +222,11 @@ async function downloadVideo(url, filename, userAgent, maxSize, threads) {
143
222
  const filePath = path_1.default.join(dir, `${filename}.mp4`);
144
223
  try {
145
224
  if (url.endsWith('.m4a') || url.endsWith('.mp3')) {
146
- throw new Error('不支持音频');
225
+ return { filePath: '', code: ErrorCode.UNSUPPORTED_CONTENT_TYPE };
147
226
  }
148
227
  const fileSize = await getFileSize(url, userAgent);
149
228
  if (maxSize > 0 && fileSize > maxSize) {
150
- throw new Error(`视频大小${fileSize}MB超过限制${maxSize}MB`);
229
+ return { filePath: '', code: ErrorCode.VIDEO_SIZE_EXCEEDED };
151
230
  }
152
231
  if (threads <= 0 || fileSize === 0) {
153
232
  const response = await (0, axios_1.default)({
@@ -161,7 +240,7 @@ async function downloadVideo(url, filename, userAgent, maxSize, threads) {
161
240
  });
162
241
  const writeStream = fs_1.default.createWriteStream(filePath);
163
242
  await (0, promises_1.pipeline)(response.data, writeStream);
164
- return filePath;
243
+ return { filePath, code: ErrorCode.SUCCESS };
165
244
  }
166
245
  const totalSize = fileSize * 1024 * 1024;
167
246
  const chunkSize = Math.ceil(totalSize / threads);
@@ -187,7 +266,7 @@ async function downloadVideo(url, filename, userAgent, maxSize, threads) {
187
266
  fs_1.default.unlinkSync(result.filePath);
188
267
  }
189
268
  writeStream.end();
190
- return filePath;
269
+ return { filePath, code: ErrorCode.SUCCESS };
191
270
  }
192
271
  catch (error) {
193
272
  if (fs_1.default.existsSync(filePath)) {
@@ -200,46 +279,9 @@ async function downloadVideo(url, filename, userAgent, maxSize, threads) {
200
279
  }
201
280
  catch (e) { }
202
281
  });
203
- throw error;
282
+ return { filePath: '', code: ErrorCode.VIDEO_DOWNLOAD_FAILED };
204
283
  }
205
284
  }
206
- function parseXingzhigeData(resData, platform) {
207
- const result = {
208
- title: '',
209
- author: '未知作者',
210
- desc: '',
211
- cover: '',
212
- video: '',
213
- images: [],
214
- stat: {
215
- like: 0,
216
- coin: 0,
217
- favorite: 0,
218
- share: 0,
219
- view: 0,
220
- duration: '00:00'
221
- },
222
- type: 'video'
223
- };
224
- if (platform === 'bilibili') {
225
- const d = resData.data || resData;
226
- result.title = d.video?.title || d.title || '';
227
- result.author = d.owner?.name || d.name || '未知UP主';
228
- result.desc = d.video?.desc || d.desc || '';
229
- result.cover = d.video?.fm || d.fm || '';
230
- result.video = d.video?.url || d.url || '';
231
- result.stat = {
232
- view: d.stat?.view || 0,
233
- favorite: d.stat?.favorite || 0,
234
- like: d.stat?.like || 0,
235
- coin: d.stat?.coin || 0,
236
- share: d.stat?.share || 0,
237
- duration: formatDuration(d.duration || 0)
238
- };
239
- result.duration = d.duration || 0;
240
- }
241
- return result;
242
- }
243
285
  function extractUrl(content) {
244
286
  let urlMatches = content.match(/https?:\/\/[^\s\"\'\>]+/gi) || [];
245
287
  return urlMatches.filter(url => {
@@ -288,8 +330,16 @@ async function resolveShortUrl(url) {
288
330
  return url;
289
331
  }
290
332
  }
291
- function formatDuration(seconds) {
292
- if (!seconds)
333
+ function formatDuration(input) {
334
+ if (!input || input === 0 || input === '0' || input === '00:00')
335
+ return '00:00';
336
+ if (typeof input === 'string') {
337
+ if (input.includes(':'))
338
+ return input;
339
+ input = Number(input);
340
+ }
341
+ const seconds = Math.floor(Number(input));
342
+ if (isNaN(seconds) || seconds <= 0)
293
343
  return '00:00';
294
344
  const hours = Math.floor(seconds / 3600);
295
345
  const minutes = Math.floor((seconds % 3600) / 60);
@@ -298,39 +348,86 @@ function formatDuration(seconds) {
298
348
  ? `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`
299
349
  : `${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
300
350
  }
301
- function parseData(data, maxDescLength, platform) {
351
+ function getNestedValue(obj, path) {
352
+ if (!obj || typeof obj !== 'object')
353
+ return undefined;
354
+ const keys = path.split('.');
355
+ let value = obj;
356
+ for (const key of keys) {
357
+ if (value === null || value === undefined)
358
+ return undefined;
359
+ value = value[key];
360
+ }
361
+ return value;
362
+ }
363
+ function findValueInObject(obj, keys) {
364
+ if (!obj || typeof obj !== 'object')
365
+ return undefined;
366
+ for (const key of keys) {
367
+ if (key.includes('.')) {
368
+ const value = getNestedValue(obj, key);
369
+ if (value !== undefined && value !== null && value !== '')
370
+ return value;
371
+ }
372
+ else {
373
+ if (obj[key] !== undefined && obj[key] !== null && obj[key] !== '')
374
+ return obj[key];
375
+ const lowerKey = key.toLowerCase();
376
+ for (const objKey of Object.keys(obj)) {
377
+ if (objKey.toLowerCase() === lowerKey) {
378
+ return obj[objKey];
379
+ }
380
+ }
381
+ }
382
+ }
383
+ return undefined;
384
+ }
385
+ function parseData(rawResponse, maxDescLength, platform) {
386
+ let data = rawResponse;
387
+ if (data.data) {
388
+ data = data.data;
389
+ }
390
+ const stat = {};
391
+ Object.entries(VARIABLE_MAPPING).forEach(([varName, keys]) => {
392
+ const value = findValueInObject(data, keys);
393
+ if (value !== undefined) {
394
+ stat[varName] = value;
395
+ }
396
+ });
302
397
  let type = data.type || 'video';
303
- let title = data.title || data.desc || '无标题';
304
- let author = data.author || data.name || '未知作者';
305
- let desc = (data.desc || data.description || title).slice(0, maxDescLength);
306
- let cover = data.cover || data.imgurl || data.pic || data.fm || '';
307
- let images = data.images || [];
308
- let video = data.video || data.url || data.playUrl || '';
309
- let duration = data.duration || 0;
310
- let stat = data.stat || {
311
- like: 0,
312
- coin: 0,
313
- favorite: 0,
314
- share: 0,
315
- view: 0,
316
- duration: '00:00'
317
- };
318
- const durationFormatted = formatDuration(duration);
319
- if (platform === 'bilibili' && data.data) {
320
- title = data.data.title || title;
321
- author = data.data.owner?.name || '未知作者';
322
- desc = (data.data.desc || title).slice(0, maxDescLength);
323
- cover = data.data.pic || cover;
324
- duration = data.data.duration || 0;
325
- video = data.playUrl || video;
326
- stat = data.data.stat || stat;
398
+ const title = findValueInObject(data, ['title']) || '无标题';
399
+ const author = findValueInObject(data, ['author.name', 'author']) || '未知作者';
400
+ const desc = (findValueInObject(data, ['desc', 'description', 'content']) || title).toString().slice(0, maxDescLength);
401
+ const cover = findValueInObject(data, ['cover', 'imgurl', 'pic', 'thumbnail']) || '';
402
+ let images = findValueInObject(data, ['imgurl', 'images', 'pics']) || [];
403
+ if (!Array.isArray(images))
404
+ images = [images];
405
+ const videoUrls = [
406
+ findValueInObject(data, ['url']),
407
+ findValueInObject(data, ['download_url']),
408
+ findValueInObject(data, ['video_backup']),
409
+ findValueInObject(data, ['playUrl']),
410
+ findValueInObject(data, ['video_url'])
411
+ ];
412
+ let video = '';
413
+ if (Array.isArray(videoUrls[2])) {
414
+ video = videoUrls[2][0]?.url || '';
415
+ }
416
+ else {
417
+ for (const url of videoUrls) {
418
+ if (url && typeof url === 'string' && url.trim() !== '') {
419
+ video = url;
420
+ break;
421
+ }
422
+ }
327
423
  }
328
- if (images.length > 0 && !video)
329
- type = 'image';
330
- if (video && (video.endsWith('.m4a') || video.endsWith('.mp3')))
331
- video = '';
424
+ const durationValue = findValueInObject(data, ['duration']);
425
+ const duration = typeof durationValue === 'number' ? durationValue : parseInt(durationValue) || 0;
426
+ const durationFormatted = formatDuration(durationValue || 0);
427
+ const live_photo = data.live_photo || [];
332
428
  return {
333
- type,
429
+ type: type,
430
+ rawData: rawResponse,
334
431
  title,
335
432
  author,
336
433
  desc,
@@ -339,14 +436,8 @@ function parseData(data, maxDescLength, platform) {
339
436
  video,
340
437
  duration,
341
438
  durationFormatted,
342
- stat: {
343
- like: stat.like || 0,
344
- coin: stat.coin || 0,
345
- favorite: stat.favorite || 0,
346
- share: stat.share || 0,
347
- view: stat.view || 0,
348
- duration: durationFormatted
349
- }
439
+ stat,
440
+ live_photo
350
441
  };
351
442
  }
352
443
  function generateFormattedText(platform, parseData, config) {
@@ -354,28 +445,31 @@ function generateFormattedText(platform, parseData, config) {
354
445
  if (platform !== 'bilibili') {
355
446
  format = format.replace(/投币:\$\{投币数\}\n?/g, '');
356
447
  }
357
- const variables = {
358
- '标题': parseData.title || '',
359
- '作者': parseData.author || '',
360
- '简介': parseData.desc || '',
361
- '视频时长': parseData.stat.duration || '',
362
- '点赞数': parseData.stat.like > 0 ? parseData.stat.like : '',
363
- '投币数': parseData.stat.coin > 0 ? parseData.stat.coin : '',
364
- '收藏数': parseData.stat.favorite > 0 ? parseData.stat.favorite : '',
365
- '转发数': parseData.stat.share > 0 ? parseData.stat.share : '',
366
- '播放数': parseData.stat.view > 0 ? parseData.stat.view : ''
367
- };
368
448
  let result = format;
369
- Object.entries(variables).forEach(([key, value]) => {
370
- const regex = new RegExp(`${key}:\\$\\{${key}\\\}([\\n]?)`, 'g');
371
- if (!value) {
372
- result = result.replace(regex, '');
373
- }
374
- else {
375
- result = result.replace(`$\{${key}\}`, value);
449
+ const formatLines = result.split('\n');
450
+ const validLines = [];
451
+ formatLines.forEach((line) => {
452
+ let isValid = true;
453
+ let processedLine = line;
454
+ const varMatches = line.match(/\$\{([^}]+)\}/g) || [];
455
+ varMatches.forEach((varMatch) => {
456
+ const varName = varMatch.replace(/\$\{|\}/g, '');
457
+ const value = parseData.stat[varName];
458
+ if (value === undefined || value === null || value === '' || value === 0 || value === '00:00') {
459
+ isValid = false;
460
+ }
461
+ else {
462
+ processedLine = processedLine.replace(varMatch, String(value));
463
+ }
464
+ });
465
+ if (isValid && processedLine.trim() !== '') {
466
+ validLines.push(processedLine);
376
467
  }
377
468
  });
378
- result = result.replace(/\n+/g, '\n').trim();
469
+ result = validLines.join('\n').trim();
470
+ if (!result) {
471
+ result = `标题:${parseData.title}\n作者:${parseData.author}\n简介:${parseData.desc}`;
472
+ }
379
473
  return result;
380
474
  }
381
475
  function clearAllCache() {
@@ -422,62 +516,106 @@ function apply(ctx, config) {
422
516
  const realUrl = await resolveShortUrl(url);
423
517
  const platform = getPlatformType(realUrl);
424
518
  if (!platform) {
425
- logger.error(`不支持该平台: ${platform || '未知'}, URL: ${url}`);
426
- return { data: null, msg: '不支持该平台链接' };
519
+ const code = ErrorCode.UNSUPPORTED_PLATFORM;
520
+ const msg = getErrorInfo(code, url);
521
+ logger.error(`[${code}] ${exports.ErrorMessageMap[code]}: ${url}`);
522
+ return { data: null, code, msg };
427
523
  }
428
524
  const apiUrl = API_CONFIG[platform];
429
525
  if (!apiUrl) {
430
- logger.error(`未配置该平台API: ${platform}, URL: ${url}`);
431
- return { data: null, msg: '该平台暂未配置解析接口' };
526
+ const code = ErrorCode.PLATFORM_API_NOT_CONFIGURED;
527
+ const msg = getErrorInfo(code, platform);
528
+ logger.error(`[${code}] ${exports.ErrorMessageMap[code]}: ${platform}`);
529
+ return { data: null, code, msg };
432
530
  }
433
531
  try {
434
- const res = await http.get(apiUrl, { params: { url: realUrl } });
435
- let parseResult = null;
436
- if (platform === 'bilibili') {
437
- const xgData = parseXingzhigeData(res.data, platform);
438
- parseResult = parseData(xgData, config.maxDescLength, platform);
532
+ const res = await http.get(apiUrl, {
533
+ params: { url: realUrl },
534
+ timeout: config.timeout
535
+ });
536
+ if (res.data.code !== 200 && res.data.code !== 0) {
537
+ const apiErrorMsg = res.data.msg || '解析失败';
538
+ const platformCode = PLATFORM_ERROR_CODE_MAP[platform] || ErrorCode.API_RETURN_ERROR;
539
+ const code = platformCode;
540
+ const msg = getErrorInfo(code, apiErrorMsg);
541
+ logger.error(`[${code}] API返回错误: ${platform}, URL: ${url}, 错误: ${apiErrorMsg}`);
542
+ return { data: null, code, msg };
439
543
  }
440
- else {
441
- if ((res.data.code === 200 || res.data.code === 0) && res.data.data) {
442
- parseResult = parseData(res.data.data, config.maxDescLength, platform);
544
+ try {
545
+ const parseResult = parseData(res.data, config.maxDescLength, platform);
546
+ if (!parseResult.video && !parseResult.images.length && !parseResult.live_photo?.length) {
547
+ const code = ErrorCode.NO_VIDEO_FOUND;
548
+ const msg = getErrorInfo(code, '未找到视频或图片内容');
549
+ logger.warn(`[${code}] 解析成功但无有效内容: ${platform}, URL: ${url}`);
550
+ return { data: null, code, msg };
443
551
  }
552
+ logger.info(`[${ErrorCode.SUCCESS}] ${platform}解析成功: ${url}`);
553
+ return {
554
+ data: parseResult,
555
+ code: ErrorCode.SUCCESS,
556
+ msg: getErrorInfo(ErrorCode.SUCCESS)
557
+ };
444
558
  }
445
- if (parseResult) {
446
- return { data: parseResult, msg: `${platform}解析成功` };
447
- }
448
- else {
449
- logger.error(`解析返回数据无效: ${platform}, URL: ${url}`);
450
- return { data: null, msg: '解析失败,返回数据无效' };
559
+ catch (parseError) {
560
+ const errorMsg = getErrorMessage(parseError);
561
+ const code = ErrorCode.API_DATA_PARSE_FAILED;
562
+ const msg = getErrorInfo(code, errorMsg);
563
+ logger.error(`[${code}] 解析数据失败: ${platform}, URL: ${url}, 错误: ${errorMsg}`);
564
+ return { data: null, code, msg };
451
565
  }
452
566
  }
453
567
  catch (error) {
454
- logger.error(`解析请求失败: ${platform}, URL: ${url}, 错误: ${getErrorMessage(error)}`);
455
- return { data: null, msg: '解析失败,请稍后重试' };
568
+ const errorMsg = getErrorMessage(error);
569
+ let code = ErrorCode.UNKNOWN_ERROR;
570
+ if (errorMsg.includes('timeout')) {
571
+ code = ErrorCode.REQUEST_TIMEOUT;
572
+ }
573
+ else if (errorMsg.includes('Network') || errorMsg.includes('network')) {
574
+ code = ErrorCode.NETWORK_ERROR;
575
+ }
576
+ else {
577
+ code = ErrorCode.NETWORK_ERROR;
578
+ }
579
+ const msg = getErrorInfo(code, errorMsg);
580
+ logger.error(`[${code}] 解析请求失败: ${platform}, URL: ${url}, 错误: ${errorMsg}`);
581
+ return { data: null, code, msg };
456
582
  }
457
583
  }
458
584
  async function processSingleUrl(session, url) {
459
585
  const hash = crypto_1.default.createHash('md5').update(url).digest('hex');
460
586
  const now = Date.now();
461
587
  if (processed.get(hash) && now - processed.get(hash) < config.sameLinkInterval * 1000) {
462
- return { data: null, msg: '请勿重复解析' };
588
+ const code = ErrorCode.DUPLICATE_PARSE;
589
+ const msg = getErrorInfo(code);
590
+ return { data: null, code, msg };
463
591
  }
464
592
  processed.set(hash, now);
465
593
  const result = await parse(url);
466
594
  if (!result.data)
467
- return { data: null, msg: result.msg };
595
+ return { data: null, code: result.code, msg: result.msg };
468
596
  const parseData = result.data;
469
597
  const platform = getPlatformType(url);
470
598
  const text = generateFormattedText(platform, parseData, config);
471
599
  return {
472
- data: { text, cover: parseData.cover, images: parseData.images, video: parseData.video, type: parseData.type },
473
- msg: 'ok'
600
+ data: {
601
+ text,
602
+ cover: parseData.cover,
603
+ images: parseData.images,
604
+ video: parseData.video,
605
+ type: parseData.type,
606
+ live_photo: parseData.live_photo
607
+ },
608
+ code: ErrorCode.SUCCESS,
609
+ msg: getErrorInfo(ErrorCode.SUCCESS)
474
610
  };
475
611
  }
476
612
  async function sendTimeout(session, content) {
477
613
  if (config.videoSendTimeout <= 0) {
478
614
  return session.send(content).catch((err) => {
615
+ const errorMsg = getErrorMessage(err);
616
+ logger.error(`[${ErrorCode.MESSAGE_SEND_FAILED}] 发送消息失败: ${errorMsg}`);
479
617
  if (!config.ignoreSendError)
480
- logger.error('发送消息失败:', err.message);
618
+ return null;
481
619
  return null;
482
620
  });
483
621
  }
@@ -485,8 +623,11 @@ function apply(ctx, config) {
485
623
  session.send(content),
486
624
  new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), config.videoSendTimeout))
487
625
  ]).catch((err) => {
626
+ const errorMsg = getErrorMessage(err);
627
+ const code = errorMsg.includes('timeout') ? ErrorCode.MESSAGE_SEND_TIMEOUT : ErrorCode.MESSAGE_SEND_FAILED;
628
+ logger.error(`[${code}] 发送消息失败: ${errorMsg}`);
488
629
  if (!config.ignoreSendError)
489
- logger.error('发送消息超时:', err.message);
630
+ return null;
490
631
  return null;
491
632
  });
492
633
  }
@@ -499,43 +640,32 @@ function apply(ctx, config) {
499
640
  linkBuffer.delete(key);
500
641
  }
501
642
  const items = [];
502
- const errs = [];
643
+ const errors = [];
503
644
  for (const url of urls) {
504
645
  const result = await processSingleUrl(session, url);
505
646
  if (result.data) {
506
647
  items.push(result.data);
507
648
  }
508
649
  else {
509
- errs.push(`【${url.slice(0, 22)}...】:${result.msg}`);
510
- logger.error(`解析失败: ${url}, 原因: ${result.msg}`);
650
+ errors.push({ url, code: result.code, msg: result.msg });
651
+ logger.error(`[${result.code}] 解析失败: ${url}, 原因: ${result.msg}`);
511
652
  }
512
653
  }
513
- if (errs.length) {
514
- logger.error(`解析失败数量: ${errs.length}, 示例: ${errs[0]}`);
515
- }
516
- const enableForward = config.enableForward && session.platform === 'onebot';
517
- const forwardMessages = [];
518
- const botName = config.botName || '视频解析机器人';
519
- if (errs.length) {
520
- const errorMsg = `⚠ 部分解析失败\n${errs.join('\n')}`;
521
- if (enableForward) {
522
- forwardMessages.push(buildForwardNode(session, errorMsg, botName));
523
- }
524
- else {
525
- await sendTimeout(session, errorMsg);
526
- await delay(600);
527
- }
654
+ if (errors.length > 0) {
655
+ const errorLines = errors.map(err => `【${err.url}】: ${err.msg}`);
656
+ const errorMsg = `❌ 解析失败列表(共${errors.length}个链接):\n${errorLines.join('\n')}`;
657
+ logger.error(`解析失败数量: ${errors.length}, 错误码列表: ${errors.map(e => e.code).join(', ')}`);
658
+ await sendTimeout(session, errorMsg);
659
+ await delay(500);
528
660
  }
529
661
  if (items.length === 0) {
530
- const failMsg = `❌ 全部解析失败\n${errs.join('\n')}`;
531
- if (enableForward) {
532
- forwardMessages.push(buildForwardNode(session, failMsg, botName));
533
- }
534
- else {
535
- await sendTimeout(session, failMsg);
536
- }
662
+ const failMsg = getErrorInfo(ErrorCode.UNKNOWN_ERROR, '所有链接均解析失败,请检查链接是否有效或稍后重试');
663
+ await sendTimeout(session, `⚠ ${failMsg}`);
537
664
  return;
538
665
  }
666
+ const enableForward = config.enableForward && session.platform === 'onebot';
667
+ const forwardMessages = [];
668
+ const botName = config.botName || '视频解析机器人';
539
669
  for (const item of items) {
540
670
  try {
541
671
  if (enableForward) {
@@ -548,28 +678,60 @@ function apply(ctx, config) {
548
678
  forwardMessages.push(buildForwardNode(session, koishi_1.h.image(item.images[i]), botName));
549
679
  }
550
680
  }
681
+ if (item.type === 'live' || item.type === 'live_photo') {
682
+ if (item.live_photo?.length) {
683
+ for (let i = 0; i < item.live_photo.length && forwardMessages.length < 100; i++) {
684
+ const liveItem = item.live_photo[i];
685
+ forwardMessages.push(buildForwardNode(session, koishi_1.h.image(liveItem.image), botName));
686
+ if (liveItem.video) {
687
+ try {
688
+ const videoElem = koishi_1.h.video(liveItem.video);
689
+ forwardMessages.push(buildForwardNode(session, videoElem, botName));
690
+ }
691
+ catch (e) {
692
+ forwardMessages.push(buildForwardNode(session, koishi_1.h.text(`视频链接: ${liveItem.video}`), botName));
693
+ }
694
+ }
695
+ }
696
+ }
697
+ else if (item.images?.length) {
698
+ for (let i = 0; i < item.images.length && forwardMessages.length < 100; i++) {
699
+ forwardMessages.push(buildForwardNode(session, koishi_1.h.image(item.images[i]), botName));
700
+ }
701
+ }
702
+ }
551
703
  if (item.video && config.showVideoFile && forwardMessages.length < 100) {
552
704
  let videoElem;
553
705
  try {
554
706
  if (config.downloadVideoBeforeSend) {
555
707
  const filename = crypto_1.default.createHash('md5').update(item.video).digest('hex');
556
- const filePath = await downloadVideo(item.video, filename, config.userAgent, config.maxVideoSize, config.downloadThreads);
557
- videoElem = koishi_1.h.file(filePath);
708
+ const downloadResult = await downloadVideo(item.video, filename, config.userAgent, config.maxVideoSize, config.downloadThreads);
709
+ if (downloadResult.code !== ErrorCode.SUCCESS) {
710
+ const errorMsg = getErrorInfo(downloadResult.code);
711
+ forwardMessages.push(buildForwardNode(session, koishi_1.h.text(`${errorMsg}\n链接:${item.video}`), botName));
712
+ }
713
+ else {
714
+ videoElem = koishi_1.h.file(downloadResult.filePath);
715
+ forwardMessages.push(buildForwardNode(session, videoElem, botName));
716
+ }
558
717
  }
559
718
  else {
560
719
  const fileSize = await getFileSize(item.video, config.userAgent);
561
720
  if (config.maxVideoSize > 0 && fileSize > config.maxVideoSize) {
562
- videoElem = koishi_1.h.text(`视频大小${fileSize}MB超过限制${config.maxVideoSize}MB,仅发送链接:${item.video}`);
721
+ const code = ErrorCode.VIDEO_SIZE_EXCEEDED;
722
+ const errorMsg = getErrorInfo(code, `${fileSize}MB超过限制${config.maxVideoSize}MB`);
723
+ videoElem = koishi_1.h.text(`${errorMsg},仅发送链接:${item.video}`);
563
724
  }
564
725
  else {
565
726
  videoElem = koishi_1.h.video(item.video);
566
727
  }
567
728
  }
568
- forwardMessages.push(buildForwardNode(session, videoElem, botName));
569
729
  }
570
730
  catch (error) {
571
- logger.error(`视频处理失败: ${getErrorMessage(error)}`);
572
- forwardMessages.push(buildForwardNode(session, koishi_1.h.text(`视频处理失败:${getErrorMessage(error)}\n链接:${item.video}`), botName));
731
+ const errorMsg = getErrorMessage(error);
732
+ const code = ErrorCode.VIDEO_DOWNLOAD_FAILED;
733
+ logger.error(`[${code}] 视频处理失败: ${errorMsg}`);
734
+ forwardMessages.push(buildForwardNode(session, koishi_1.h.text(getErrorInfo(code, errorMsg) + `\n链接:${item.video}`), botName));
573
735
  }
574
736
  }
575
737
  }
@@ -578,7 +740,28 @@ function apply(ctx, config) {
578
740
  await sendTimeout(session, item.text);
579
741
  await delay(300);
580
742
  }
581
- if (item.type === 'image' && item.images?.length) {
743
+ if (item.type === 'live' || item.type === 'live_photo') {
744
+ if (item.live_photo?.length) {
745
+ for (const liveItem of item.live_photo) {
746
+ await sendTimeout(session, koishi_1.h.image(liveItem.image));
747
+ await delay(200);
748
+ if (liveItem.video) {
749
+ try {
750
+ await sendTimeout(session, koishi_1.h.video(liveItem.video));
751
+ }
752
+ catch (e) {
753
+ await sendTimeout(session, koishi_1.h.text(`Live Photo 视频链接: ${liveItem.video}`));
754
+ }
755
+ await delay(300);
756
+ }
757
+ }
758
+ }
759
+ else if (item.images?.length) {
760
+ const imgMsg = (0, koishi_1.h)('message', ...item.images.map((url) => koishi_1.h.image(url)));
761
+ await sendTimeout(session, imgMsg);
762
+ }
763
+ }
764
+ else if (item.type === 'image' && item.images?.length) {
582
765
  const imgMsg = (0, koishi_1.h)('message', ...item.images.map((url) => koishi_1.h.image(url)));
583
766
  await sendTimeout(session, imgMsg);
584
767
  }
@@ -592,13 +775,22 @@ function apply(ctx, config) {
592
775
  let videoElem;
593
776
  if (config.downloadVideoBeforeSend) {
594
777
  const filename = crypto_1.default.createHash('md5').update(item.video).digest('hex');
595
- const filePath = await downloadVideo(item.video, filename, config.userAgent, config.maxVideoSize, config.downloadThreads);
596
- videoElem = koishi_1.h.file(filePath);
778
+ const downloadResult = await downloadVideo(item.video, filename, config.userAgent, config.maxVideoSize, config.downloadThreads);
779
+ if (downloadResult.code !== ErrorCode.SUCCESS) {
780
+ const errorMsg = getErrorInfo(downloadResult.code);
781
+ await sendTimeout(session, koishi_1.h.text(`${errorMsg}\n链接:${item.video}`));
782
+ continue;
783
+ }
784
+ else {
785
+ videoElem = koishi_1.h.file(downloadResult.filePath);
786
+ }
597
787
  }
598
788
  else {
599
789
  const fileSize = await getFileSize(item.video, config.userAgent);
600
790
  if (config.maxVideoSize > 0 && fileSize > config.maxVideoSize) {
601
- videoElem = koishi_1.h.text(`视频大小${fileSize}MB超过限制${config.maxVideoSize}MB,仅发送链接:${item.video}`);
791
+ const code = ErrorCode.VIDEO_SIZE_EXCEEDED;
792
+ const errorMsg = getErrorInfo(code, `${fileSize}MB超过限制${config.maxVideoSize}MB`);
793
+ videoElem = koishi_1.h.text(`${errorMsg},仅发送链接:${item.video}`);
602
794
  }
603
795
  else {
604
796
  videoElem = koishi_1.h.video(item.video);
@@ -607,8 +799,10 @@ function apply(ctx, config) {
607
799
  await sendTimeout(session, videoElem);
608
800
  }
609
801
  catch (error) {
610
- logger.error(`视频处理失败: ${getErrorMessage(error)}`);
611
- await sendTimeout(session, koishi_1.h.text(`视频处理失败:${getErrorMessage(error)}\n链接:${item.video}`));
802
+ const errorMsg = getErrorMessage(error);
803
+ const code = ErrorCode.VIDEO_DOWNLOAD_FAILED;
804
+ logger.error(`[${code}] 视频处理失败: ${errorMsg}`);
805
+ await sendTimeout(session, koishi_1.h.text(getErrorInfo(code, errorMsg) + `\n链接:${item.video}`));
612
806
  }
613
807
  }
614
808
  }
@@ -616,8 +810,10 @@ function apply(ctx, config) {
616
810
  }
617
811
  }
618
812
  catch (error) {
619
- logger.error(`处理内容失败: ${getErrorMessage(error)}`);
620
- await sendTimeout(session, `❌ 处理${item.type}内容失败: ${getErrorMessage(error)}`);
813
+ const errorMsg = getErrorMessage(error);
814
+ const code = ErrorCode.UNKNOWN_ERROR;
815
+ logger.error(`[${code}] 处理内容失败: ${errorMsg}`);
816
+ await sendTimeout(session, koishi_1.h.text(getErrorInfo(code, `处理${item.type}内容失败: ${errorMsg}`)));
621
817
  }
622
818
  }
623
819
  if (enableForward && forwardMessages.length) {
@@ -627,7 +823,9 @@ function apply(ctx, config) {
627
823
  await sendTimeout(session, forwardMsg);
628
824
  }
629
825
  catch (error) {
630
- logger.error(`合并转发失败: ${getErrorMessage(error)}`);
826
+ const errorMsg = getErrorMessage(error);
827
+ const code = ErrorCode.FORWARD_MESSAGE_FAILED;
828
+ logger.error(`[${code}] 合并转发失败: ${errorMsg}`);
631
829
  for (const node of forwardMessages) {
632
830
  await sendTimeout(session, node.data.content);
633
831
  await delay(500);
@@ -670,21 +868,25 @@ function apply(ctx, config) {
670
868
  });
671
869
  ctx.command('parse <url>', '手动解析视频链接')
672
870
  .action(async ({ session }, url) => {
673
- if (!url)
674
- return '请输入视频链接';
871
+ if (!url) {
872
+ const code = ErrorCode.INVALID_URL;
873
+ return getErrorInfo(code, '请输入视频链接');
874
+ }
675
875
  let urls = extractUrl(url);
676
876
  if (urls.length === 0 && hasPlatformKeyword(url)) {
677
877
  const allLinks = url.match(/https?:\/\/[^\s\"\'\>\]]+/gi) || [];
678
878
  urls = allLinks.filter((u) => getPlatformType(u));
679
879
  }
680
- if (urls.length === 0)
681
- return '不支持该链接';
880
+ if (urls.length === 0) {
881
+ const code = ErrorCode.UNSUPPORTED_PLATFORM;
882
+ return getErrorInfo(code, '不支持该链接');
883
+ }
682
884
  await flush(session, urls);
683
885
  });
684
886
  ctx.command('clear-cache', '清空解析缓存与临时文件')
685
887
  .action(() => {
686
888
  clearAllCache();
687
- return '解析缓存已清空';
889
+ return getErrorInfo(ErrorCode.SUCCESS, '解析缓存已清空');
688
890
  });
689
891
  setInterval(() => {
690
892
  const now = Date.now();
@@ -703,18 +905,24 @@ function apply(ctx, config) {
703
905
  const stat = fs_1.default.statSync(path_1.default.join(tempDir, file));
704
906
  if (now - stat.mtimeMs > 3600000) {
705
907
  fs_1.default.unlinkSync(path_1.default.join(tempDir, file));
908
+ logger.info(`清理过期临时文件: ${file}`);
706
909
  }
707
910
  }
708
911
  catch (error) {
709
- logger.error(`清理临时文件失败: ${file}, ${getErrorMessage(error)}`);
912
+ const errorMsg = getErrorMessage(error);
913
+ logger.error(`[${ErrorCode.UNKNOWN_ERROR}] 清理临时文件失败: ${file}, ${errorMsg}`);
710
914
  }
711
915
  });
712
916
  }, 1800000);
713
917
  if (config.autoClearCacheInterval > 0) {
714
918
  setInterval(() => {
715
919
  clearAllCache();
920
+ logger.info(getErrorInfo(ErrorCode.SUCCESS, '自动清理缓存完成'));
716
921
  }, config.autoClearCacheInterval * 60000);
717
922
  }
718
- process.on('exit', clearAllCache);
719
- logger.info('视频解析插件已加载');
923
+ process.on('exit', () => {
924
+ clearAllCache();
925
+ logger.info(getErrorInfo(ErrorCode.SUCCESS, '进程退出,已清理缓存'));
926
+ });
927
+ logger.info(getErrorInfo(ErrorCode.SUCCESS, '视频解析插件已加载'));
720
928
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-video-parser-all",
3
- "description": "Koishi 全平台视频解析插件,支持抖音/快手/B站/小红书/微博/今日头条/皮皮搞笑/皮皮虾/右视频链接解析",
4
- "version": "0.5.1",
3
+ "description": "Koishi 全平台视频解析插件,支持抖音/快手/B站/小红书/微博/今日头条/皮皮搞笑/皮皮虾/最右视频链接解析",
4
+ "version": "0.5.3",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [
package/readme.md CHANGED
@@ -8,7 +8,6 @@
8
8
  - 🎨 自定义解析结果格式、返回内容类型(封面/链接/视频)
9
9
  - ⚡ 内置防重复解析、接口重试、自动缓存清理等实用功能
10
10
  - 📤 支持 OneBot 平台消息合并转发,优化展示体验
11
- - 🔌 内置多套解析 API,自动降级容错,提升解析成功率
12
11
 
13
12
  ### English
14
13
  This is a **video parsing plugin** developed for the Koishi bot framework, supporting automatic recognition and parsing of short video links from mainstream platforms such as Douyin, Kuaishou, Bilibili, Xiaohongshu, Weibo, Toutiao, Pipi Funny, Pipi Shrimp, and Zuiyou. Core features:
@@ -16,7 +15,6 @@ This is a **video parsing plugin** developed for the Koishi bot framework, suppo
16
15
  - 🎨 Customize the parsing result format and return content type (cover/link/video)
17
16
  - ⚡ Built-in duplicate prevention, retry logic, auto cache cleanup
18
17
  - 📤 Support OneBot message forwarding for better display experience
19
- - 🔌 Multiple built-in parsing APIs with automatic failover
20
18
 
21
19
  ## 项目仓库 (Repository)
22
20
  - GitHub: `https://github.com/Minecraft-1314/koishi-plugin-video-parser-all`
@@ -76,8 +74,6 @@ This is a **video parsing plugin** developed for the Koishi bot framework, suppo
76
74
  |----------------------|-------------------------|
77
75
  | Minecraft-1314 | 插件完整开发 (Complete plugin development) |
78
76
  | JH-Ahua | BugPk-Api 支持 |
79
- | 素颜API | 素颜API 支持 |
80
- | 星之阁API | 星之阁API 支持 |
81
77
  | (欢迎提交 PR 加入贡献者列表) | (Welcome to submit PR to join the contributor list) |
82
78
 
83
79
  ## 许可协议 (License)