tools_batch_files 1.0.22 → 1.0.24

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tools_batch_files",
3
- "version": "1.0.22",
3
+ "version": "1.0.24",
4
4
  "description": "批处理视频工具",
5
5
  "keywords": [
6
6
  "utils",
@@ -36,6 +36,7 @@
36
36
  "点我批量上传图片.bat",
37
37
  "package.json",
38
38
  "package-lock.json",
39
- "utils"
39
+ "utils",
40
+ "src"
40
41
  ]
41
42
  }
@@ -0,0 +1,40 @@
1
+ const fs = require('fs');
2
+ const readline = require('readline');
3
+
4
+ //上传失败的文件汇总到 txt中,脚本跑完后,统一便利再上传。
5
+
6
+ const logFilePath = 'log.txt';
7
+
8
+ // 创建 readline 接口实例
9
+ const rl = readline.createInterface({
10
+ input: fs.createReadStream(logFilePath),
11
+ output: process.stdout,
12
+ terminal: false
13
+ });
14
+
15
+ // 匹配视频文件名,和id = undefined
16
+ const filenameRegex = /.*\*\*\*([^*]+)\*\*\*/;
17
+ const idUndefinedRegex = /id = undefined/;
18
+
19
+ let currentFileName = '';
20
+
21
+ rl.on('line', (line) => {
22
+ // 检查是否是文件名行
23
+ if (line.includes('**************************') && !line.includes('end')) {
24
+ const match = filenameRegex.exec(line);
25
+ if (match && match[1]) {
26
+ currentFileName = match[1].trim(); // 提取并保存当前文件名
27
+ }
28
+ }
29
+
30
+ // 检查id是否未定义
31
+ if (idUndefinedRegex.test(line) && currentFileName) {
32
+ console.log(`找到undefined ID对应的文件名: ${currentFileName}`);
33
+ // 你可以在这里执行进一步操作,例如存储文件名
34
+ fs.appendFileSync('undefined-ids.txt', `${currentFileName}\n`);
35
+ }
36
+ });
37
+
38
+ rl.on('close', () => {
39
+ console.log('Finished reading file.');
40
+ });
package/src/photoBatch.js CHANGED
@@ -38,7 +38,7 @@ const expandedName = ".jpg";
38
38
  const queueCount = 50;
39
39
 
40
40
  //起始任务下标
41
- let taskIndex = 0;
41
+ let taskIndex = 123265;
42
42
 
43
43
  const readExcel = (path) => {
44
44
  const workbook = XLSX.readFile(path);
@@ -494,12 +494,24 @@ const transitionExcelToJSON = () => {
494
494
  logger(
495
495
  `run-------------------------------------第${index}条开始------------------------------------`
496
496
  );
497
- logger(`-------------------${new Date()}------------------`);
497
+ const readExcelStartTime = new Date();
498
498
  const row = jsonData[index];
499
499
 
500
+ const readExcelEndTime = new Date();
501
+ const timeInSeconds = (
502
+ (readExcelEndTime - readExcelStartTime) /
503
+ 1000
504
+ ).toFixed(2);
505
+
506
+ logger(`第${index}条Excel搜索时间:${timeInSeconds}秒`);
507
+ logger(`-------------------${new Date()}------------------`);
500
508
  if (!row) {
501
509
  return;
502
510
  }
511
+ logger(
512
+ `----------------------------------------第${index}条task开始--------------------------------`
513
+ );
514
+ logger(`-------------------${new Date()}------------------`);
503
515
  task(row, index)
504
516
  .then(() => {
505
517
  taskIndex++;
@@ -0,0 +1,566 @@
1
+ #!/usr/bin/env node
2
+ const XLSX = require("xlsx");
3
+ const ffmpeg = require("fluent-ffmpeg");
4
+ const fs = require("fs");
5
+ const path = require("path");
6
+ const fs_asnyc = require("fs").promises;
7
+ const archiver = require("archiver");
8
+ const axios = require("axios");
9
+ const generateUniqueHash = require("../utils/index");
10
+ const FormData = require("form-data");
11
+
12
+ //执行目录
13
+ const exeDir = __dirname;
14
+ //当前工作目录
15
+ const workDir = process.cwd();
16
+
17
+ // 表格目录
18
+ const excelDir = path.join(workDir, "excel", "excel.xlsx");
19
+ // error错误日志目录
20
+ const errorDir = path.join(workDir, "error.txt");
21
+ //水印
22
+ const watermarkImage = path.join(__dirname, "..", "imgs", "picWater.png");
23
+
24
+ // 截图数量
25
+ const SCREENSHOT_COUNT = 5;
26
+ const ORIGIN_FILE_DIR = "photos";
27
+ const PHOTO_PREVIEW_DIR = "preview_photo";
28
+ const ZIP_VIDEO_DIR = "zip_video";
29
+ const ZIP_VIDEO_DIR_400 = "small_video";
30
+ const SCREENSHOT_DIR = "preview_photo";
31
+ const SCREENSHOT_WATERMARK_DIR = "screenshots_watermark";
32
+ const ZIP_WATERMARK_VIDEO_DIR = "zip_watermark_video";
33
+ const ZIP_SCREENSHOT_WM_DIR = "preview_watermark_photo";
34
+ const SOURCE_PHOTO_DIR = "source_photo";
35
+ const ZIP_FILES_DIR = "zip";
36
+
37
+ const expandedName = ".jpg";
38
+
39
+ //并发数量
40
+ const queueCount = 50;
41
+
42
+ //起始任务下标
43
+ let taskIndex = 0;
44
+
45
+ const readExcel = (path) => {
46
+ const workbook = XLSX.readFile(path);
47
+
48
+ // 获取第一个工作表(Sheet)
49
+ const firstSheetName = workbook.SheetNames[0];
50
+ const worksheet = workbook.Sheets[firstSheetName];
51
+
52
+ // 将工作表转换为 JSON 对象
53
+ const jsonData = XLSX.utils.sheet_to_json(worksheet);
54
+ return jsonData;
55
+ };
56
+
57
+ //返回error数组
58
+ const readErrorTxt = (path) => {
59
+ fs.readFile(path, "utf8", (err, data) => {
60
+ if (err) {
61
+ console.error(err);
62
+ return;
63
+ }
64
+
65
+ const lines = data.split("\n");
66
+ return lines;
67
+ });
68
+ };
69
+
70
+ /**
71
+ * 重传的index数组
72
+ */
73
+ const findErrIndex = (jsonData, txtData) => {
74
+ const indexArr = [];
75
+
76
+ txtData.forEach((itemErr, indexErr) => {
77
+ // 在excel中查找匹配项
78
+ const foundIndex = jsonData.findIndex(
79
+ (itemExcel) => itemExcel.fileName === itemErr
80
+ );
81
+ if (foundIndex !== -1) {
82
+ indexArr.push(indexErr);
83
+ }
84
+ });
85
+ return indexArr;
86
+ };
87
+
88
+ function logFileSize(path) {
89
+ const fileSize = fs.statSync(path).size / (1024 * 1024);
90
+ const formattedSize = fileSize.toFixed(2); // 保留两位小数
91
+ logger(`图片大小:${formattedSize}M`);
92
+ }
93
+
94
+ //普通日志
95
+ const logger = (log, hash, err) => {
96
+ console.log(log);
97
+ fs.writeFileSync("log.txt", log + "\n", {
98
+ flag: "a",
99
+ });
100
+ };
101
+
102
+ //错误日志
103
+ const disposeError = (err) => {
104
+ logger("---任务失败---", err);
105
+ logger(
106
+ `******************************************************************end`
107
+ );
108
+ fs.writeFileSync("error.txt", err + "\n", {
109
+ flag: "a",
110
+ });
111
+ };
112
+
113
+ /**
114
+ * 递归遍历文件夹,查找文件
115
+ * @param {*} dir 文件夹路径
116
+ * @param {*} fileName 文件名称
117
+ * @returns string 匹配成功的文件路径
118
+ */
119
+ function findFileInDir(dir, fileName) {
120
+ const files = fs.readdirSync(dir);
121
+ for (const file of files) {
122
+ const filePath = path.join(dir, file);
123
+ const stat = fs.statSync(filePath);
124
+ if (stat.isDirectory()) {
125
+ const result = findFileInDir(filePath, fileName);
126
+ if (result) {
127
+ return result;
128
+ }
129
+ } else if (file.startsWith(fileName) && file.endsWith(expandedName)) {
130
+ return filePath;
131
+ }
132
+ }
133
+ return null;
134
+ }
135
+
136
+ // function findFileInDir(dir, fileName) {
137
+ // return new Promise((resolve, reject) => {
138
+ // fs.readdir(dir, (err, files) => {
139
+ // if (err) {
140
+ // reject(err);
141
+ // return;
142
+ // }
143
+
144
+ // // 遍历目录中的文件和子目录
145
+ // for (const file of files) {
146
+ // const filePath = path.join(dir, file);
147
+ // fs.stat(filePath, (err, stat) => {
148
+ // if (err) {
149
+ // reject(err); // 如果获取文件状态出错,直接reject
150
+ // return;
151
+ // }
152
+ // if (stat.isDirectory()) {
153
+ // // 如果是目录,递归调用findFileInDir
154
+ // findFileInDir(filePath, fileName)
155
+ // .then(resolve) // 如果找到了目标文件,将结果resolve
156
+ // .catch(reject); // 如果在子目录中出错,将错误reject
157
+ // } else if (file.startsWith(fileName) && file.endsWith(expandedName)) {
158
+ // resolve(filePath); // 如果找到了目标文件,将结果resolve
159
+ // }
160
+ // });
161
+ // }
162
+
163
+ // // 如果遍历完了所有文件和子目录仍未找到目标文件,resolve一个null表示未找到
164
+ // resolve(null);
165
+ // });
166
+ // });
167
+ // }
168
+
169
+ /**
170
+ * 生成预览图 indexFilePath: output/0
171
+ * originFilePath E:\workspace\图片测试\photos\abc222.png
172
+ */
173
+ const PriviewPhoto = (indexFilePath, originFilePath, fileName) => {
174
+ //存放预览图图的文件夹
175
+ const outputDir = path.join(indexFilePath, SCREENSHOT_DIR);
176
+ ensureDirSync(outputDir);
177
+
178
+ const outputFile = path.join(outputDir, fileName);
179
+
180
+ return new Promise((resolve, reject) => {
181
+ const watermarkScreenshotsComand = ffmpeg(originFilePath);
182
+ watermarkScreenshotsComand
183
+ .size("1280x?")
184
+ .output(outputFile)
185
+ .on("error", (err) => {
186
+ logger("截图预览图出错: " + err);
187
+ reject();
188
+ })
189
+ .on("end", () => {
190
+ logger("预览图完成: " + outputFile);
191
+ resolve();
192
+ })
193
+ .run();
194
+ });
195
+ };
196
+
197
+ /**
198
+ * 生成水印预览图
199
+ */
200
+ const watermarkPriviewPhoto = (indexFilePath, originFilePath, fileName) => {
201
+ //存放水印截图的文件夹
202
+ const outputDir = path.join(indexFilePath, ZIP_SCREENSHOT_WM_DIR);
203
+ const inputDir = path.join(indexFilePath, SCREENSHOT_DIR, fileName);
204
+ ensureDirSync(outputDir);
205
+
206
+ const outputFile = path.join(outputDir, fileName);
207
+
208
+ return new Promise((resolve, reject) => {
209
+ const watermarkScreenshotsComand = ffmpeg(inputDir);
210
+ watermarkScreenshotsComand
211
+ .input(watermarkImage)
212
+ .complexFilter("overlay=(main_w-overlay_w)/2:(main_h-overlay_h)/2")
213
+ .output(outputFile)
214
+ .on("error", (err) => {
215
+ logger("截图添加水印出错: " + err);
216
+ reject();
217
+ })
218
+ .on("end", () => {
219
+ logger("截图水印添加完成: " + outputFile);
220
+ resolve();
221
+ })
222
+ .run();
223
+ });
224
+ };
225
+
226
+ /**
227
+ * 确保目录存在,如果不存在则创建它
228
+ */
229
+ function ensureDirSync(dirpath) {
230
+ try {
231
+ if (!fs.existsSync(dirpath)) {
232
+ fs.mkdirSync(dirpath, { recursive: true });
233
+ logger(`目录创建成功:${dirpath}`);
234
+ }
235
+ } catch (err) {
236
+ logger(`创建目录时出错:${err}`);
237
+ throw err;
238
+ }
239
+ }
240
+
241
+ /**
242
+ * 打包物料
243
+ */
244
+ const archiveZip = (fileName, inputPath, originFilePath) => {
245
+ const zipDir = path.join(inputPath, "zip");
246
+ const timestamp = new Date().getTime();
247
+
248
+ ensureDirSync(zipDir);
249
+ const zipStream = fs.createWriteStream(
250
+ path.join(zipDir, `package${timestamp}.zip`)
251
+ );
252
+ const archive = archiver("zip", {
253
+ zlib: { level: 9 },
254
+ });
255
+
256
+ return new Promise((resolve, reject) => {
257
+ zipStream.on("close", function () {
258
+ logger("压缩数据:" + archive.pointer() + " total bytes");
259
+ logger(
260
+ "完成归档archiver has been finalized and the output file descriptor has closed."
261
+ );
262
+ resolve();
263
+ });
264
+
265
+ archive.on("warning", function (err) {
266
+ if (err.code === "ENOENT") {
267
+ logger("压缩-warning:" + err);
268
+ } else {
269
+ throw err;
270
+ }
271
+ });
272
+
273
+ archive.on("error", function (err) {
274
+ logger("压缩失败!" + err);
275
+ reject();
276
+ });
277
+
278
+ archive.pipe(zipStream);
279
+ const directories = [ZIP_SCREENSHOT_WM_DIR, SCREENSHOT_DIR];
280
+
281
+ directories.forEach((dir) => {
282
+ const dirPath = path.join(inputPath, dir);
283
+ archive.directory(dirPath, dir);
284
+ });
285
+
286
+ archive.file(originFilePath, {
287
+ name: path.join(SOURCE_PHOTO_DIR, fileName),
288
+ });
289
+ // 完成归档
290
+ archive.finalize();
291
+ });
292
+ };
293
+
294
+ /**
295
+ * 获取 元数据
296
+ */
297
+ const getMetadata = async (
298
+ fileName,
299
+ indexFilePath,
300
+ originFilePath,
301
+ { rowFileName, title, keyword }
302
+ ) => {
303
+ //源图片数据
304
+ const photoMetadataComand = ffmpeg(originFilePath);
305
+ //预览图数据
306
+ const photoInputPath = path.join(indexFilePath, SCREENSHOT_DIR, fileName);
307
+ const photoPreviewMetadataComand = ffmpeg(photoInputPath);
308
+
309
+ const metaDataParams = {
310
+ photo_id: rowFileName,
311
+ width: "",
312
+ height: "",
313
+ format: "",
314
+ size: "",
315
+ pr: 160,
316
+ dpi: 0,
317
+ is_government: 0,
318
+ common_width: "",
319
+ common_height: "",
320
+ dujia: 0,
321
+ xiaoxiang: 0,
322
+ title,
323
+ keyword,
324
+ en_keyword: "",
325
+ // preview_path: "",
326
+ // markImage_path: "",
327
+ // photo_path: "",
328
+ // common_image_path: "",
329
+ // en_keyword: "",
330
+ plate_id: 3,
331
+ transform_plate_id: 3,
332
+ tag_id: 47,
333
+ userid: 1003,
334
+ username: "美好景象",
335
+ demand_kind: 3,
336
+ source_from: 71,
337
+ file: "",
338
+ };
339
+
340
+ await new Promise((resolve, reject) => {
341
+ photoMetadataComand.ffprobe(function (err, metadata) {
342
+ // console.log("metadata");
343
+ // console.log(JSON.stringify(metadata));
344
+ if (metadata) {
345
+ const formatStream = metadata.format;
346
+ const photoStream = metadata.streams.find(
347
+ (s) => s.codec_type === "video"
348
+ );
349
+
350
+ metaDataParams.width = photoStream.width;
351
+ metaDataParams.height = photoStream.height;
352
+ // metaDataParams.format = formatStream.format_name;
353
+ metaDataParams.format = "jpg";
354
+ metaDataParams.size = formatStream.size;
355
+
356
+ resolve();
357
+ } else {
358
+ reject(err);
359
+ }
360
+ });
361
+ });
362
+ await new Promise((resolve, reject) => {
363
+ photoPreviewMetadataComand.ffprobe(function (err, metadata) {
364
+ if (metadata) {
365
+ const photoStream = metadata.streams.find(
366
+ (s) => s.codec_type === "video"
367
+ );
368
+
369
+ metaDataParams.common_width = photoStream.width;
370
+ metaDataParams.common_height = photoStream.height;
371
+ resolve();
372
+ } else {
373
+ reject(err);
374
+ }
375
+ });
376
+ });
377
+
378
+ return metaDataParams;
379
+ };
380
+
381
+ /**
382
+ * 接口
383
+ */
384
+ const postData = (dataParams, indexFilePath) => {
385
+ const formData = new FormData();
386
+
387
+ const zipFiles = fs
388
+ .readdirSync(path.join(indexFilePath, ZIP_FILES_DIR))
389
+ .find((file) => file.endsWith(".zip"));
390
+
391
+ const packageZip = path.join(indexFilePath, ZIP_FILES_DIR, zipFiles);
392
+
393
+ formData.append("file", fs.createReadStream(packageZip));
394
+ for (const key in dataParams) {
395
+ if (Object.hasOwnProperty.call(dataParams, key)) {
396
+ const value = dataParams[key];
397
+ formData.append(key, value);
398
+ }
399
+ }
400
+
401
+ logger("等待接口返回结果……");
402
+
403
+ // return axios.post("http://192.168.102.61:9999/upload/photo", formData, {
404
+ return axios.post("http://127.0.0.1:9999/upload/photo", formData, {
405
+ headers: {
406
+ "Content-Type": "multipart/form-data",
407
+ },
408
+ timeout: 300000,
409
+ });
410
+ };
411
+
412
+ /**
413
+ * 任务
414
+ */
415
+ const task = async (row, index) => {
416
+ logger(
417
+ "**************************" + row.fileName + "**************************"
418
+ );
419
+ // Excel的列名分别为: fileName title keyword
420
+ // 表格中文件名无后缀,遂手动添加写死为mp4
421
+ let fileName = row.fileName;
422
+ const rowFileName = row.fileName;
423
+ const title = row.title;
424
+ const keywordArr = row.keyword.split(",");
425
+ const filteredArray = keywordArr.filter((item) => item.trim() !== "");
426
+ const keyword = filteredArray.join(" ");
427
+
428
+ if (!fileName.includes(".")) {
429
+ fileName = row.fileName + expandedName;
430
+ }
431
+
432
+ const getPathStartTime = new Date();
433
+ // 源图片文件夹路径
434
+ const originFilePath = await findFileInDir(
435
+ path.join(workDir, ORIGIN_FILE_DIR),
436
+ fileName
437
+ );
438
+ const getPathEndTime = new Date();
439
+ const timeInSeconds = ((getPathEndTime - getPathStartTime) / 1000).toFixed(2);
440
+
441
+ logger(`第${index}条Path路径搜索时间:${timeInSeconds}秒`);
442
+ logger(`-------------------${new Date()}------------------`);
443
+
444
+ console.log("originFilePath:" + originFilePath);
445
+ console.log("fileName:" + fileName);
446
+
447
+ if (!originFilePath) {
448
+ logger(`图片文件 ${fileName} 不存在`);
449
+ return;
450
+ }
451
+
452
+ //index文件夹 output/0
453
+ const indexFilePath = path.join(workDir, "output", index + "");
454
+
455
+ try {
456
+ const workStartTime = new Date();
457
+ await fs_asnyc.access(originFilePath, fs_asnyc.constants.F_OK);
458
+ logFileSize(originFilePath);
459
+ await PriviewPhoto(indexFilePath, originFilePath, fileName);
460
+ await watermarkPriviewPhoto(indexFilePath, originFilePath, fileName);
461
+ await archiveZip(fileName, indexFilePath, originFilePath);
462
+ const dataParams = await getMetadata(
463
+ fileName,
464
+ indexFilePath,
465
+ originFilePath,
466
+ {
467
+ rowFileName,
468
+ title,
469
+ keyword,
470
+ }
471
+ );
472
+ const workEndTime = new Date();
473
+ const timeInSeconds = ((workEndTime - workStartTime) / 1000).toFixed(2);
474
+
475
+ logger(`第${index}条4090处理总时间:${timeInSeconds}秒`);
476
+ logger(`-------------------${new Date()}------------------`);
477
+
478
+ const postStartTime = new Date();
479
+ const resData = await postData(dataParams, indexFilePath);
480
+ const postEndTime = new Date();
481
+ const timeInSeconds1 = ((postEndTime - postStartTime) / 1000).toFixed(2);
482
+
483
+ logger(`第${index}条接口处理总时间:${timeInSeconds1}秒`);
484
+ logger(`-------------------${new Date()}------------------`);
485
+
486
+ if (resData.data.code === 200) {
487
+ logger("请求成功!");
488
+ logger(resData.data.code);
489
+ console.log(resData.data);
490
+ //删除文件夹
491
+ fs.rmdirSync(indexFilePath, { recursive: true });
492
+ } else {
493
+ throw new Error("请求失败!" + resData.data.msg);
494
+ }
495
+
496
+ logger(
497
+ `----------------------------------------第${index}条结束---------------------------------end`
498
+ );
499
+ } catch (error) {
500
+ // 可以约定code,来表示不同的错误信息
501
+ if (error.code === "ENOENT") {
502
+ logger(`图片文件 ${fileName} 不存在`);
503
+ } else {
504
+ logger("图片任务处理失败:" + error);
505
+ logger(error);
506
+ }
507
+ fs.rmdirSync(indexFilePath, { recursive: true });
508
+ disposeError(fileName);
509
+ }
510
+ };
511
+
512
+ /**
513
+ * 解析Excel数据
514
+ */
515
+ const transitionExcelToJSON = () => {
516
+ //当前任务hash
517
+ const hashOrigin = generateUniqueHash();
518
+ const hash = hashOrigin.slice(0, 8);
519
+ logger("图片批量任务任务开始---" + hash);
520
+ logger("当前目录: " + workDir);
521
+ logger("工作目录: " + __dirname);
522
+
523
+ const jsonData = readExcel(excelDir);
524
+
525
+ //遍历txt文件,拿到文件名,去excel表匹配对应的行,找到index,
526
+ //队列如何做?把所有搜到的index存成数组。然后循环查找
527
+ const txtData = readErrorTxt(errorDir);
528
+ const findErrIndexArr = findErrIndex(jsonData, txtData);
529
+
530
+ const run = (index) => {
531
+ try {
532
+ logger(
533
+ `run-------------------------------------第${index}条开始------------------------------------`
534
+ );
535
+ logger(`-------------------${new Date()}------------------`);
536
+ const row = jsonData[index];
537
+
538
+ if (!row) {
539
+ return;
540
+ }
541
+
542
+ task(row, index)
543
+ .then(() => {
544
+ taskIndex++;
545
+ run(taskIndex);
546
+ })
547
+ .catch(() => {
548
+ taskIndex++;
549
+ run(taskIndex);
550
+ });
551
+ } catch (error) {
552
+ logger("捕获错误!" + error);
553
+ taskIndex++;
554
+ run(taskIndex);
555
+ }
556
+ };
557
+
558
+ for (let i = 0; i < queueCount; i++) {
559
+ const idx = findErrIndexArr[i + taskIndex];
560
+ if (idx) {
561
+ run(idx);
562
+ }
563
+ }
564
+ };
565
+
566
+ transitionExcelToJSON();
@@ -0,0 +1,100 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+
4
+ const workDir = process.cwd();
5
+
6
+ const ORIGIN_FILE_DIR = "video";
7
+
8
+ // error错误日志目录
9
+ const errorDir = path.join(workDir, "error.txt");
10
+
11
+ const Error_Files_Dir = "failed_videos";
12
+ // const Error_Files_Dir = "failed_photos";
13
+
14
+ /**
15
+ * 确保目录存在,如果不存在则创建它
16
+ */
17
+ function ensureDirSync(dirpath) {
18
+ try {
19
+ if (!fs.existsSync(dirpath)) {
20
+ fs.mkdirSync(dirpath, { recursive: true });
21
+ }
22
+ } catch (err) {
23
+ throw err;
24
+ }
25
+ }
26
+
27
+ //普通日志
28
+ const logger = (log) => {
29
+ console.log(log);
30
+ fs.writeFileSync("errorCopyLog.txt", log + "\n", {
31
+ flag: "a",
32
+ });
33
+ };
34
+
35
+ /**
36
+ * 上传或处理失败的文件,放到fail文件夹中,这样之前的目录就可以删除了
37
+ * @param {*} sourcePath 源文件Path
38
+ * @param {*} destinationPath 存放失败文件的,目标文件Path
39
+ */
40
+ // const moveFailedVideo = (sourcePath, destinationPath) => {
41
+ // console.log("源文件Path" + sourcePath);
42
+ // console.log("目标文件Path" + destinationPath);
43
+
44
+ // ensureDirSync(destinationPath);
45
+ // fs.copyFileSync(sourcePath, destinationPath);
46
+ // };
47
+
48
+ /**
49
+ * 递归遍历文件夹,查找mp4文件
50
+ * @param {*} dir 文件夹路径
51
+ * @param {*} fileName 文件名称
52
+ * @returns {filePath, file} 匹配成功的文件路径,和文件名
53
+ */
54
+ function findFileInDir(dir, fileName) {
55
+ const files = fs.readdirSync(dir);
56
+ for (const file of files) {
57
+ const filePath = path.join(dir, file);
58
+ const stat = fs.statSync(filePath);
59
+ if (stat.isDirectory()) {
60
+ const result = findFileInDir(filePath, fileName);
61
+ if (result) {
62
+ return result;
63
+ }
64
+ } else if (file.startsWith(fileName) && file.endsWith(".mov")) {
65
+ return { filePath, file };
66
+ }
67
+ }
68
+ return null;
69
+ }
70
+
71
+ //返回error中 fileName的数组(带格式)
72
+ const readErrorTxt = (path) => {
73
+ try {
74
+ const data = fs.readFileSync(path, "utf-8");
75
+ const lines = data.split("\n").map((line) => line.trim()); // 删除每行两侧的空白符
76
+ return lines;
77
+ } catch (error) {
78
+ console.error("读取文件时出错:", error);
79
+ return [];
80
+ }
81
+ };
82
+
83
+ const txtData = readErrorTxt(errorDir);
84
+ logger("error数据共计:" + txtData.length);
85
+
86
+ txtData.forEach((item, index) => {
87
+ const fileObj = findFileInDir(path.join(workDir, ORIGIN_FILE_DIR), item);
88
+
89
+ if (fileObj) {
90
+ const { filePath: originFilePath, file: fileName } = fileObj;
91
+
92
+ if (originFilePath) {
93
+ const failPath = path.join(workDir, Error_Files_Dir, fileName);
94
+ // ensureDirSync(failPath)
95
+
96
+ fs.copyFileSync(originFilePath, failPath);
97
+ logger(`第${index} / ${txtData.length}个复制完成!`);
98
+ }
99
+ }
100
+ });