Fix muxing

This commit is contained in:
2026-05-15 04:10:06 +02:00
parent 05faf1ce3b
commit 6deb883472
26 changed files with 662 additions and 169 deletions

View File

@@ -13,6 +13,20 @@ const muxer_1 = require("./muxer");
const config_1 = require("../../config/config");
const mediaUtils_1 = require("../../utils/mediaUtils");
const events_1 = require("events");
function jobTempDir(baseTempDir, jobId) {
return path_1.default.join(baseTempDir, jobId);
}
function safeCleanupJobTmp(dir) {
try {
if (!fs_1.default.existsSync(dir))
return;
(0, mediaUtils_1.cleanupTempFiles)(dir);
fs_1.default.rmSync(dir, { recursive: true, force: true });
}
catch (err) {
console.warn(`Failed to clean up tmp dir ${dir}:`, err.message);
}
}
class JobManager {
constructor() {
this.queue = [];
@@ -33,12 +47,22 @@ class JobManager {
}
createJob(videoPath, configOverride = {}, outputOptions = {}) {
const baseConfig = (0, config_1.getDefaultConfig)();
const mergedConfig = { ...baseConfig, ...configOverride };
// Drop empty/undefined/null values so blank form fields don't clobber the
// baked-in defaults (a blank prompt textarea must NOT overwrite the real
// prompt with "").
const cleanedOverride = {};
for (const [k, v] of Object.entries(configOverride)) {
if (v === '' || v === null || v === undefined)
continue;
cleanedOverride[k] = v;
}
const mergedConfig = { ...baseConfig, ...cleanedOverride };
const filename = path_1.default.basename(videoPath);
const opts = {
audio: outputOptions.audio !== false,
subtitles: outputOptions.subtitles !== false,
muxed: outputOptions.muxed || false
muxed: outputOptions.muxed || false,
muxMode: outputOptions.muxMode === 'mixed' ? 'mixed' : 'separate'
};
return (0, jobStore_1.createJob)(videoPath, filename, mergedConfig, opts);
}
@@ -92,6 +116,18 @@ class JobManager {
throw new Error('Job not found');
if (job.status === 'processing')
throw new Error('Cannot delete a running job');
try {
const config = JSON.parse(job.config);
// job.config may contain either the base tempDir (older jobs) or the
// per-job tempDir (newer jobs). Trim a trailing job-id segment if present;
// otherwise compute the per-job dir from the stored base.
const stored = config.tempDir || './desc/tmp/';
const candidate = path_1.default.basename(stored) === jobId ? stored : jobTempDir(stored, jobId);
safeCleanupJobTmp(candidate);
}
catch {
// ignore: cleanup is best-effort and must not block deletion
}
(0, jobStore_1.deleteJob)(jobId);
}
listJobs() {
@@ -156,6 +192,14 @@ class JobManager {
this.emitProgress(job.id);
const config = JSON.parse(job.config);
const outputOptions = JSON.parse(job.output_options);
// Isolate this job's intermediates so concurrent jobs (or future resumes)
// don't collide on filenames like frame_00001.jpg / segment_3_std.wav.
// The pipeline already reads config.tempDir, so just override it here.
const baseTempDir = config.tempDir || './desc/tmp/';
if (path_1.default.basename(baseTempDir) !== job.id) {
config.tempDir = jobTempDir(baseTempDir, job.id);
}
fs_1.default.mkdirSync(config.tempDir, { recursive: true });
const existingSegments = JSON.parse(job.segments || '[]');
const lastContext = JSON.parse(job.last_context || '{}');
const startIndex = existingSegments.length > 0 ? job.current_index : 0;
@@ -209,8 +253,14 @@ class JobManager {
outputSubtitlesVtt = vttPath;
}
if (outputOptions.muxed && fs_1.default.existsSync(outputAudio)) {
const muxedPath = path_1.default.join(outputDir, `${baseName}_described.mkv`);
(0, muxer_1.muxAudioDescription)(job.video_path, outputAudio, muxedPath);
const isMixed = outputOptions.muxMode === 'mixed';
const muxedPath = path_1.default.join(outputDir, `${baseName}${isMixed ? '_described_mixed' : '_described'}.mkv`);
if (isMixed) {
(0, muxer_1.muxMixedAudioDescription)(job.video_path, outputAudio, muxedPath);
}
else {
(0, muxer_1.muxAudioDescription)(job.video_path, outputAudio, muxedPath);
}
outputMuxed = muxedPath;
}
(0, jobStore_1.saveJobOutputs)(job.id, {
@@ -222,9 +272,11 @@ class JobManager {
(0, jobStore_1.saveCheckpoint)(job.id, JSON.stringify(segments), totalUnits, totalUnits, 0, '{}', 100);
(0, jobStore_1.updateJobStatus)(job.id, 'completed');
this.emitProgress(job.id);
safeCleanupJobTmp(config.tempDir);
}
catch (err) {
if (err.message === 'JOB_PAUSED') {
// Keep config.tempDir intact — restart will resume into the same dir.
(0, jobStore_1.updateJobStatus)(job.id, 'paused');
this.emitProgress(job.id);
return;
@@ -232,6 +284,7 @@ class JobManager {
const errorMsg = err.message || 'Unknown error';
(0, jobStore_1.updateJobStatus)(job.id, 'failed', errorMsg);
this.emitProgress(job.id);
safeCleanupJobTmp(config.tempDir);
}
}
}