crabatool 1.0.856 → 1.0.857

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/index.js CHANGED
@@ -338,6 +338,7 @@ async function checkFast(args) {
338
338
  if (args.includes('-runEv')) {
339
339
  start.bindConfigByArgv('-evSavePath');
340
340
  start.bindConfigByArgv('-port');
341
+ start.bindConfigByArgv('-local', 'boolean');
341
342
  start.runEvServer();
342
343
  return false;
343
344
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "crabatool",
3
- "version": "1.0.856",
3
+ "version": "1.0.857",
4
4
  "description": "crabatool",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -19,6 +19,9 @@ var evConfig = {
19
19
  // 监控的文件
20
20
  var monitoringFiles = {};
21
21
 
22
+ // 已完成的文件(防止重复处理)
23
+ var completedFiles = {};
24
+
22
25
  // 确保输出目录存在
23
26
  if (!fs.existsSync(evConfig.outputDir)) {
24
27
  fs.mkdirSync(evConfig.outputDir, { recursive: true });
@@ -62,6 +65,9 @@ function init() {
62
65
  // 直接启动runEv服务时,什么都不用做
63
66
  // 等待前端连接和start消息,如果等到的是连接消息,记录连接时间。
64
67
  log('evsockettool 初始化完成,等待前端连接');
68
+ if (config.local) {
69
+ startEvRecording();
70
+ }
65
71
  }
66
72
 
67
73
  // 启动ev录屏
@@ -132,10 +138,21 @@ function stopEvRecording(conn) {
132
138
  // 清理监控文件
133
139
  for (var filePath in monitoringFiles) {
134
140
  if (monitoringFiles.hasOwnProperty(filePath)) {
135
- clearInterval(monitoringFiles[filePath]);
141
+ if (monitoringFiles[filePath].timer) {
142
+ clearInterval(monitoringFiles[filePath].timer);
143
+ }
136
144
  delete monitoringFiles[filePath];
137
145
  }
138
146
  }
147
+ log('清理监控文件');
148
+
149
+ // 清理已完成文件标记
150
+ for (var completedFile in completedFiles) {
151
+ if (completedFiles.hasOwnProperty(completedFile)) {
152
+ delete completedFiles[completedFile];
153
+ }
154
+ }
155
+ log('清理已完成文件标记');
139
156
 
140
157
  // 清理watcher
141
158
  if (evConfig.watcher) {
@@ -169,15 +186,21 @@ function watchEvOutput(conn) {
169
186
  evConfig.watcher.on('add', function(filePath) {
170
187
  var ext = path.extname(filePath).toLowerCase();
171
188
  if (ext === '.mp3' || ext === '.wav' || ext === '.mp4') {
172
- log('发现新的媒体文件:', filePath);
189
+ // 检查文件是否已完成
190
+ if (completedFiles[filePath]) {
191
+ log('文件已处理完成,跳过:', filePath);
192
+ return;
193
+ }
194
+ console.log('\r\n\r\n');
195
+ log('---------发现新的媒体文件---------', filePath);
173
196
 
174
197
  // 检查文件创建时间是否大于启动时间
175
198
  try {
176
199
  var stats = fs.statSync(filePath);
177
200
  var fileCreateTime = stats.ctime.getTime();
178
201
  if (fileCreateTime > evConfig.startTime) {
179
- log('录屏开始,文件已生成:', filePath);
180
202
  sendEvMessage(conn, 'msg', '录屏已开始,正在录制中');
203
+ startFileMonitoring(conn, filePath);
181
204
  }
182
205
  } catch (ex) {
183
206
  log('检查文件信息失败:', ex.message);
@@ -188,25 +211,29 @@ function watchEvOutput(conn) {
188
211
  evConfig.watcher.on('change', function(filePath) {
189
212
  var ext = path.extname(filePath).toLowerCase();
190
213
  if (ext === '.mp3' || ext === '.wav' || ext === '.mp4') {
191
- log('文件变化:', filePath);
214
+ //log('文件变化:', filePath);
215
+
216
+ // 检查文件是否已完成
217
+ if (completedFiles[filePath]) {
218
+ log('文件已处理完成,跳过:', filePath);
219
+ return;
220
+ }
192
221
 
193
222
  // 检查文件创建时间和大小
194
223
  try {
195
224
  var stats = fs.statSync(filePath);
196
225
  var fileCreateTime = stats.ctime.getTime();
197
-
198
226
  // 文件创建时间必须大于启动时间
199
227
  if (fileCreateTime > evConfig.startTime) {
200
- log('文件创建时间符合要求:', new Date(fileCreateTime).toISOString());
201
-
202
- // 如果文件还没有被监控,开始监控
203
- if (!monitoringFiles[filePath]) {
204
- log('开始监控文件:', filePath, '当前大小:', stats.size, 'bytes');
205
- // 先标记为监控中,防止短时间内重复触发
206
- monitoringFiles[filePath] = true;
207
- startMonitoringFile(conn, filePath);
228
+ log('文件发生改变:', filePath, '当前大小:', stats.size, 'bytes');
229
+
230
+ // 如果文件已经在监控中,重置成功计数
231
+ if (monitoringFiles[filePath]) {
232
+ log('文件在监控中发生变化,重置成功计数:', filePath);
233
+ monitoringFiles[filePath].successCount = 0;
208
234
  } else {
209
- log('文件已在监控中,跳过:', filePath);
235
+ // 开始监控文件
236
+ startFileMonitoring(conn, filePath);
210
237
  }
211
238
  } else {
212
239
  log('文件创建时间不符合要求,跳过:', filePath);
@@ -222,74 +249,60 @@ function watchEvOutput(conn) {
222
249
  });
223
250
  }
224
251
 
225
- // 监控文件大小变化,判断录制是否真正结束
226
- function startMonitoringFile(conn, filePath) {
227
- // 如果已经是一个有效的interval,说明已经在监控中
228
- if (monitoringFiles[filePath] && monitoringFiles[filePath] !== true) {
229
- log('文件已在监控中,跳过:', filePath);
252
+ // 开始监控文件,每隔1秒尝试重命名检测,连续3次成功才算完成
253
+ function startFileMonitoring(conn, filePath) {
254
+ // 如果已经在监控中,跳过
255
+ if (monitoringFiles[filePath]) {
256
+ //log('文件已在监控中,跳过:', filePath);
230
257
  return;
231
258
  }
259
+ var stats = fs.statSync(filePath);
260
+ log('开始监控文件是否录制完成:', filePath, '当前大小:', stats.size, 'bytes');
261
+
262
+ // 标记为监控中,存储状态
263
+ monitoringFiles[filePath] = {
264
+ timer: null,
265
+ successCount: 0,
266
+ requiredSuccessCount: 3 // 需要连续3次成功
267
+ };
232
268
 
233
- var lastSize = 0;
234
- var stableCount = 0;
235
- var hasGrowth = false;
236
-
237
- // 根据文件类型设置不同的阈值
238
- var ext = path.extname(filePath).toLowerCase();
239
- var isVideo = ext === '.mp4';
240
- var isAudio = ext === '.mp3' || ext === '.wav';
241
-
242
- // MP4视频:帧率20fps,文件增长快,检查间隔1秒,稳定3次(3秒)即可判定完成
243
- // MP3音频:码率128kbps,文件增长慢,检查间隔3秒,稳定2次(6秒)即可判定完成
244
- var checkInterval = isVideo ? 1000 : (isAudio ? 3000 : 2000); // MP4: 1秒, MP3: 3秒, 其他: 2秒
245
- var stableThreshold = isVideo ? 3 : (isAudio ? 2 : 3); // MP4: 3次(3秒), MP3: 2次(6秒), 其他: 3次
246
- var minFileSize = isVideo ? 5000 : (isAudio ? 3000 : 1000); // MP4: 5KB, MP3: 3KB, 其他: 1KB
247
-
248
- log('开始监控文件:', path.basename(filePath), '类型:', isVideo ? '视频' : (isAudio ? '音频' : '其他'),
249
- '检查间隔:', checkInterval + 'ms', '稳定阈值:', stableThreshold + '次');
250
-
251
- // 清除之前的标记,设置为真正的interval
252
- if (monitoringFiles[filePath] === true) {
253
- delete monitoringFiles[filePath];
254
- }
255
-
256
- monitoringFiles[filePath] = setInterval(function() {
257
- try {
258
- var stats = fs.statSync(filePath);
259
- var currentSize = stats.size;
260
-
261
- if (currentSize > lastSize) {
262
- hasGrowth = true;
263
- stableCount = 0; // 重置计数
264
- log('文件大小增长:', filePath, '当前大小:', currentSize, 'bytes');
265
- } else if (currentSize === lastSize) {
266
- stableCount++;
267
- log('文件大小稳定:', filePath, '连续稳定次数:', stableCount, '稳定时间:', (stableCount * checkInterval / 1000).toFixed(1), '秒');
268
- }
269
-
270
- lastSize = currentSize;
271
-
272
- // 当文件大小连续稳定5次(15秒)且至少有过一次增长,并且文件大小大于最小阈值时,认为录制真正结束
273
- if (stableCount >= stableThreshold && hasGrowth && currentSize >= minFileSize) {
274
- clearInterval(monitoringFiles[filePath]);
275
- delete monitoringFiles[filePath];
276
- log('文件录制真正完成:', filePath, '最终大小:', currentSize, 'bytes');
277
- processEvFile(conn, filePath);
269
+ const state = monitoringFiles[filePath];
270
+
271
+ state.timer = setInterval(function() {
272
+ // 尝试重命名检测文件是否被占用
273
+ checkFileLock(filePath, function(isLocked) {
274
+ if (!isLocked) {
275
+ // 重命名成功,增加成功计数
276
+ state.successCount++;
277
+ log('文件重命名成功,连续成功次数:', state.successCount + '/' + state.requiredSuccessCount, filePath);
278
+
279
+ // 连续3次成功,说明文件未被占用,录制完成
280
+ if (state.successCount >= state.requiredSuccessCount) {
281
+ clearInterval(state.timer);
282
+ delete monitoringFiles[filePath];
283
+ // 标记文件为已完成
284
+ completedFiles[filePath] = true;
285
+ log('【文件录制完成】:', filePath);
286
+ processEvFile(conn, filePath);
287
+ }
288
+ } else {
289
+ // 重命名失败,重置成功计数,文件仍被占用,继续监控
290
+ state.successCount = 0;
291
+ //log('文件仍被占用,重置成功计数:', filePath);
278
292
  }
279
- } catch (ex) {
280
- log('监控文件失败:', ex.message);
281
- clearInterval(monitoringFiles[filePath]);
282
- delete monitoringFiles[filePath];
283
- }
284
- }, checkInterval);
293
+ });
294
+ }, 1000); // 每隔1秒检查一次
285
295
  }
286
296
 
287
297
  // 处理ev生成的文件
288
298
  function processEvFile(conn, filePath) {
299
+ var stats = fs.statSync(filePath);
300
+ log('【开始上传文件到七牛】:', filePath, '当前大小:', stats.size, 'bytes');
301
+
289
302
  // 检查是否有七牛token
290
303
  if (!evConfig.qiniuToken) {
291
- log('没有七牛token,无法上传文件');
292
- sendEvMessage(conn, 'error', '录制完成,但没有七牛token,无法上传文件');
304
+ log('但没有七牛token,无法上传文件');
305
+ sendEvMessage(conn, 'error', '【录制完成】,但没有七牛token,无法上传文件');
293
306
  return;
294
307
  }
295
308
 
@@ -307,8 +320,6 @@ function processEvFile(conn, filePath) {
307
320
  var formUploader = new qiniu.form_up.FormUploader();
308
321
  var putExtra = new qiniu.form_up.PutExtra();
309
322
 
310
- log('开始上传文件到七牛服务器:', filePath);
311
-
312
323
  // 上传文件
313
324
  formUploader.putFile(evConfig.qiniuToken, qiniuFileName, filePath, putExtra, function(respErr, respBody, respInfo) {
314
325
  if (respErr) {
@@ -343,6 +354,9 @@ function processEvFile(conn, filePath) {
343
354
 
344
355
  // 发送ev消息给前端
345
356
  function sendEvMessage(conn, status, message, data) {
357
+ if (!conn) {
358
+ return;
359
+ }
346
360
  var response = {
347
361
  mode: 'evRecord',
348
362
  status: status,
@@ -359,6 +373,50 @@ function sendEvMessage(conn, status, message, data) {
359
373
  }
360
374
  }
361
375
 
376
+ // 检查文件是否被占用(通过重命名检测)
377
+ function checkFileLock(filePath, callback) {
378
+ // 生成临时文件名(在原文件名后添加递增数字)
379
+ const dir = path.dirname(filePath);
380
+ const ext = path.extname(filePath);
381
+ const baseName = path.basename(filePath, ext);
382
+
383
+ // 生成带递增数字的临时文件名
384
+ let tempName = baseName + '_temp' + ext;
385
+ let tempPath = path.join(dir, tempName);
386
+ let counter = 1;
387
+
388
+ // 确保临时文件名不冲突
389
+ while (fs.existsSync(tempPath)) {
390
+ tempName = baseName + '_temp' + counter + ext;
391
+ tempPath = path.join(dir, tempName);
392
+ counter++;
393
+ }
394
+
395
+ //log('尝试重命名文件:', filePath, '→', tempPath);
396
+
397
+ // 尝试重命名文件
398
+ fs.rename(filePath, tempPath, function(err) {
399
+ if (err) {
400
+ // 重命名失败,说明文件被占用
401
+ //log('文件被占用,重命名失败:', err.message);
402
+ callback(true);
403
+ } else {
404
+ // 重命名成功,说明文件未被占用
405
+ //log('文件未被占用,重命名成功');
406
+
407
+ // 恢复原文件名
408
+ fs.rename(tempPath, filePath, function(restoreErr) {
409
+ if (restoreErr) {
410
+ log('恢复文件名失败:', restoreErr.message);
411
+ } else {
412
+ log('文件未被占用,重命名成功:', filePath);
413
+ }
414
+ callback(false);
415
+ });
416
+ }
417
+ });
418
+ }
419
+
362
420
  // 自动检测ev录屏工具路径
363
421
  function autoDetectEvPath() {
364
422
  var possiblePaths = [
@@ -53,7 +53,6 @@ function createSocket() {
53
53
  }
54
54
  } catch (ex) { }
55
55
  });
56
-
57
56
  conn.on('connect', function(code, reason) {
58
57
  utils.log('connect连接成功');
59
58
  broadcast('ws connect success');
@@ -61,9 +60,7 @@ function createSocket() {
61
60
  conn.on("close", function(code, reason) {
62
61
  utils.debug("connect关闭");
63
62
  });
64
- conn.on("error", function(code, reason) {
65
-
66
- });
63
+ conn.on("error", function(code, reason) {});
67
64
  }).listen(config.socketPort);
68
65
 
69
66
  socketServer.on('connection', function(code, reason) {