tools_batch_files 1.0.0 → 1.0.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/README.md +46 -0
- package/imgs/miz-watermark.png +0 -0
- package/index.js +390 -297
- package/package.json +19 -3
- package//347/202/271/346/210/221/346/211/271/351/207/217/344/270/212/344/274/240/350/247/206/351/242/221.bat +1 -1
- package/error.txt +0 -140
- package/excel/demo-video-demo.xlsx +0 -0
- package/excel/demo-video.xlsx +0 -0
- package/filterFail.js +0 -40
- package/log.txt +0 -118826
- package/utils/index.js +0 -16
- package//344/275/277/347/224/250/350/257/264/346/230/216.txt +0 -12
package/index.js
CHANGED
@@ -1,72 +1,151 @@
|
|
1
|
+
const XLSX = require("xlsx");
|
1
2
|
const ffmpeg = require("fluent-ffmpeg");
|
2
3
|
const fs = require("fs");
|
3
4
|
const path = require("path");
|
4
|
-
const
|
5
|
-
const
|
5
|
+
const fs_asnyc = require("fs").promises;
|
6
|
+
const archiver = require("archiver");
|
6
7
|
const axios = require("axios");
|
7
8
|
const generateUniqueHash = require("./utils/index");
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
const
|
13
|
-
//
|
14
|
-
const
|
15
|
-
|
16
|
-
const
|
17
|
-
|
18
|
-
|
19
|
-
const
|
10
|
+
// 表格目录
|
11
|
+
const excelDir = path.join(__dirname, "excel", "excel.xlsx");
|
12
|
+
//水印
|
13
|
+
const watermarkImage = path.join(process.cwd(), "imgs", "miz-watermark.png");
|
14
|
+
// 截图数量
|
15
|
+
const SCREENSHOT_COUNT = 5;
|
16
|
+
const ORIGIN_FILE_DIR = "video";
|
17
|
+
const ZIP_VIDEO_DIR = "zip_video";
|
18
|
+
const SCREENSHOT_DIR = "screenshot";
|
19
|
+
const ZIP_WATERMARK_VIDEO_DIR = "zip_watermark_video";
|
20
|
+
const ZIP_SCREENSHOT_DIR = "screenshot_watermark";
|
21
|
+
const SOURCE_VIDEO_DIR = "source_video";
|
22
|
+
|
23
|
+
//并发数量
|
24
|
+
const queueCount = 1;
|
25
|
+
|
26
|
+
//任务开始序列
|
27
|
+
let taskIndex = 0;
|
20
28
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
29
|
+
/**
|
30
|
+
* 递归遍历文件夹,查找mp4文件
|
31
|
+
* @param {*} dir 文件夹路径
|
32
|
+
* @param {*} fileName 文件名称
|
33
|
+
* @returns string 匹配成功的文件路径
|
34
|
+
*/
|
35
|
+
function findFileInDir(dir, fileName) {
|
36
|
+
const files = fs.readdirSync(dir);
|
37
|
+
for (const file of files) {
|
38
|
+
const filePath = path.join(dir, file);
|
39
|
+
const stat = fs.statSync(filePath);
|
40
|
+
if (stat.isDirectory()) {
|
41
|
+
const result = findFileInDir(filePath, fileName);
|
42
|
+
if (result) {
|
43
|
+
return result;
|
44
|
+
}
|
45
|
+
} else if (
|
46
|
+
file.startsWith(fileName) &&
|
47
|
+
(file.endsWith(".mp4") || file.endsWith(".mov"))
|
48
|
+
) {
|
49
|
+
return filePath;
|
50
|
+
}
|
51
|
+
}
|
52
|
+
return null;
|
53
|
+
}
|
25
54
|
|
26
|
-
|
27
|
-
|
55
|
+
/**
|
56
|
+
* 确保目录存在,如果不存在则创建它
|
57
|
+
*/
|
58
|
+
function ensureDirSync(dirpath) {
|
59
|
+
try {
|
60
|
+
if (!fs.existsSync(dirpath)) {
|
61
|
+
fs.mkdirSync(dirpath, { recursive: true });
|
62
|
+
logger(`目录创建成功:${dirpath}`);
|
63
|
+
}
|
64
|
+
} catch (err) {
|
65
|
+
logger(`创建目录时出错:${err}`);
|
66
|
+
throw err;
|
67
|
+
}
|
68
|
+
}
|
28
69
|
|
29
70
|
/**
|
30
|
-
*
|
71
|
+
* 检查文件是否存在
|
31
72
|
*/
|
32
|
-
|
33
|
-
|
73
|
+
function checkFileExistence(path) {
|
74
|
+
return new Promise((resolve, reject) => {
|
75
|
+
fs.access(path, fs.constants.F_OK, (err) => {
|
76
|
+
if (!err) {
|
77
|
+
logger("文件存在,可以访问");
|
78
|
+
resolve(true);
|
79
|
+
} else {
|
80
|
+
logger(`视频文件 ${path} 不存在`);
|
81
|
+
resolve(false);
|
82
|
+
}
|
83
|
+
});
|
84
|
+
});
|
85
|
+
}
|
34
86
|
|
35
|
-
|
87
|
+
//普通日志
|
88
|
+
const logger = (log, hash, err) => {
|
89
|
+
console.log(log);
|
90
|
+
fs.writeFileSync("log.txt", log + "\n", {
|
91
|
+
flag: "a",
|
92
|
+
});
|
93
|
+
};
|
94
|
+
|
95
|
+
//错误日志
|
96
|
+
const disposeError = (hash, err) => {
|
97
|
+
logger("---任务失败---", hash, err);
|
98
|
+
logger(
|
99
|
+
`******************************************************************end`
|
100
|
+
);
|
101
|
+
fs.writeFileSync("error.txt", err + "\n", {
|
102
|
+
flag: "a",
|
103
|
+
});
|
104
|
+
};
|
36
105
|
|
37
|
-
|
38
|
-
const
|
106
|
+
const readExcel = (path) => {
|
107
|
+
const workbook = XLSX.readFile(path);
|
39
108
|
|
40
|
-
//
|
41
|
-
|
109
|
+
// 获取第一个工作表(Sheet)
|
110
|
+
const firstSheetName = workbook.SheetNames[0];
|
111
|
+
const worksheet = workbook.Sheets[firstSheetName];
|
42
112
|
|
43
|
-
//
|
44
|
-
|
113
|
+
// 将工作表转换为 JSON 对象
|
114
|
+
const jsonData = XLSX.utils.sheet_to_json(worksheet);
|
115
|
+
return jsonData;
|
116
|
+
};
|
45
117
|
|
46
|
-
|
47
|
-
|
118
|
+
function logFileSize(path) {
|
119
|
+
const fileSize = fs.statSync(path).size / (1024 * 1024);
|
120
|
+
const formattedSize = fileSize.toFixed(2); // 保留两位小数
|
121
|
+
logger(`视频大小:${formattedSize}M`);
|
122
|
+
}
|
48
123
|
|
49
|
-
|
50
|
-
|
124
|
+
/**
|
125
|
+
* 压缩视频
|
126
|
+
* @param {*} fileName 文件名
|
127
|
+
* @param {*} outputFileDir 产物文件夹
|
128
|
+
* @param {*} inputFilePath 源文件路径
|
129
|
+
* @returns Promise
|
130
|
+
*/
|
131
|
+
const compressVideo = (fileName, outputFileDir, inputFilePath) => {
|
132
|
+
// const inputFilePath = path.join(__dirname, "video", fileName);
|
133
|
+
const outputFilePath = path.join(
|
134
|
+
outputFileDir,
|
135
|
+
ZIP_VIDEO_DIR,
|
136
|
+
`zip-${fileName}`
|
137
|
+
);
|
138
|
+
const comand = ffmpeg(inputFilePath);
|
139
|
+
|
140
|
+
ensureDirSync(path.dirname(outputFilePath));
|
51
141
|
return new Promise((resolve, reject) => {
|
52
|
-
|
53
|
-
.
|
54
|
-
|
55
|
-
// .size(
|
56
|
-
|
57
|
-
.videoCodec('libx264')
|
58
|
-
// .videoCodec("mpeg4")
|
59
|
-
|
60
|
-
// 设置视频比特率
|
61
|
-
.videoBitrate(videoBitrate + "k")
|
62
|
-
// 设置音频编解码参数
|
63
|
-
// .audioCodec("aac")
|
64
|
-
.audioCodec('libmp3lame')
|
65
|
-
// 设置音频比特率
|
66
|
-
// .audioBitrate('128k')
|
67
|
-
// 设置输出文件路径
|
142
|
+
comand
|
143
|
+
.videoCodec("libx264")
|
144
|
+
.size("1280x?")
|
145
|
+
// .size("1280x720")
|
146
|
+
|
68
147
|
.output(outputFilePath)
|
69
|
-
|
148
|
+
|
70
149
|
.on("start", () => {
|
71
150
|
logger("视频开始压缩……");
|
72
151
|
})
|
@@ -75,58 +154,26 @@ const batchVideoFile = (fileName) => {
|
|
75
154
|
resolve(outputFilePath);
|
76
155
|
})
|
77
156
|
.on("error", (err) => {
|
78
|
-
reject(err);
|
79
157
|
logger("视频压缩出错:", err);
|
158
|
+
reject(err);
|
80
159
|
})
|
81
160
|
.run();
|
82
161
|
});
|
83
162
|
};
|
84
163
|
|
85
164
|
/**
|
86
|
-
*
|
165
|
+
* 生成5张截图
|
87
166
|
*/
|
88
|
-
|
89
|
-
|
90
|
-
// const outputFilePath = outputTransitionCodecFolderPath + "/" + fileName;
|
91
|
-
|
92
|
-
// const command = ffmpeg(inputFilePath);
|
93
|
-
|
94
|
-
// return new Promise((resolve, reject) => {
|
95
|
-
// command
|
96
|
-
// .input(inputFilePath)
|
97
|
-
// // .videoCodec('libx264')
|
98
|
-
// .videoCodec("mpeg4")
|
99
|
-
|
100
|
-
// .output(outputFilePath)
|
101
|
-
// .on("start", () => {
|
102
|
-
// logger("视频开始转码……");
|
103
|
-
// })
|
104
|
-
// .on("end", () => {
|
105
|
-
// logger("视频转码完成!");
|
106
|
-
// const videoTo264Path = path.join("output", "videoTo264", fileName); // 转码后视频文件路径
|
107
|
-
|
108
|
-
// resolve(videoTo264Path);
|
109
|
-
// })
|
110
|
-
// .on("error", (err) => {
|
111
|
-
// reject(err);
|
112
|
-
// logger("视频转码出错:", err);
|
113
|
-
// })
|
114
|
-
// .run();
|
115
|
-
// });
|
116
|
-
// }
|
117
|
-
|
118
|
-
/**
|
119
|
-
* 截图
|
120
|
-
*/
|
121
|
-
const get5Screenshots = (fileName) => {
|
122
|
-
const inputFilePath = videoFolderPath + "/" + fileName;
|
123
|
-
|
124
|
-
const command = ffmpeg(inputFilePath);
|
167
|
+
const get5Screenshots = (fileName, outputFilePath, inputFilePath) => {
|
168
|
+
const folderPath = path.join(outputFilePath, SCREENSHOT_DIR); // 构建完整的目录路径
|
125
169
|
|
170
|
+
ensureDirSync(folderPath);
|
126
171
|
return new Promise((resolve, reject) => {
|
127
|
-
|
172
|
+
const screenshotsCommand = ffmpeg(inputFilePath);
|
173
|
+
|
174
|
+
screenshotsCommand
|
128
175
|
.on("start", () => {
|
129
|
-
logger("
|
176
|
+
logger("开始从视频中截图……");
|
130
177
|
})
|
131
178
|
.on("end", (stdout, stderr) => {
|
132
179
|
logger(inputFilePath + "截图完成!!!");
|
@@ -139,159 +186,281 @@ const get5Screenshots = (fileName) => {
|
|
139
186
|
|
140
187
|
.screenshots({
|
141
188
|
timemarks: [0, "25%", "50%", "75%", "90%"],
|
142
|
-
folder:
|
143
|
-
filename: `screenshots
|
144
|
-
size:
|
145
|
-
})
|
146
|
-
.on("filenames", (filenamesArr) => {
|
147
|
-
// 将截图文件名存储到临时数组中
|
148
|
-
filenames.push(...filenamesArr);
|
189
|
+
folder: folderPath,
|
190
|
+
filename: `screenshots-%i.png`,
|
191
|
+
size: "840x?",
|
149
192
|
});
|
150
193
|
});
|
151
194
|
};
|
152
195
|
|
153
196
|
/**
|
154
|
-
*
|
197
|
+
* 视频打水印
|
155
198
|
*/
|
156
|
-
const
|
157
|
-
|
158
|
-
|
199
|
+
const watermarkVideo = (fileName, outputFileDir) => {
|
200
|
+
//用压缩后的视频作为输入源,增加效率
|
201
|
+
const inputFilePath = path.join(
|
202
|
+
outputFileDir,
|
203
|
+
ZIP_VIDEO_DIR,
|
204
|
+
`zip-${fileName}`
|
205
|
+
);
|
206
|
+
const outputFilePath = path.join(
|
207
|
+
outputFileDir,
|
208
|
+
ZIP_WATERMARK_VIDEO_DIR,
|
209
|
+
`zip-watermark-${fileName}`
|
210
|
+
);
|
211
|
+
const watermarkCommand = ffmpeg(inputFilePath);
|
212
|
+
|
213
|
+
ensureDirSync(path.dirname(outputFilePath));
|
214
|
+
|
215
|
+
return new Promise((resolve, reject) => {
|
216
|
+
watermarkCommand
|
217
|
+
.input(watermarkImage) // 添加水印图片作为第二个输入
|
159
218
|
|
160
|
-
|
219
|
+
.complexFilter([
|
220
|
+
"[0:v][1:v]overlay=(main_w-overlay_w)/2:(main_h-overlay_h)/2",
|
221
|
+
])
|
222
|
+
.output(outputFilePath)
|
223
|
+
.on("start", () => {
|
224
|
+
logger("视频开始打水印……");
|
225
|
+
})
|
226
|
+
.on("end", () => {
|
227
|
+
logger("视频打水印完成!");
|
228
|
+
resolve(outputFilePath);
|
229
|
+
})
|
230
|
+
.on("error", (err) => {
|
231
|
+
logger("视频打水印出错:", err);
|
232
|
+
reject(err);
|
233
|
+
})
|
234
|
+
.run();
|
161
235
|
});
|
162
|
-
|
163
|
-
}
|
236
|
+
};
|
164
237
|
|
165
238
|
/**
|
166
|
-
*
|
239
|
+
* 截图水印,根据截图打水印
|
167
240
|
*/
|
168
|
-
const
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
241
|
+
const watermarkScreenshots = (indexFilePath) => {
|
242
|
+
//存放水印截图的文件夹
|
243
|
+
const inputDir = path.join(indexFilePath, SCREENSHOT_DIR);
|
244
|
+
const outputDir = path.join(indexFilePath, ZIP_SCREENSHOT_DIR);
|
245
|
+
ensureDirSync(outputDir);
|
246
|
+
|
247
|
+
return new Promise((resolve, reject) => {
|
248
|
+
fs.readdir(inputDir, (err, files) => {
|
249
|
+
if (err) {
|
250
|
+
logger("读取截图文件夹出错: " + err);
|
251
|
+
return;
|
252
|
+
} else {
|
253
|
+
files.forEach((file, index) => {
|
254
|
+
if (path.extname(file) === ".png") {
|
255
|
+
const inputFile = path.join(inputDir, file);
|
256
|
+
const outputFile = path.join(outputDir, file);
|
257
|
+
|
258
|
+
const watermarkScreenshotsComand = ffmpeg(inputFile);
|
259
|
+
watermarkScreenshotsComand
|
260
|
+
.input(watermarkImage)
|
261
|
+
.complexFilter([
|
262
|
+
"[0:v][1:v]overlay=(main_w-overlay_w)/2:(main_h-overlay_h)/2",
|
263
|
+
])
|
264
|
+
.output(outputFile)
|
265
|
+
.on("error", (err) => {
|
266
|
+
logger("截图添加水印出错: ", err);
|
267
|
+
reject();
|
268
|
+
})
|
269
|
+
.on("end", () => {
|
270
|
+
logger("截图水印添加完成: " + outputFile);
|
271
|
+
if (index + 1 === SCREENSHOT_COUNT) {
|
272
|
+
resolve();
|
273
|
+
}
|
274
|
+
})
|
275
|
+
.run();
|
276
|
+
} else {
|
277
|
+
logger("截图不是png文件,无法添加图片水印!");
|
278
|
+
reject();
|
279
|
+
}
|
280
|
+
});
|
281
|
+
}
|
282
|
+
});
|
283
|
+
});
|
284
|
+
};
|
174
285
|
|
175
286
|
/**
|
176
|
-
*
|
287
|
+
* 打包物料
|
177
288
|
*/
|
178
|
-
const
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
289
|
+
const archiveZip = (fileName, inputPath) => {
|
290
|
+
const sourceVideoPath = path.join(__dirname, "video", fileName);
|
291
|
+
const zipDir = path.join(inputPath, "zip");
|
292
|
+
|
293
|
+
ensureDirSync(zipDir);
|
294
|
+
// create a file to stream archive data to.
|
295
|
+
const zipStream = fs.createWriteStream(path.join(zipDir, "package.zip"));
|
296
|
+
const archive = archiver("zip", {
|
297
|
+
zlib: { level: 9 }, // Sets the compression level.
|
298
|
+
});
|
299
|
+
|
300
|
+
return new Promise((resolve, reject) => {
|
301
|
+
// listen for all archive data to be written
|
302
|
+
// 'close' event is fired only when a file descriptor is involved
|
303
|
+
zipStream.on("close", function () {
|
304
|
+
logger("压缩数据:" + archive.pointer() + " total bytes");
|
305
|
+
logger(
|
306
|
+
"完成归档archiver has been finalized and the output file descriptor has closed."
|
307
|
+
);
|
308
|
+
resolve();
|
309
|
+
});
|
310
|
+
|
311
|
+
// good practice to catch warnings (ie stat failures and other non-blocking errors)
|
312
|
+
archive.on("warning", function (err) {
|
313
|
+
if (err.code === "ENOENT") {
|
314
|
+
logger("压缩-warning:" + err);
|
315
|
+
} else {
|
316
|
+
// throw error
|
317
|
+
throw err;
|
318
|
+
}
|
319
|
+
});
|
320
|
+
|
321
|
+
// good practice to catch this error explicitly
|
322
|
+
archive.on("error", function (err) {
|
323
|
+
logger("压缩失败!" + err);
|
324
|
+
reject();
|
325
|
+
});
|
326
|
+
|
327
|
+
archive.pipe(zipStream);
|
328
|
+
|
329
|
+
const directories = [
|
330
|
+
ZIP_VIDEO_DIR,
|
331
|
+
SCREENSHOT_DIR,
|
332
|
+
ZIP_WATERMARK_VIDEO_DIR,
|
333
|
+
ZIP_SCREENSHOT_DIR,
|
334
|
+
];
|
335
|
+
|
336
|
+
directories.forEach((dir) => {
|
337
|
+
const dirPath = path.join(inputPath, dir);
|
338
|
+
archive.directory(dirPath, dir);
|
339
|
+
});
|
340
|
+
|
341
|
+
archive.file(sourceVideoPath, {
|
342
|
+
name: path.join(SOURCE_VIDEO_DIR, fileName),
|
343
|
+
});
|
344
|
+
// 完成归档
|
345
|
+
archive.finalize();
|
346
|
+
});
|
347
|
+
};
|
184
348
|
|
185
349
|
/**
|
186
|
-
*
|
187
|
-
* @param {number} type 1:压缩+截图 2:仅截图
|
188
|
-
* @param {object} options path路径
|
350
|
+
* 获取 元数据
|
189
351
|
*/
|
190
|
-
const
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
)
|
352
|
+
const getMetadata = async (fileName, indexFilePath) => {
|
353
|
+
const inputFilePath = path.join(__dirname, "video", fileName);
|
354
|
+
//第一张截图
|
355
|
+
const photoInputPath = path.join(
|
356
|
+
indexFilePath,
|
357
|
+
SCREENSHOT_DIR,
|
358
|
+
"screenshots-1.png"
|
359
|
+
);
|
360
|
+
|
361
|
+
const videoMetadataComand = ffmpeg(inputFilePath);
|
362
|
+
const photoMetadataComand = ffmpeg(photoInputPath);
|
363
|
+
const metaDataParams = {
|
364
|
+
videoWidth: "",
|
365
|
+
videoHeight: "",
|
366
|
+
videoSize: "",
|
367
|
+
videoDuration: "",
|
368
|
+
videoId: "",
|
369
|
+
pr: "",
|
370
|
+
photoWidth: "",
|
371
|
+
photoHeight: "",
|
372
|
+
};
|
373
|
+
await new Promise((resolve, reject) => {
|
374
|
+
videoMetadataComand.ffprobe(function (err, metadata) {
|
375
|
+
const videoStream = metadata.streams.find(
|
376
|
+
(s) => s.codec_type === "video"
|
377
|
+
);
|
378
|
+
const formatStream = metadata.format;
|
379
|
+
|
380
|
+
metaDataParams.videoWidth = videoStream.width;
|
381
|
+
metaDataParams.videoHeight = videoStream.height;
|
382
|
+
metaDataParams.videoDuration = videoStream.duration;
|
383
|
+
metaDataParams.videoSize = formatStream.size;
|
384
|
+
resolve();
|
385
|
+
});
|
386
|
+
});
|
387
|
+
|
388
|
+
await new Promise((resolve, reject) => {
|
389
|
+
photoMetadataComand.ffprobe(function (err, metadata) {
|
390
|
+
const photoStream = metadata.streams.find(
|
391
|
+
(s) => s.codec_type === "video"
|
392
|
+
);
|
393
|
+
|
394
|
+
metaDataParams.photoWidth = photoStream.width;
|
395
|
+
metaDataParams.photoHeight = photoStream.height;
|
396
|
+
resolve();
|
397
|
+
});
|
213
398
|
});
|
214
399
|
|
215
|
-
return
|
400
|
+
return metaDataParams;
|
216
401
|
};
|
217
402
|
|
218
403
|
/**
|
219
|
-
*
|
404
|
+
* 接口
|
220
405
|
*/
|
221
406
|
const postData = (data) => {
|
222
407
|
logger("等待接口返回结果……");
|
223
408
|
|
224
|
-
return axios
|
225
|
-
|
409
|
+
return axios.post(
|
410
|
+
"https://designer-api.51miz.com/designer/CallBack/uploadVideoData",
|
411
|
+
data,
|
412
|
+
{
|
226
413
|
headers: {
|
227
414
|
"Content-Type": "multipart/form-data",
|
228
415
|
},
|
229
|
-
}
|
230
|
-
|
231
|
-
console.log(res.data);
|
232
|
-
if (res.data.status == 200) {
|
233
|
-
updateSuccess(res.data.id)
|
234
|
-
|
235
|
-
} else {
|
236
|
-
logger('xxxxx数据上传失败xxxxx: ');
|
237
|
-
logger(`******************************************************************end`)
|
238
|
-
deleteScreenshots();
|
239
|
-
return Promise.reject();
|
240
|
-
}
|
241
|
-
})
|
242
|
-
.catch((err) => {
|
243
|
-
logger('xxxxx数据上传失败xxxxx: ' + err);
|
244
|
-
logger(`******************************************************************end`)
|
245
|
-
deleteScreenshots();
|
246
|
-
return Promise.reject();
|
247
|
-
})
|
416
|
+
}
|
417
|
+
);
|
248
418
|
};
|
249
419
|
|
250
|
-
//处理错误信息
|
251
|
-
const disposeError = (hash, err) => {
|
252
|
-
logger('任务失败---', hash, err);
|
253
|
-
fs.writeFileSync(hash + '-error.txt', err + '\n', {
|
254
|
-
flag: 'a'
|
255
|
-
})
|
256
|
-
fs.writeFileSync('error.txt', err + '\n', {
|
257
|
-
flag: 'a'
|
258
|
-
})
|
259
|
-
}
|
260
|
-
|
261
|
-
//处理错误信息
|
262
|
-
const logger = (log) => {
|
263
|
-
console.log(log);
|
264
|
-
fs.writeFileSync('log.txt', log + '\n', {
|
265
|
-
flag: 'a'
|
266
|
-
})
|
267
|
-
}
|
268
|
-
|
269
420
|
/**
|
270
|
-
*
|
421
|
+
* 任务
|
271
422
|
*/
|
272
|
-
const
|
423
|
+
const task = async (row, index) => {
|
424
|
+
logger(
|
425
|
+
"**************************" + row.fileName + "**************************"
|
426
|
+
);
|
427
|
+
// Excel的列名分别为: fileName title keyword
|
428
|
+
const fileName = row.fileName;
|
429
|
+
// const originFilePath = path.join(__dirname, "video", fileName); // 源视频文件路径
|
430
|
+
const originFilePath = findFileInDir(
|
431
|
+
path.join(__dirname, ORIGIN_FILE_DIR),
|
432
|
+
fileName
|
433
|
+
); // 源视频文件路径
|
434
|
+
console.log("originFilePath");
|
435
|
+
console.log(originFilePath);
|
436
|
+
|
437
|
+
//index文件夹 output/0
|
438
|
+
const indexFilePath = path.join(__dirname, "output", index + "");
|
439
|
+
|
273
440
|
try {
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
logger("截图文件夹已清空。")
|
290
|
-
// }
|
291
|
-
});
|
292
|
-
}
|
441
|
+
await fs_asnyc.access(originFilePath, fs_asnyc.constants.F_OK);
|
442
|
+
logFileSize(originFilePath);
|
443
|
+
await compressVideo(fileName, indexFilePath, originFilePath);
|
444
|
+
await get5Screenshots(fileName, indexFilePath, originFilePath);
|
445
|
+
await watermarkVideo(fileName, indexFilePath);
|
446
|
+
await watermarkScreenshots(indexFilePath);
|
447
|
+
await archiveZip(fileName, indexFilePath);
|
448
|
+
const dataParams = await getMetadata(fileName, indexFilePath);
|
449
|
+
console.log("dataParams");
|
450
|
+
console.log(dataParams);
|
451
|
+
|
452
|
+
// await postData(dataParams);
|
453
|
+
logger(
|
454
|
+
`----------------------------------------第${index}条结束---------------------------------end`
|
455
|
+
);
|
293
456
|
} catch (error) {
|
294
|
-
|
457
|
+
// 可以约定code,来表示不同的错误信息
|
458
|
+
if (error.code === "ENOENT") {
|
459
|
+
logger(`视频文件 ${fileName} 不存在`);
|
460
|
+
} else {
|
461
|
+
logger("视频处理失败", error.message);
|
462
|
+
}
|
463
|
+
disposeError(hash, fileName);
|
295
464
|
}
|
296
465
|
};
|
297
466
|
|
@@ -302,97 +471,22 @@ const transitionExcelToJSON = () => {
|
|
302
471
|
//当前任务hash
|
303
472
|
const hashOrigin = generateUniqueHash();
|
304
473
|
const hash = hashOrigin.slice(0, 8);
|
305
|
-
logger(
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
// 获取第一个工作表(Sheet)
|
310
|
-
const firstSheetName = workbook.SheetNames[0];
|
311
|
-
const worksheet = workbook.Sheets[firstSheetName];
|
312
|
-
|
313
|
-
// 将工作表转换为 JSON 对象
|
314
|
-
const jsonData = XLSX.utils.sheet_to_json(worksheet);
|
315
|
-
|
316
|
-
let taskIndex = 1680;
|
317
|
-
|
318
|
-
const task = (row) => {
|
319
|
-
logger("**************************" + row.fileName + "**************************");
|
320
|
-
clearDirectoryContents(screenshotsDir);
|
321
|
-
return new Promise((resolve, reject) => {
|
322
|
-
const fileName = row.fileName; // Excel的列名分别为: fileName title keyword
|
323
|
-
const title = row.title;
|
324
|
-
const keyword = row.keyword;
|
325
|
-
|
326
|
-
const videoFilePath = path.join(videoFolderPath, fileName); // 视频文件路径
|
327
|
-
|
328
|
-
// 检查视频文件是否存在
|
329
|
-
fs.access(videoFilePath, fs.constants.F_OK, (err) => {
|
330
|
-
if (!err) {
|
331
|
-
const fileSize = fs.statSync(videoFilePath).size / (1024 * 1024);
|
332
|
-
logger("视频大小" + fileSize + 'M');
|
333
|
-
|
334
|
-
//如果原视频 < 50M ,那么不压缩
|
335
|
-
// if (fileSize > 50) {
|
336
|
-
|
337
|
-
Promise.all([batchVideoFile(fileName), get5Screenshots(fileName)])
|
338
|
-
.then(([outputVideoFilePath]) => {
|
339
|
-
console.log("视频处理结束……成功");
|
340
|
-
concatData(1, {
|
341
|
-
title,
|
342
|
-
keyword,
|
343
|
-
videoFilePath,
|
344
|
-
outputVideoFilePath,
|
345
|
-
screenshotsDir,
|
346
|
-
}).then(() => {
|
347
|
-
console.log('concatData成功');
|
348
|
-
resolve();
|
349
|
-
}).catch((reason) => {
|
350
|
-
disposeError(hash, fileName);
|
351
|
-
logger("压缩+截图失败" + reason);
|
352
|
-
reject();
|
353
|
-
});
|
354
|
-
})
|
355
|
-
.catch((reason) => {
|
356
|
-
disposeError(hash, fileName);
|
357
|
-
logger("All失败" + reason);
|
358
|
-
reject();
|
359
|
-
});
|
360
|
-
|
361
|
-
// } else {
|
362
|
-
// logger("<50M,不压缩");
|
363
|
-
// get5Screenshots(fileName).then(() => {
|
364
|
-
// concatData(2, {
|
365
|
-
// title,
|
366
|
-
// keyword,
|
367
|
-
// videoFilePath,
|
368
|
-
// outputVideoFilePath: '',
|
369
|
-
// screenshotsDir,
|
370
|
-
// }).then(() => {
|
371
|
-
// resolve();
|
372
|
-
// }).catch((reason) => {
|
373
|
-
// disposeError(hash, fileName);
|
374
|
-
// logger("仅截图 失败" + reason);
|
375
|
-
// });
|
376
|
-
// });
|
377
|
-
// }
|
378
|
-
} else {
|
379
|
-
logger(`视频文件 ${fileName} 不存在`);
|
380
|
-
resolve();
|
381
|
-
}
|
382
|
-
});
|
383
|
-
})
|
384
|
-
}
|
474
|
+
logger("任务开始---" + hash);
|
475
|
+
logger("当前目录: " + __dirname);
|
476
|
+
logger("工作目录: " + process.cwd());
|
385
477
|
|
478
|
+
const jsonData = readExcel(excelDir);
|
386
479
|
|
387
480
|
const run = (index) => {
|
388
|
-
logger(`----------------------------------------第${index}条----------------------------------------`)
|
389
|
-
logger(`----------------------------------------${new Date()}----------------------------------------`)
|
390
|
-
|
391
481
|
const row = jsonData[index];
|
392
482
|
if (!row) {
|
393
483
|
return;
|
394
484
|
}
|
395
|
-
|
485
|
+
logger(
|
486
|
+
`----------------------------------------第${index}条开始------------------------------------`
|
487
|
+
);
|
488
|
+
logger(`-------------------${new Date()}------------------`);
|
489
|
+
task(row, index)
|
396
490
|
.then(() => {
|
397
491
|
taskIndex++;
|
398
492
|
run(taskIndex);
|
@@ -400,13 +494,12 @@ const transitionExcelToJSON = () => {
|
|
400
494
|
.catch(() => {
|
401
495
|
taskIndex++;
|
402
496
|
run(taskIndex);
|
403
|
-
})
|
404
|
-
}
|
497
|
+
});
|
498
|
+
};
|
405
499
|
|
406
|
-
for (let i = 0; i <
|
500
|
+
for (let i = 0; i < queueCount; i++) {
|
407
501
|
run(i);
|
408
502
|
}
|
409
|
-
|
410
503
|
};
|
411
504
|
|
412
505
|
transitionExcelToJSON();
|