11
This commit is contained in:
@@ -169,8 +169,36 @@ async function uploadFile(baseUrl, apiKey, filePath) {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
function locateFfmpegBinary() {
|
||||
const fromEnv = process.env.AICLIENT_FFMPEG_PATH;
|
||||
if (fromEnv && fs.existsSync(fromEnv)) return fromEnv;
|
||||
return process.platform === "win32" ? "ffmpeg.exe" : "ffmpeg";
|
||||
}
|
||||
|
||||
function locateFfprobeBinary() {
|
||||
const fromEnv = process.env.AICLIENT_FFPROBE_PATH;
|
||||
if (fromEnv && fs.existsSync(fromEnv)) return fromEnv;
|
||||
const ffmpeg = locateFfmpegBinary();
|
||||
if (ffmpeg && ffmpeg !== "ffmpeg" && ffmpeg !== "ffmpeg.exe") {
|
||||
const dir = path.dirname(ffmpeg);
|
||||
const sibling = path.join(dir, process.platform === "win32" ? "ffprobe.exe" : "ffprobe");
|
||||
if (fs.existsSync(sibling)) return sibling;
|
||||
}
|
||||
return process.platform === "win32" ? "ffprobe.exe" : "ffprobe";
|
||||
}
|
||||
|
||||
function parseDurationFromFfmpegStderr(text) {
|
||||
const m = String(text || "").match(/Duration:\s*(\d+):(\d+):(\d+(?:\.\d+)?)/);
|
||||
if (!m) return 0;
|
||||
const h = parseFloat(m[1]);
|
||||
const min = parseFloat(m[2]);
|
||||
const s = parseFloat(m[3]);
|
||||
if (!Number.isFinite(h + min + s)) return 0;
|
||||
return h * 3600 + min * 60 + s;
|
||||
}
|
||||
|
||||
function probeAudioDuration(audioPath) {
|
||||
const ffprobe = process.env.AICLIENT_FFPROBE_PATH || "ffprobe";
|
||||
const ffprobe = locateFfprobeBinary();
|
||||
const r = spawnSync(
|
||||
ffprobe,
|
||||
[
|
||||
@@ -184,9 +212,19 @@ function probeAudioDuration(audioPath) {
|
||||
],
|
||||
{ encoding: "utf8", windowsHide: true },
|
||||
);
|
||||
if (r.status !== 0) return 0;
|
||||
const n = parseFloat(String(r.stdout || "").trim());
|
||||
return Number.isFinite(n) && n > 0 ? n : 0;
|
||||
if (r.status === 0) {
|
||||
const n = parseFloat(String(r.stdout || "").trim());
|
||||
if (Number.isFinite(n) && n > 0) return n;
|
||||
}
|
||||
|
||||
const ffmpeg = locateFfmpegBinary();
|
||||
const r2 = spawnSync(ffmpeg, ["-i", audioPath], {
|
||||
encoding: "utf8",
|
||||
windowsHide: true,
|
||||
});
|
||||
const fromStderr = parseDurationFromFfmpegStderr(r2.stderr || "");
|
||||
if (fromStderr > 0) return fromStderr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
async function createTask(config, videoFileName, audioFileName, audioDuration) {
|
||||
@@ -379,6 +417,14 @@ globalThis.__nodejsMain = async function (params) {
|
||||
if (!Number.isFinite(audioDuration) || audioDuration <= 0) {
|
||||
audioDuration = probeAudioDuration(audioPath);
|
||||
}
|
||||
if (audioDuration <= 0) {
|
||||
return {
|
||||
success: false,
|
||||
error:
|
||||
"无法获取音频时长。云端口播需将时长传给 RunningHub(请安装 ffmpeg/ffprobe,或升级客户端后重试)",
|
||||
};
|
||||
}
|
||||
const durationSec = Math.ceil(audioDuration);
|
||||
|
||||
try {
|
||||
const videoFileName = await uploadFile(config.baseUrl, config.apiKey, videoPath);
|
||||
@@ -387,7 +433,7 @@ globalThis.__nodejsMain = async function (params) {
|
||||
config,
|
||||
videoFileName,
|
||||
audioFileName,
|
||||
audioDuration,
|
||||
durationSec,
|
||||
);
|
||||
const done = await waitForTask(config, taskId);
|
||||
const remoteUrl = pickResultUrl(done.results);
|
||||
@@ -398,7 +444,12 @@ globalThis.__nodejsMain = async function (params) {
|
||||
};
|
||||
}
|
||||
const localPath = await ensureLocalVideo(remoteUrl);
|
||||
return { success: true, videoPath: localPath, taskId };
|
||||
return {
|
||||
success: true,
|
||||
videoPath: localPath,
|
||||
taskId,
|
||||
audioDuration: durationSec,
|
||||
};
|
||||
} catch (e) {
|
||||
return { success: false, error: e.message || String(e) };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user