tools_batch_files 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- package/error.txt +140 -0
- package/excel/demo-video-demo.xlsx +0 -0
- package/excel/demo-video.xlsx +0 -0
- package/filterFail.js +40 -0
- package/index.js +412 -0
- package/log.txt +118826 -0
- package/package.json +19 -0
- package/utils/index.js +16 -0
- package//344/275/277/347/224/250/350/257/264/346/230/216.txt +12 -0
- 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 +3 -0
package/error.txt
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
城市高楼建筑封顶施工航拍 (9).mp4
|
2
|
+
福建福州台江万达广场金融中心夜景航拍 (10).mp4
|
3
|
+
福建泉州城市夜景东海CBD夜景灯光航拍 (11)~1.mp4
|
4
|
+
广东澳门城市风光澳门塔航拍 (1).mp4
|
5
|
+
广东东莞城市CBD鸿福路高楼航拍 (9).mp4
|
6
|
+
广东佛山千灯湖城市风光航拍 (1).mp4.baiduyun.p.downloading
|
7
|
+
广东佛山千灯湖城市风光航拍 (1).mp4.baiduyun.p.downloading
|
8
|
+
广东佛山千灯湖城市风光航拍 (13).mp4.baiduyun.p.downloading
|
9
|
+
广东佛山千灯湖城市风光航拍 (14).mp4
|
10
|
+
广东佛山千灯湖城市风光航拍 (15).mp4
|
11
|
+
广东佛山千灯湖城市风光航拍 (17).mp4
|
12
|
+
广东佛山千灯湖城市风光航拍 (3).mp4
|
13
|
+
广东佛山千灯湖城市风光航拍 (13).mp4.baiduyun.p.downloading
|
14
|
+
广东佛山千灯湖城市风光航拍 (14).mp4
|
15
|
+
广东佛山千灯湖城市风光航拍 (15).mp4
|
16
|
+
广东佛山千灯湖城市风光航拍 (17).mp4
|
17
|
+
广东佛山千灯湖城市风光航拍 (3).mp4
|
18
|
+
广东佛山千灯湖城市风光航拍 (13).mp4.baiduyun.p.downloading
|
19
|
+
广东惠州市政府大楼航拍 (13).mp4
|
20
|
+
广东惠州市政府大楼航拍 (14).mp4
|
21
|
+
广东惠州市政府大楼航拍 (15).mp4
|
22
|
+
广东惠州市政府大楼航拍 (3).mp4
|
23
|
+
广东惠州市政府大楼航拍 (6).mp4
|
24
|
+
广东惠州市政府大楼航拍 (9).mp4
|
25
|
+
广东惠州市政府航拍 (1).mp4
|
26
|
+
广东惠州西湖泗洲塔航拍 (12).mp4
|
27
|
+
广东湛江人民广场人民大会堂航拍 (1).mp4
|
28
|
+
广东湛江人民广场人民大会堂航拍 (10).mp4
|
29
|
+
广东湛江人民广场人民大会堂航拍 (11).mp4
|
30
|
+
广东湛江人民广场人民大会堂航拍 (13).mp4
|
31
|
+
广东湛江人民广场人民大会堂航拍 (14).mp4
|
32
|
+
广东湛江人民广场人民大会堂航拍 (15).mp4
|
33
|
+
广东湛江人民广场人民大会堂航拍 (16).mp4
|
34
|
+
广东湛江人民广场人民大会堂航拍 (2).mp4
|
35
|
+
广东湛江人民广场人民大会堂航拍 (3).mp4
|
36
|
+
广东湛江人民广场人民大会堂航拍 (4).mp4
|
37
|
+
广东湛江人民广场人民大会堂航拍 (6).mp4
|
38
|
+
广东湛江人民广场人民大会堂航拍 (7).mp4
|
39
|
+
广东肇庆七星山5A景区星湖航拍 (4).mp4
|
40
|
+
广东肇庆星湖景区夜景航拍 (5).mp4
|
41
|
+
广东中山城市夜景灯光航拍 (2).mp4
|
42
|
+
广东中山市政府办公大楼航拍 (1).mp4
|
43
|
+
广东中山市政府办公大楼航拍 (2).mp4
|
44
|
+
广东中山市政府办公大楼航拍 (4).mp4
|
45
|
+
广东中山市政府办公大楼航拍 (5).mp4
|
46
|
+
广东中山市政府办公大楼航拍 (6).mp4
|
47
|
+
广东中山市政府办公大楼航拍 (7).mp4
|
48
|
+
广东中山市政府办公大楼航拍 (8).mp4
|
49
|
+
广东中山市政府办公大楼航拍 (9).mp4
|
50
|
+
广东中山市政府航拍 (3).mp4
|
51
|
+
广东中山市政府航拍 (4).mp4
|
52
|
+
广东中山市政府航拍 (8).mp4
|
53
|
+
广东中山市政府航拍 (9).mp4
|
54
|
+
广东中山市政府建筑航拍 (1).mp4
|
55
|
+
广东中山市政府建筑航拍 (2).mp4
|
56
|
+
广东中山市政府建筑航拍 (3).mp4
|
57
|
+
广西北海北部湾广场航拍 (1).mp4
|
58
|
+
航拍河南新乡市市政府 (4).mp4
|
59
|
+
航拍衡阳莲湖广场.mp4
|
60
|
+
航拍洛阳开元湖洛阳市政府夜景 (6).mp4
|
61
|
+
航拍阳朔龙脊山.mp4
|
62
|
+
航拍阳朔西街古塔.mp4
|
63
|
+
河北邯郸丛台公园航拍 (3).mp4
|
64
|
+
河北石家庄城市风光建设航拍 (48).mp4
|
65
|
+
河南许昌市委市政府航拍 (1).mp4
|
66
|
+
河南许昌市委市政府航拍 (2).mp4
|
67
|
+
河南许昌市委市政府航拍 (3).mp4
|
68
|
+
河南许昌市委市政府航拍 (5).mp4
|
69
|
+
河南许昌市委市政府航拍 (6).mp4
|
70
|
+
河南许昌市委市政府航拍 (8).mp4
|
71
|
+
河南许昌市政府航拍 (1).mp4
|
72
|
+
河南许昌市政府航拍 (15).mp4
|
73
|
+
河南许昌市政府航拍 (17).mp4
|
74
|
+
河南许昌市政府航拍 (7).mp4
|
75
|
+
河南许昌市政府航拍 (9).mp4
|
76
|
+
湖南湘潭大桥夜景交通航拍 (6).mp4
|
77
|
+
江南水乡乌镇5A景区航拍 (2).mp4
|
78
|
+
江南水乡乌镇5A景区航拍 (3).mp4
|
79
|
+
江南水乡乌镇5A景区航拍 (5).mp4
|
80
|
+
江苏常州城市风光地标建筑航拍 (101).mp4
|
81
|
+
江苏淮安城市风光周恩来故居航拍 (1)~1.mp4
|
82
|
+
江苏淮安城市风光周恩来故居航拍 (10)~1.mp4
|
83
|
+
江苏淮安城市风光周恩来故居航拍 (13)~1.mp4
|
84
|
+
江苏淮安城市风光周恩来故居航拍 (2)~1.mp4
|
85
|
+
江苏淮安城市风光周恩来故居航拍 (3)~1.mp4
|
86
|
+
江苏淮安城市风光周恩来故居航拍 (6)~1.mp4
|
87
|
+
江苏淮安周恩来故里5A景区航拍 (1).mp4
|
88
|
+
江苏淮安周恩来故里5A景区航拍 (12).mp4
|
89
|
+
江苏淮安周恩来故里5A景区航拍 (13).mp4
|
90
|
+
江苏淮安周恩来故里5A景区航拍 (2).mp4
|
91
|
+
江苏淮安周恩来故里5A景区航拍 (4).mp4
|
92
|
+
江苏淮安周恩来故里5A景区航拍 (9).mp4
|
93
|
+
江苏苏州观前街步行街航拍 (2).mp4
|
94
|
+
江苏宿迁城市风光大剧院体育馆图书馆市政府航拍 (23).mp4
|
95
|
+
江苏宿迁城市风光大剧院体育馆图书馆市政府航拍 (34).mp4
|
96
|
+
江苏宿迁城市风光大剧院体育馆图书馆市政府航拍 (38).mp4
|
97
|
+
江苏宿迁城市风光大剧院体育馆图书馆市政府航拍 (41).mp4
|
98
|
+
江苏宿迁城市风光大剧院体育馆图书馆市政府航拍 (54).mp4
|
99
|
+
江苏宿迁市政府航拍 (10).mp4
|
100
|
+
江苏宿迁市政府航拍 (11).mp4
|
101
|
+
江苏宿迁市政府航拍 (13).mp4
|
102
|
+
江苏宿迁市政府航拍 (3).mp4
|
103
|
+
江苏宿迁市政府航拍 (7).mp4
|
104
|
+
江苏徐州电视塔地标建筑航拍 (1).mp4
|
105
|
+
江苏徐州电视塔地标建筑航拍 (7).mp4
|
106
|
+
江苏徐州汉文化园航拍 (46).mp4
|
107
|
+
江苏扬州东关历史文化旅游区航拍 (11).mp4
|
108
|
+
江苏扬州瘦西湖5A景区 (6).mp4
|
109
|
+
江苏扬州瘦西湖5A景区航拍 (1).mp4
|
110
|
+
山东东营区政府航拍 (1).mp4
|
111
|
+
山东东营区政府航拍 (2).mp4
|
112
|
+
山东东营市政府航拍 (10).mp4
|
113
|
+
山东东营市政府航拍 (14).mp4
|
114
|
+
山东东营市政府航拍 (16).mp4
|
115
|
+
山东东营市政府航拍 (18).mp4
|
116
|
+
山东东营市政府航拍 (4).mp4
|
117
|
+
山东青岛北站夜景灯光航拍 (1).mp4
|
118
|
+
山东泰安泰山市政府中轴线航拍 (3).mp4
|
119
|
+
寺庙下雨素材苏州北寺塔实拍 (41).mp4
|
120
|
+
寺庙下雨素材苏州北寺塔实拍 (42).mp4
|
121
|
+
寺庙下雨素材苏州北寺塔实拍 (92).mp4
|
122
|
+
苏州周庄古镇5A景区江南水乡航拍 (3).mp4
|
123
|
+
天津城市风光航拍 (33).mp4
|
124
|
+
夏日树枝树叶植物实拍 (9).mp4
|
125
|
+
云南昆明金殿4A景区航拍 (13).mp4
|
126
|
+
浙江杭州城市风光航拍 (8).mp4
|
127
|
+
浙江杭州西湖全景大景航拍 (12).mp4
|
128
|
+
浙江宁波城市夜幕降临夜景灯光航拍 (5).mp4
|
129
|
+
浙江宁波天一阁博物馆5A景区航拍 (22).mp4
|
130
|
+
浙江绍兴城市夜景灯光交通航拍 (33).mp4
|
131
|
+
浙江台州市政府航拍 (1).mp4
|
132
|
+
浙江台州市政府航拍 (2).mp4
|
133
|
+
浙江台州市政府航拍 (4).mp4
|
134
|
+
浙江台州市政府航拍 (7).mp4
|
135
|
+
浙江台州市政府航拍 (9).mp4
|
136
|
+
中国蚌埠古民居博览园航拍 (14).mp4
|
137
|
+
中国蚌埠古民居博览园航拍 (15).mp4
|
138
|
+
珠海澳门城市风光航拍 (9).mp4
|
139
|
+
祖国大好河山安徽滁州琅琊山景区航拍 (27).mp4
|
140
|
+
祖国大好河山肇庆鼎湖山5A景区蝴蝶湖航拍 (7).mp4
|
Binary file
|
Binary file
|
package/filterFail.js
ADDED
@@ -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/index.js
ADDED
@@ -0,0 +1,412 @@
|
|
1
|
+
const ffmpeg = require("fluent-ffmpeg");
|
2
|
+
const fs = require("fs");
|
3
|
+
const path = require("path");
|
4
|
+
const XLSX = require("xlsx");
|
5
|
+
const FormData = require("form-data");
|
6
|
+
const axios = require("axios");
|
7
|
+
const generateUniqueHash = require("./utils/index");
|
8
|
+
|
9
|
+
const hashOneTime = generateUniqueHash();
|
10
|
+
|
11
|
+
// 源文件目录
|
12
|
+
const videoFolderPath = "video";
|
13
|
+
// 转码文件目录
|
14
|
+
const videoTo264oFolderPath = "output/videoTo264";
|
15
|
+
// 压缩后
|
16
|
+
const outputFolderPath = "output/video";
|
17
|
+
|
18
|
+
//转码后
|
19
|
+
const outputTransitionCodecFolderPath = "output/videoTo264"
|
20
|
+
|
21
|
+
const screenshotsFlodar = "output";
|
22
|
+
const screenshotsFlodarChild = "screenshots";
|
23
|
+
// 5张截图保存目录
|
24
|
+
const screenshotsDir = `${screenshotsFlodar}/${screenshotsFlodarChild}`;
|
25
|
+
|
26
|
+
//临时存储截图数组
|
27
|
+
let filenames = [];
|
28
|
+
|
29
|
+
/**
|
30
|
+
* 压缩视频
|
31
|
+
*/
|
32
|
+
const batchVideoFile = (fileName) => {
|
33
|
+
const inputFilePath = videoFolderPath + "/" + fileName;
|
34
|
+
|
35
|
+
const outputFilePath = outputFolderPath + "/" + "zip-" + fileName;
|
36
|
+
|
37
|
+
const command = ffmpeg();
|
38
|
+
const videoBitrate = 4000;
|
39
|
+
|
40
|
+
// 设置目标文件大小为50MB
|
41
|
+
// const targetFileSize = 50 * 1024 * 1024;
|
42
|
+
|
43
|
+
// 获取原视频文件大小(bytes)
|
44
|
+
// const inputFileSize = fs.statSync(inputFilePath).size;
|
45
|
+
|
46
|
+
// 根据目标文件大小估算比特率
|
47
|
+
// const videoBitrate = Math.floor((targetFileSize / inputFileSize) * 1000);
|
48
|
+
|
49
|
+
// 创建 ffmpeg 实例
|
50
|
+
// 输入视频文件
|
51
|
+
return new Promise((resolve, reject) => {
|
52
|
+
command
|
53
|
+
.input(inputFilePath)
|
54
|
+
// 设置视频尺寸为
|
55
|
+
// .size('1280x720')
|
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
|
+
// 设置输出文件路径
|
68
|
+
.output(outputFilePath)
|
69
|
+
// 开始处理
|
70
|
+
.on("start", () => {
|
71
|
+
logger("视频开始压缩……");
|
72
|
+
})
|
73
|
+
.on("end", () => {
|
74
|
+
logger("视频压缩完成!");
|
75
|
+
resolve(outputFilePath);
|
76
|
+
})
|
77
|
+
.on("error", (err) => {
|
78
|
+
reject(err);
|
79
|
+
logger("视频压缩出错:", err);
|
80
|
+
})
|
81
|
+
.run();
|
82
|
+
});
|
83
|
+
};
|
84
|
+
|
85
|
+
/**
|
86
|
+
* 转码
|
87
|
+
*/
|
88
|
+
// const transitionCodec = (fileName) => {
|
89
|
+
// const inputFilePath = videoFolderPath + "/" + fileName;
|
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);
|
125
|
+
|
126
|
+
return new Promise((resolve, reject) => {
|
127
|
+
command
|
128
|
+
.on("start", () => {
|
129
|
+
logger("开始截图");
|
130
|
+
})
|
131
|
+
.on("end", (stdout, stderr) => {
|
132
|
+
logger(inputFilePath + "截图完成!!!");
|
133
|
+
resolve();
|
134
|
+
})
|
135
|
+
.on("error", (err) => {
|
136
|
+
logger(inputFilePath + "截图出错:", err);
|
137
|
+
reject(err);
|
138
|
+
})
|
139
|
+
|
140
|
+
.screenshots({
|
141
|
+
timemarks: [0, "25%", "50%", "75%", "90%"],
|
142
|
+
folder: screenshotsDir,
|
143
|
+
filename: `screenshots-at-%s-%i-seconds.png`, //hash或者随机字符
|
144
|
+
size: '840x?'
|
145
|
+
})
|
146
|
+
.on("filenames", (filenamesArr) => {
|
147
|
+
// 将截图文件名存储到临时数组中
|
148
|
+
filenames.push(...filenamesArr);
|
149
|
+
});
|
150
|
+
});
|
151
|
+
};
|
152
|
+
|
153
|
+
/**
|
154
|
+
* 删除截图文件
|
155
|
+
*/
|
156
|
+
const deleteScreenshots = () => {
|
157
|
+
filenames.forEach((item) => {
|
158
|
+
const filePath = path.join("output", "screenshots", item);
|
159
|
+
|
160
|
+
fs.unlinkSync(filePath);
|
161
|
+
});
|
162
|
+
filenames = [];
|
163
|
+
}
|
164
|
+
|
165
|
+
/**
|
166
|
+
* 失败逻辑和 记录错误日志
|
167
|
+
*/
|
168
|
+
const updateFail = (err) => {
|
169
|
+
logger('xxxxx数据上传失败xxxxx: ' + err);
|
170
|
+
logger(`******************************************************************end`)
|
171
|
+
deleteScreenshots();
|
172
|
+
return Promise.reject();
|
173
|
+
}
|
174
|
+
|
175
|
+
/**
|
176
|
+
* 成功逻辑和 记录普通日志
|
177
|
+
*/
|
178
|
+
const updateSuccess = (id) => {
|
179
|
+
deleteScreenshots();
|
180
|
+
logger("数据上传成功!");
|
181
|
+
logger("id = " + id);
|
182
|
+
logger(`******************************************************************end`)
|
183
|
+
}
|
184
|
+
|
185
|
+
/**
|
186
|
+
* 拼接数据
|
187
|
+
* @param {number} type 1:压缩+截图 2:仅截图
|
188
|
+
* @param {object} options path路径
|
189
|
+
*/
|
190
|
+
const concatData = (
|
191
|
+
type,
|
192
|
+
{ title, keyword, videoFilePath, outputVideoFilePath, screenshotsDir }
|
193
|
+
) => {
|
194
|
+
const formData = new FormData();
|
195
|
+
const formatKeyword = keyword.split(",").join(" ");
|
196
|
+
|
197
|
+
if (type === 1) {
|
198
|
+
formData.append("zip_video", fs.createReadStream(outputVideoFilePath));
|
199
|
+
}
|
200
|
+
// else {
|
201
|
+
// formData.append("zip_video", fs.createReadStream(videoFilePath));
|
202
|
+
// }
|
203
|
+
|
204
|
+
formData.append("title", title);
|
205
|
+
formData.append("keyword", formatKeyword);
|
206
|
+
formData.append("source_video", fs.createReadStream(videoFilePath));
|
207
|
+
|
208
|
+
fs.readdirSync(screenshotsDir).forEach((item, index) => {
|
209
|
+
formData.append(
|
210
|
+
`screenshot${index + 1}`,
|
211
|
+
fs.createReadStream(`${screenshotsDir}/${item}`)
|
212
|
+
);
|
213
|
+
});
|
214
|
+
|
215
|
+
return postData(formData);
|
216
|
+
};
|
217
|
+
|
218
|
+
/**
|
219
|
+
* 数据传输
|
220
|
+
*/
|
221
|
+
const postData = (data) => {
|
222
|
+
logger("等待接口返回结果……");
|
223
|
+
|
224
|
+
return axios
|
225
|
+
.post("https://designer-api.51miz.com/designer/CallBack/uploadVideoData", data, {
|
226
|
+
headers: {
|
227
|
+
"Content-Type": "multipart/form-data",
|
228
|
+
},
|
229
|
+
})
|
230
|
+
.then((res) => {
|
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
|
+
})
|
248
|
+
};
|
249
|
+
|
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
|
+
/**
|
270
|
+
* 脚本开始前清空截图文件夹
|
271
|
+
*/
|
272
|
+
const clearDirectoryContents = (dirPath) => {
|
273
|
+
try {
|
274
|
+
const files = fs.readdirSync(dirPath); // 读取目录中的所有文件和目录
|
275
|
+
|
276
|
+
if (files.length > 0) {
|
277
|
+
files.forEach(function (filename) {
|
278
|
+
const filePath = path.join(dirPath, filename);
|
279
|
+
// const fileStat = fs.statSync(filePath);
|
280
|
+
|
281
|
+
// if (fileStat.isDirectory()) {
|
282
|
+
// // 如果是目录,则递归清空目录
|
283
|
+
// clearDirectoryContents(filePath);
|
284
|
+
// // 删除目录本身
|
285
|
+
// fs.rmdirSync(filePath);
|
286
|
+
// } else {
|
287
|
+
// 如果是文件,直接删除
|
288
|
+
fs.unlinkSync(filePath);
|
289
|
+
logger("截图文件夹已清空。")
|
290
|
+
// }
|
291
|
+
});
|
292
|
+
}
|
293
|
+
} catch (error) {
|
294
|
+
console.error('清空截图文件夹失败!', error);
|
295
|
+
}
|
296
|
+
};
|
297
|
+
|
298
|
+
/**
|
299
|
+
* 解析Excel数据
|
300
|
+
*/
|
301
|
+
const transitionExcelToJSON = () => {
|
302
|
+
//当前任务hash
|
303
|
+
const hashOrigin = generateUniqueHash();
|
304
|
+
const hash = hashOrigin.slice(0, 8);
|
305
|
+
logger('任务开始---' + hash);
|
306
|
+
// 读取 Excel 文件
|
307
|
+
const workbook = XLSX.readFile("excel/demo-video.xlsx");
|
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
|
+
}
|
385
|
+
|
386
|
+
|
387
|
+
const run = (index) => {
|
388
|
+
logger(`----------------------------------------第${index}条----------------------------------------`)
|
389
|
+
logger(`----------------------------------------${new Date()}----------------------------------------`)
|
390
|
+
|
391
|
+
const row = jsonData[index];
|
392
|
+
if (!row) {
|
393
|
+
return;
|
394
|
+
}
|
395
|
+
task(row)
|
396
|
+
.then(() => {
|
397
|
+
taskIndex++;
|
398
|
+
run(taskIndex);
|
399
|
+
})
|
400
|
+
.catch(() => {
|
401
|
+
taskIndex++;
|
402
|
+
run(taskIndex);
|
403
|
+
})
|
404
|
+
}
|
405
|
+
|
406
|
+
for (let i = 0; i < 1; i++) {
|
407
|
+
run(i);
|
408
|
+
}
|
409
|
+
|
410
|
+
};
|
411
|
+
|
412
|
+
transitionExcelToJSON();
|