koishi-plugin-video-parser-all 0.1.9 → 0.2.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.
Files changed (3) hide show
  1. package/lib/index.d.ts +3 -16
  2. package/lib/index.js +135 -239
  3. package/package.json +1 -1
package/lib/index.d.ts CHANGED
@@ -18,22 +18,9 @@ export interface Config {
18
18
  enableForward: boolean;
19
19
  downloadVideoBeforeSend: boolean;
20
20
  messageBufferDelay: number;
21
- commonApi: string;
22
- douyin: {
23
- mode: 'common' | 'own' | 'custom';
24
- ownApi: string;
25
- customApi: string;
26
- };
27
- kuaishou: {
28
- mode: 'common' | 'own' | 'custom';
29
- ownApi: string;
30
- customApi: string;
31
- };
32
- bilibili: {
33
- mode: 'common' | 'own' | 'custom';
34
- ownApi: string;
35
- customApi: string;
36
- };
21
+ retryTimes: number;
22
+ retryInterval: number;
23
+ apiUrl: string;
37
24
  }
38
25
  export declare const Config: Schema<Config>;
39
26
  export declare function apply(ctx: Context, config: Config): void;
package/lib/index.js CHANGED
@@ -14,59 +14,36 @@ const promises_1 = require("stream/promises");
14
14
  const worker_threads_1 = require("worker_threads");
15
15
  exports.name = 'video-parser-all';
16
16
  exports.Config = koishi_1.Schema.object({
17
- enable: koishi_1.Schema.boolean().default(true).description('是否启用插件'),
18
- showWaitingTip: koishi_1.Schema.boolean().default(true).description('是否显示解析等待提示'),
19
- revokeWaitingTip: koishi_1.Schema.boolean().default(true).description('是否撤回等待提示文本'),
20
- waitingTipText: koishi_1.Schema.string().default('正在解析视频…').description('等待提示文本'),
21
- sameLinkInterval: koishi_1.Schema.number().default(180).description('相同链接重复间隔秒'),
22
- imageParseFormat: koishi_1.Schema.string()
23
- .role('textarea')
24
- .default('${标题} ${tab} ${UP主}\n${简介}\n${~~~}\n${封面}')
25
- .description('图文格式:${标题} ${UP主} ${简介} ${点赞} ${投币} ${收藏} ${转发} ${观看} ${弹幕} ${tab} ${~~~} ${封面}'),
17
+ enable: koishi_1.Schema.boolean().default(true),
18
+ showWaitingTip: koishi_1.Schema.boolean().default(true),
19
+ // 修复:boolean.default → Schema.boolean().default
20
+ revokeWaitingTip: koishi_1.Schema.boolean().default(true),
21
+ waitingTipText: koishi_1.Schema.string().default('正在解析视频…'),
22
+ sameLinkInterval: koishi_1.Schema.number().default(180),
23
+ imageParseFormat: koishi_1.Schema.string().role('textarea').default('${标题} ${tab} ${UP主}\n${简介}\n${~~~}\n${封面}'),
26
24
  returnContent: koishi_1.Schema.object({
27
- showImageText: koishi_1.Schema.boolean().default(true).description('返回图文'),
28
- showVideoUrl: koishi_1.Schema.boolean().default(false).description('返回视频直链'),
29
- showVideoFile: koishi_1.Schema.boolean().default(true).description('返回视频')
30
- }).description('内容组件'),
31
- maxDescLength: koishi_1.Schema.number().default(200).description('简介最大长度'),
32
- timeout: koishi_1.Schema.number().default(15000).description('请求超时毫秒'),
33
- ignoreSendError: koishi_1.Schema.boolean().default(true).description('忽略发送错误'),
34
- enableForward: koishi_1.Schema.boolean().default(false).description('合并转发(仅onebot)'),
35
- downloadVideoBeforeSend: koishi_1.Schema.boolean().default(false).description('先下载视频再发送(解决onebot问题)'),
36
- messageBufferDelay: koishi_1.Schema.number().default(1).min(0).description('消息缓冲延迟秒(可设为0)'),
37
- commonApi: koishi_1.Schema.string().default('https://api.bugpk.com/api/short_videos').description('通用解析API'),
38
- douyin: koishi_1.Schema.object({
39
- mode: koishi_1.Schema.union([
40
- koishi_1.Schema.const('common').description('使用通用API'),
41
- koishi_1.Schema.const('own').description('使用平台专属API'),
42
- koishi_1.Schema.const('custom').description('使用自定义API'),
43
- ]).default('common').description('抖音解析模式'),
44
- ownApi: koishi_1.Schema.string().default('https://api.bugpk.com/api/douyin').description('抖音专属API'),
45
- customApi: koishi_1.Schema.string().description('抖音自定义API'),
46
- }).description('抖音配置'),
47
- kuaishou: koishi_1.Schema.object({
48
- mode: koishi_1.Schema.union([
49
- koishi_1.Schema.const('common').description('使用通用API'),
50
- koishi_1.Schema.const('own').description('使用平台专属API'),
51
- koishi_1.Schema.const('custom').description('使用自定义API'),
52
- ]).default('common').description('快手解析模式'),
53
- ownApi: koishi_1.Schema.string().default('https://api.bugpk.com/api/ksjx').description('快手专属API'),
54
- customApi: koishi_1.Schema.string().description('快手自定义API'),
55
- }).description('快手配置'),
56
- bilibili: koishi_1.Schema.object({
57
- mode: koishi_1.Schema.union([
58
- koishi_1.Schema.const('common').description('使用通用API'),
59
- koishi_1.Schema.const('own').description('使用平台专属API'),
60
- koishi_1.Schema.const('custom').description('使用自定义API'),
61
- ]).default('common').description('B站解析模式'),
62
- ownApi: koishi_1.Schema.string().default('https://api.bugpk.com/api/bilibili').description('B站专属API'),
63
- customApi: koishi_1.Schema.string().description('B站自定义API'),
64
- }).description('B站配置'),
25
+ showImageText: koishi_1.Schema.boolean().default(true),
26
+ showVideoUrl: koishi_1.Schema.boolean().default(false),
27
+ showVideoFile: koishi_1.Schema.boolean().default(true),
28
+ }),
29
+ maxDescLength: koishi_1.Schema.number().default(200),
30
+ timeout: koishi_1.Schema.number().default(15000),
31
+ ignoreSendError: koishi_1.Schema.boolean().default(true),
32
+ enableForward: koishi_1.Schema.boolean().default(false),
33
+ downloadVideoBeforeSend: koishi_1.Schema.boolean().default(false),
34
+ messageBufferDelay: koishi_1.Schema.number().default(1).min(0),
35
+ retryTimes: koishi_1.Schema.number().default(3).min(0),
36
+ retryInterval: koishi_1.Schema.number().default(2000).min(500),
37
+ apiUrl: koishi_1.Schema.string().default('https://api.bugpk.com/api/short_videos'),
65
38
  });
66
39
  if (!worker_threads_1.isMainThread) {
67
40
  const { url, filePath } = worker_threads_1.workerData;
68
41
  (async () => {
69
42
  try {
43
+ if (url.endsWith('.m4a') || url.endsWith('.mp3')) {
44
+ worker_threads_1.parentPort?.postMessage({ success: false, error: '不支持音频' });
45
+ return;
46
+ }
70
47
  const response = await (0, axios_1.default)({
71
48
  url,
72
49
  method: 'GET',
@@ -80,10 +57,7 @@ if (!worker_threads_1.isMainThread) {
80
57
  worker_threads_1.parentPort?.postMessage({ success: true, filePath });
81
58
  }
82
59
  catch (error) {
83
- worker_threads_1.parentPort?.postMessage({
84
- success: false,
85
- error: error.message
86
- });
60
+ worker_threads_1.parentPort?.postMessage({ success: false, error: error.message });
87
61
  }
88
62
  })();
89
63
  }
@@ -91,8 +65,8 @@ const processed = new Map();
91
65
  const linkBuffer = new Map();
92
66
  const PLATFORM_KEYWORDS = {
93
67
  bilibili: ['bilibili', 'b23', 'B站'],
94
- kuaishou: ['kuaishou', '快手'],
95
- douyin: ['douyin', '抖音']
68
+ kuaishou: ['kuaishou', '快手', 'v.kuaishou.com'],
69
+ douyin: ['douyin', '抖音', 'v.douyin.com']
96
70
  };
97
71
  function extractUrl(content) {
98
72
  const urlMatches = content.match(/https?:\/\/[^\s]+/gi) || [];
@@ -121,30 +95,23 @@ async function downloadVideoWithThreads(url, filename) {
121
95
  if (!fs_1.default.existsSync(dir))
122
96
  fs_1.default.mkdirSync(dir, { recursive: true });
123
97
  const filePath = path_1.default.join(dir, `${filename}.mp4`);
124
- const worker = new worker_threads_1.Worker(__filename, {
125
- workerData: { url, filePath }
126
- });
98
+ const worker = new worker_threads_1.Worker(__filename, { workerData: { url, filePath } });
127
99
  worker.on('message', (result) => {
128
- if (result.success) {
100
+ if (result.success)
129
101
  resolve(result.filePath);
130
- }
131
- else {
102
+ else
132
103
  reject(new Error(result.error));
133
- }
134
- });
135
- worker.on('error', (error) => {
136
- reject(error);
137
104
  });
105
+ worker.on('error', reject);
138
106
  worker.on('exit', (code) => {
139
- if (code !== 0) {
140
- reject(new Error(`下载线程退出,代码: ${code}`));
141
- }
107
+ if (code !== 0)
108
+ reject(new Error('worker exit'));
142
109
  });
143
110
  });
144
111
  }
145
- function parseData(data, platform, maxDescLength) {
146
- let title = data.title || '无标题';
147
- let author = data.auther?.name || data.user?.name || data.auther || data.author || '未知作者';
112
+ function parseData(data, maxDescLength) {
113
+ let title = data.title || data.desc || '无标题';
114
+ let author = data.author || data.auther || data.user?.name || '未知作者';
148
115
  let desc = data.desc || data.description || title || '无简介';
149
116
  desc = desc.slice(0, maxDescLength);
150
117
  let digg = data.like || data.digg || 0;
@@ -155,34 +122,34 @@ function parseData(data, platform, maxDescLength) {
155
122
  let danmaku = data.danmaku || data.comment || 0;
156
123
  let cover = data.cover || data.imgurl || data.pic || '';
157
124
  let video = '';
158
- if (data.url)
125
+ if (data.videos && Array.isArray(data.videos) && data.videos.length > 0 && data.videos[0].url) {
126
+ video = data.videos[0].url;
127
+ }
128
+ else if (data.url) {
159
129
  video = data.url;
160
- else if (data.video_url)
161
- video = data.video_url;
162
- else if (data.videos && Array.isArray(data.videos) && data.videos.length > 0) {
163
- video = data.videos[0].url || '';
164
130
  }
165
- else if (data.play)
166
- video = data.play;
167
- else if (data.hd_url)
168
- video = data.hd_url;
131
+ else if (data.video_backup && Array.isArray(data.video_backup) && data.video_backup[0]?.url) {
132
+ video = data.video_backup[0].url;
133
+ }
134
+ if (video.endsWith('.m4a') || video.endsWith('.mp3'))
135
+ video = '';
169
136
  return { title, author, desc, digg, coin, collect, share, play, danmaku, cover, video };
170
137
  }
171
138
  function clearAllCache() {
172
139
  processed.clear();
173
- linkBuffer.forEach(buffer => clearTimeout(buffer.timer));
140
+ linkBuffer.forEach(b => clearTimeout(b.timer));
174
141
  linkBuffer.clear();
175
142
  const tempDir = path_1.default.join(process.cwd(), 'temp_videos');
176
143
  if (fs_1.default.existsSync(tempDir)) {
177
- fs_1.default.readdirSync(tempDir).forEach(file => {
144
+ fs_1.default.readdirSync(tempDir).forEach(f => {
178
145
  try {
179
- const filePath = path_1.default.join(tempDir, file);
180
- fs_1.default.unlinkSync(filePath);
146
+ fs_1.default.unlinkSync(path_1.default.join(tempDir, f));
181
147
  }
182
- catch (e) { }
148
+ catch { }
183
149
  });
184
150
  }
185
151
  }
152
+ const delay = (ms) => new Promise(r => setTimeout(r, ms));
186
153
  function apply(ctx, config) {
187
154
  if (!worker_threads_1.isMainThread)
188
155
  return;
@@ -193,47 +160,32 @@ function apply(ctx, config) {
193
160
  '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'
194
161
  }
195
162
  });
196
- function getApi(platform) {
197
- const conf = config[platform];
198
- if (conf.mode === 'custom' && conf.customApi)
199
- return conf.customApi;
200
- if (conf.mode === 'own')
201
- return conf.ownApi;
202
- return config.commonApi;
203
- }
204
163
  async function parse(url) {
205
164
  const platform = getPlatformType(url);
206
165
  if (!platform)
207
- return { data: null, platform: null };
208
- const api = getApi(platform);
209
- if (!api)
210
- return { data: null, platform: null };
211
- try {
212
- const res = await http.get(api, { params: { url } });
213
- if ((res.data.code === 200 || res.data.code === 0) && res.data.data) {
214
- return {
215
- data: parseData(res.data.data, platform, config.maxDescLength),
216
- platform
217
- };
218
- }
219
- else {
220
- ctx.logger.error(`API返回非成功状态: ${JSON.stringify(res.data)}`);
166
+ return { data: null };
167
+ for (let i = 0; i <= config.retryTimes; i++) {
168
+ try {
169
+ const res = await http.get(config.apiUrl, { params: { url } });
170
+ const isSuccess = (res.data.code === 200 || res.data.code === 0) && res.data.data;
171
+ if (isSuccess) {
172
+ return { data: parseData(res.data.data, config.maxDescLength) };
173
+ }
221
174
  }
175
+ catch (e) { }
176
+ if (i < config.retryTimes)
177
+ await delay(config.retryInterval);
222
178
  }
223
- catch (e) {
224
- ctx.logger.error(`解析失败: ${e.message}`);
225
- }
226
- return { data: null, platform: null };
179
+ return { data: null };
227
180
  }
228
181
  async function processSingleUrl(session, url) {
229
182
  const hash = crypto_1.default.createHash('md5').update(url).digest('hex');
230
183
  const now = Date.now();
231
- if (processed.get(hash) && now - processed.get(hash) < config.sameLinkInterval * 1000) {
184
+ if (processed.get(hash) && now - processed.get(hash) < config.sameLinkInterval * 1000)
232
185
  return null;
233
- }
234
186
  processed.set(hash, now);
235
187
  const parseResult = await parse(url);
236
- if (!parseResult.data)
188
+ if (!parseResult.data || !parseResult.data.video)
237
189
  return null;
238
190
  let text = config.imageParseFormat
239
191
  .replace(/\${标题}/g, parseResult.data.title)
@@ -245,28 +197,26 @@ function apply(ctx, config) {
245
197
  .replace(/\${转发}/g, String(parseResult.data.share))
246
198
  .replace(/\${观看}/g, String(parseResult.data.play))
247
199
  .replace(/\${弹幕}/g, String(parseResult.data.danmaku))
248
- .replace(/\${tab}/g, '\t')
249
- .replace(/\${~~~}/g, '\n');
200
+ .replace(/\${tab}/g, '\t').replace(/\${~~~}/g, '\n');
250
201
  const contentParts = [];
251
202
  if (config.returnContent.showImageText) {
252
- const [beforeCover, afterCover] = text.split('${封面}');
253
- if (beforeCover && beforeCover.trim())
254
- contentParts.push(beforeCover.trim());
203
+ const [b, a] = text.split('${封面}');
204
+ if (b?.trim())
205
+ contentParts.push(b.trim());
255
206
  if (parseResult.data.cover)
256
207
  contentParts.push(koishi_1.h.image(parseResult.data.cover));
257
- if (afterCover && afterCover.trim())
258
- contentParts.push(afterCover.trim());
208
+ if (a?.trim())
209
+ contentParts.push(a.trim());
259
210
  }
260
211
  let videoContent = '';
261
212
  if (config.returnContent.showVideoFile && parseResult.data.video) {
262
213
  if (config.downloadVideoBeforeSend && session.platform === 'onebot') {
263
214
  try {
264
- const filename = crypto_1.default.createHash('md5').update(parseResult.data.video).digest('hex');
265
- const filePath = await downloadVideoWithThreads(parseResult.data.video, filename);
266
- videoContent = koishi_1.h.file(filePath);
215
+ const name = crypto_1.default.createHash('md5').update(parseResult.data.video).digest('hex');
216
+ const fp = await downloadVideoWithThreads(parseResult.data.video, name);
217
+ videoContent = koishi_1.h.file(fp);
267
218
  }
268
219
  catch (e) {
269
- ctx.logger.error(`下载视频失败: ${e.message}`);
270
220
  videoContent = koishi_1.h.video(parseResult.data.video);
271
221
  }
272
222
  }
@@ -277,153 +227,99 @@ function apply(ctx, config) {
277
227
  if (config.returnContent.showVideoUrl && parseResult.data.video) {
278
228
  contentParts.push(`🔗 无水印链接:${parseResult.data.video}`);
279
229
  }
280
- return {
281
- textContent: contentParts.join('\n'),
282
- videoContent,
283
- rawData: parseResult.data
284
- };
230
+ return { textContent: contentParts.join('\n'), videoContent };
285
231
  }
286
- async function revokeWaitingTip(session, sessionKey) {
232
+ async function revokeTip(session, key) {
287
233
  if (!config.revokeWaitingTip || session.platform !== 'onebot')
288
234
  return;
289
- const bufferData = linkBuffer.get(sessionKey);
290
- if (!bufferData?.tipMsgId)
235
+ const buf = linkBuffer.get(key);
236
+ if (!buf?.tipMsgId)
291
237
  return;
292
238
  try {
293
- await session.bot.deleteMessage(session.channelId, bufferData.tipMsgId);
294
- }
295
- catch (e) {
296
- ctx.logger.debug(`撤回等待提示失败: ${e.message}`);
239
+ await session.bot.deleteMessage(session.channelId, buf.tipMsgId);
297
240
  }
241
+ catch { }
298
242
  }
299
- async function flushBuffer(session) {
300
- const sessionKey = `${session.platform}:${session.userId}:${session.channelId}`;
301
- const bufferData = linkBuffer.get(sessionKey);
302
- if (!bufferData)
243
+ async function flush(session) {
244
+ const key = `${session.platform}:${session.userId}:${session.channelId}`;
245
+ const buf = linkBuffer.get(key);
246
+ if (!buf)
303
247
  return;
304
- clearTimeout(bufferData.timer);
305
- linkBuffer.delete(sessionKey);
306
- await revokeWaitingTip(session, sessionKey);
248
+ clearTimeout(buf.timer);
249
+ linkBuffer.delete(key);
250
+ await revokeTip(session, key);
307
251
  const results = [];
308
- for (const url of bufferData.urls) {
309
- const result = await processSingleUrl(session, url);
310
- if (result)
311
- results.push(result);
252
+ for (const u of buf.urls) {
253
+ const r = await processSingleUrl(session, u);
254
+ if (r)
255
+ results.push(r);
312
256
  }
313
257
  if (results.length === 0) {
314
- await session.send('视频解析失败,请稍后重试');
258
+ await session.send('视频解析失败');
315
259
  return;
316
260
  }
317
261
  if (config.enableForward && session.platform === 'onebot') {
318
262
  try {
319
- const forwardMsg = (0, koishi_1.h)('message', {
320
- type: 'forward',
321
- data: {
322
- messages: results.map(result => {
323
- const content = [];
324
- if (result.textContent)
325
- content.push(result.textContent);
326
- if (result.videoContent)
327
- content.push(result.videoContent);
328
- return {
329
- type: 'node',
330
- data: {
331
- name: '视频解析机器人',
332
- uin: session.selfId,
333
- content: content
334
- }
335
- };
336
- })
337
- }
263
+ const forwardMessages = results.map(result => {
264
+ const content = [];
265
+ if (result.textContent)
266
+ content.push(result.textContent);
267
+ if (result.videoContent)
268
+ content.push(result.videoContent);
269
+ return (0, koishi_1.h)('message', [
270
+ (0, koishi_1.h)('author', { id: session.selfId, name: '解析机器人' }),
271
+ ...content
272
+ ]);
338
273
  });
339
- await session.send(forwardMsg);
274
+ await session.send((0, koishi_1.h)('message', { forward: true }, forwardMessages));
340
275
  return;
341
276
  }
342
277
  catch (e) {
343
- ctx.logger.error(`合并转发失败: ${e.message}`);
344
278
  await session.send('合并转发失败,将分开发送');
345
279
  }
346
280
  }
347
- for (const result of results) {
281
+ for (const r of results) {
348
282
  try {
349
- if (result.textContent)
350
- await session.send(result.textContent);
351
- if (result.videoContent)
352
- await session.send(result.videoContent);
353
- }
354
- catch (e) {
355
- if (!config.ignoreSendError) {
356
- ctx.logger.warn(`发送消息失败: ${e.message}`);
357
- }
283
+ if (r.textContent)
284
+ await session.send(r.textContent);
285
+ if (r.videoContent)
286
+ await session.send(r.videoContent);
358
287
  }
288
+ catch { }
359
289
  }
360
290
  }
361
291
  ctx.on('message', async (session) => {
362
- try {
363
- if (!config.enable)
364
- return;
365
- const content = session.content.trim();
366
- if (!hasPlatformKeyword(content))
367
- return;
368
- const urls = extractUrl(content);
369
- if (urls.length === 0)
370
- return;
371
- const sessionKey = `${session.platform}:${session.userId}:${session.channelId}`;
372
- if (linkBuffer.has(sessionKey)) {
373
- const existing = linkBuffer.get(sessionKey);
374
- existing.urls.push(...urls);
375
- clearTimeout(existing.timer);
376
- existing.timer = setTimeout(() => flushBuffer(session), Math.max(0, config.messageBufferDelay * 1000));
377
- linkBuffer.set(sessionKey, existing);
378
- return;
379
- }
380
- let tipMsgId;
381
- if (config.showWaitingTip) {
382
- const tipText = config.waitingTipText;
383
- const tipMsg = await session.send(tipText).catch(e => {
384
- if (!config.ignoreSendError)
385
- ctx.logger.warn(`发送等待提示失败: ${e.message}`);
386
- return null;
387
- });
388
- tipMsgId = tipMsg?.messageId || tipMsg?.id || tipMsg;
389
- }
390
- linkBuffer.set(sessionKey, {
391
- urls,
392
- timer: setTimeout(() => flushBuffer(session), Math.max(0, config.messageBufferDelay * 1000)),
393
- tipMsgId
394
- });
292
+ if (!config.enable)
293
+ return;
294
+ const content = session.content.trim();
295
+ if (!hasPlatformKeyword(content))
296
+ return;
297
+ const urls = extractUrl(content);
298
+ if (urls.length === 0)
299
+ return;
300
+ const key = `${session.platform}:${session.userId}:${session.channelId}`;
301
+ if (linkBuffer.has(key)) {
302
+ const b = linkBuffer.get(key);
303
+ b.urls.push(...urls);
304
+ clearTimeout(b.timer);
305
+ b.timer = setTimeout(() => flush(session), Math.max(0, config.messageBufferDelay * 1000));
306
+ linkBuffer.set(key, b);
307
+ return;
395
308
  }
396
- catch (e) {
397
- ctx.logger.error(`消息处理异常: ${e.message}`);
398
- const sessionKey = `${session.platform}:${session.userId}:${session.channelId}`;
399
- if (linkBuffer.has(sessionKey)) {
400
- clearTimeout(linkBuffer.get(sessionKey).timer);
401
- linkBuffer.delete(sessionKey);
402
- }
309
+ let tipId;
310
+ if (config.showWaitingTip) {
311
+ const m = await session.send(config.waitingTipText).catch(() => null);
312
+ tipId = m?.messageId || m?.id || m;
403
313
  }
314
+ linkBuffer.set(key, {
315
+ urls,
316
+ timer: setTimeout(() => flush(session), Math.max(0, config.messageBufferDelay * 1000)),
317
+ tipMsgId: tipId
318
+ });
404
319
  });
405
320
  setInterval(() => {
406
321
  const now = Date.now();
407
- processed.forEach((timestamp, hash) => {
408
- if (now - timestamp > 86400000) {
409
- processed.delete(hash);
410
- }
411
- });
412
- const tempDir = path_1.default.join(process.cwd(), 'temp_videos');
413
- if (fs_1.default.existsSync(tempDir)) {
414
- fs_1.default.readdirSync(tempDir).forEach(file => {
415
- try {
416
- const filePath = path_1.default.join(tempDir, file);
417
- const stat = fs_1.default.statSync(filePath);
418
- if (now - stat.ctimeMs > 3600000) {
419
- fs_1.default.unlinkSync(filePath);
420
- }
421
- }
422
- catch (e) {
423
- ctx.logger.error(`清理临时文件失败: ${e.message}`);
424
- }
425
- });
426
- }
322
+ processed.forEach((t, h) => now - t > 86400000 && processed.delete(h));
427
323
  }, 3600000);
428
- ctx.logger.info('视频解析插件加载完成,缓存已清空');
324
+ ctx.logger.info('视频解析已加载');
429
325
  }
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.1.9",
4
+ "version": "0.2.0",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [