tools_batch_files 1.0.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.
- 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();
|