Files
story-studio/remotion/node_modules/@remotion/cli/dist/render-queue/queue.js
2026-02-21 10:33:18 +01:00

223 lines
7.4 KiB
JavaScript

"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.cancelJob = exports.removeJob = exports.addJob = exports.getRenderQueue = void 0;
const studio_server_1 = require("@remotion/studio-server");
const node_path_1 = __importDefault(require("node:path"));
const chalk_1 = require("../chalk");
const log_1 = require("../log");
const print_error_1 = require("../print-error");
const progress_types_1 = require("../progress-types");
const process_still_1 = require("./process-still");
const process_video_1 = require("./process-video");
let jobQueue = [];
const updateJob = (id, updater) => {
jobQueue = jobQueue.map((j) => {
if (id === j.id) {
return updater(j);
}
return j;
});
notifyClientsOfJobUpdate();
};
const getRenderQueue = () => {
return jobQueue.map((j) => {
const { cleanup, ...rest } = j;
return rest;
});
};
exports.getRenderQueue = getRenderQueue;
const notifyClientsOfJobUpdate = () => {
studio_server_1.StudioServerInternals.waitForLiveEventsListener().then((listener) => {
listener.sendEventToClient({
type: 'render-queue-updated',
queue: (0, exports.getRenderQueue)(),
});
});
};
const processJob = async ({ job, remotionRoot, entryPoint, onProgress, addCleanupCallback, logLevel, }) => {
if (job.type === 'still') {
await (0, process_still_1.processStill)({
job,
remotionRoot,
entryPoint,
onProgress,
addCleanupCallback,
});
return;
}
if (job.type === 'video' || job.type === 'sequence') {
await (0, process_video_1.processVideoJob)({
job,
remotionRoot,
entryPoint,
onProgress,
addCleanupCallback,
logLevel,
});
return;
}
throw new Error(`Unknown job ${JSON.stringify(job)}`);
};
const addJob = ({ job, entryPoint, remotionRoot, logLevel, }) => {
jobQueue.push(job);
processJobIfPossible({ entryPoint, remotionRoot, logLevel });
notifyClientsOfJobUpdate();
};
exports.addJob = addJob;
const removeJob = (jobId) => {
jobQueue = jobQueue.filter((job) => {
if (job.id === jobId) {
job.cleanup.forEach((c) => {
c();
});
return false;
}
return true;
});
notifyClientsOfJobUpdate();
};
exports.removeJob = removeJob;
const cancelJob = (jobId) => {
for (const job of jobQueue) {
if (job.id === jobId) {
if (job.status !== 'running') {
throw new Error('Job is not running');
}
job.cancelToken.cancel();
break;
}
}
};
exports.cancelJob = cancelJob;
const processJobIfPossible = async ({ remotionRoot, entryPoint, logLevel, }) => {
const runningJob = jobQueue.find((q) => {
return q.status === 'running';
});
if (runningJob) {
return;
}
const nextJob = jobQueue.find((q) => {
return q.status === 'idle';
});
if (!nextJob) {
return;
}
const jobCleanups = [];
try {
updateJob(nextJob.id, (job) => {
return {
...job,
status: 'running',
progress: {
value: 0,
message: 'Starting job...',
...(0, progress_types_1.initialAggregateRenderProgress)(),
},
};
});
const startTime = Date.now();
log_1.Log.info({ indent: false, logLevel }, chalk_1.chalk.gray('╭─ Starting render '));
let lastProgress = null;
await processJob({
job: nextJob,
entryPoint,
remotionRoot,
onProgress: (progress) => {
updateJob(nextJob.id, (job) => {
lastProgress = progress;
// Ignore late callbacks of progress updates after cancelling
if (job.status === 'failed' || job.status === 'done') {
return job;
}
if (job.type === 'still') {
return {
...job,
status: 'running',
progress,
};
}
if (job.type === 'video') {
return {
...job,
status: 'running',
progress,
};
}
if (job.type === 'sequence') {
return {
...job,
status: 'running',
progress,
};
}
throw new Error('Unknown job type');
});
},
addCleanupCallback: (label, cleanup) => {
jobCleanups.push({ label, job: cleanup });
},
logLevel,
});
log_1.Log.info({ indent: false, logLevel }, chalk_1.chalk.gray('╰─ Done in ' + (Date.now() - startTime) + 'ms.'));
const { unwatch } = studio_server_1.StudioServerInternals.installFileWatcher({
file: node_path_1.default.resolve(remotionRoot, nextJob.outName),
onChange: (type) => {
if (type === 'created') {
updateJob(nextJob.id, (job) => ({
...job,
deletedOutputLocation: false,
}));
}
if (type === 'deleted') {
updateJob(nextJob.id, (job) => ({
...job,
deletedOutputLocation: true,
}));
}
},
});
updateJob(nextJob.id, (job) => {
if (!lastProgress) {
throw new Error('expected progress');
}
return {
...job,
status: 'done',
cleanup: [...job.cleanup, unwatch],
progress: { message: 'Done', value: 1, ...lastProgress },
};
});
}
catch (err) {
log_1.Log.error({ indent: false, logLevel }, chalk_1.chalk.gray('\n╰─ '), chalk_1.chalk.red('Failed to render'));
updateJob(nextJob.id, (job) => {
return {
...job,
status: 'failed',
error: {
message: err.message,
stack: err.stack,
},
};
});
await (0, print_error_1.printError)(err, logLevel);
studio_server_1.StudioServerInternals.waitForLiveEventsListener().then((listener) => {
listener.sendEventToClient({
type: 'render-job-failed',
compositionId: nextJob.compositionId,
error: err,
});
});
}
finally {
await Promise.all(jobCleanups.map(({ job, label }) => {
log_1.Log.verbose({ indent: false, logLevel }, 'Cleanup: ' + label);
return job();
}));
}
processJobIfPossible({ remotionRoot, entryPoint, logLevel });
};