223 lines
7.4 KiB
JavaScript
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 });
|
|
};
|