(job, args, expectedSeconds)
| 1059 | } |
| 1060 | |
| 1061 | function startExportJob(job, args, expectedSeconds) { |
| 1062 | let cleanedUp = false; |
| 1063 | const cleanup = () => { |
| 1064 | if (cleanedUp) return; |
| 1065 | cleanedUp = true; |
| 1066 | for (const file of job.tempFiles || []) { |
| 1067 | fsp.unlink(file).catch(() => {}); |
| 1068 | } |
| 1069 | }; |
| 1070 | const child = spawn(FFMPEG, args, { |
| 1071 | cwd: ROOT, |
| 1072 | windowsHide: true, |
| 1073 | stdio: ["ignore", "ignore", "pipe", "pipe"] |
| 1074 | }); |
| 1075 | |
| 1076 | child.stdio[2].on("data", chunk => { |
| 1077 | job.stderr += chunk.toString(); |
| 1078 | if (job.stderr.length > 20000) job.stderr = job.stderr.slice(-20000); |
| 1079 | }); |
| 1080 | |
| 1081 | let progressBuffer = ""; |
| 1082 | child.stdio[3].on("data", chunk => { |
| 1083 | progressBuffer += chunk.toString(); |
| 1084 | const lines = progressBuffer.split(/\r?\n/); |
| 1085 | progressBuffer = lines.pop() || ""; |
| 1086 | for (const line of lines) { |
| 1087 | const [key, value] = line.split("="); |
| 1088 | if (key === "out_time_ms" || key === "out_time_us") { |
| 1089 | const seconds = Number(value) / 1000000; |
| 1090 | if (Number.isFinite(seconds)) { |
| 1091 | job.percent = Math.max(job.percent, Math.min(99, Math.round((seconds / expectedSeconds) * 100))); |
| 1092 | job.message = `Rendering... ${job.percent}%`; |
| 1093 | } |
| 1094 | } |
| 1095 | if (key === "progress" && value === "end") { |
| 1096 | job.percent = 100; |
| 1097 | job.message = "Finalizing..."; |
| 1098 | } |
| 1099 | } |
| 1100 | }); |
| 1101 | |
| 1102 | child.on("error", error => { |
| 1103 | job.status = "error"; |
| 1104 | job.error = explainMissingTool(error, FFMPEG); |
| 1105 | job.message = "Export failed."; |
| 1106 | cleanup(); |
| 1107 | }); |
| 1108 | |
| 1109 | child.on("close", code => { |
| 1110 | if (code === 0) { |
| 1111 | job.status = "complete"; |
| 1112 | job.percent = 100; |
| 1113 | job.message = "Export complete."; |
| 1114 | } else if (job.status !== "error") { |
| 1115 | job.status = "error"; |
| 1116 | job.error = `ffmpeg exited with code ${code}`; |
| 1117 | job.message = "Export failed."; |
| 1118 | } |
no test coverage detected