init commit

This commit is contained in:
Carlos
2026-02-21 10:33:18 +01:00
parent c863a943ed
commit 9d955bf338
9512 changed files with 2015317 additions and 1305 deletions

View File

@@ -0,0 +1,5 @@
import React from 'react';
export declare const RENDER_STATUS_INDICATOR_SIZE = 16;
export declare const CircularProgress: React.FC<{
readonly progress: number;
}>;

View File

@@ -0,0 +1,18 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CircularProgress = exports.RENDER_STATUS_INDICATOR_SIZE = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const colors_1 = require("../../helpers/colors");
exports.RENDER_STATUS_INDICATOR_SIZE = 16;
const STROKE_WIDTH = 3;
const container = {
height: exports.RENDER_STATUS_INDICATOR_SIZE,
width: exports.RENDER_STATUS_INDICATOR_SIZE,
transform: `rotate(-90deg)`,
};
const CircularProgress = ({ progress }) => {
const r = exports.RENDER_STATUS_INDICATOR_SIZE / 2 - STROKE_WIDTH;
const circumference = r * Math.PI * 2;
return ((0, jsx_runtime_1.jsx)("svg", { style: container, viewBox: `0 0 ${exports.RENDER_STATUS_INDICATOR_SIZE} ${exports.RENDER_STATUS_INDICATOR_SIZE}`, children: (0, jsx_runtime_1.jsx)("circle", { r: exports.RENDER_STATUS_INDICATOR_SIZE / 2 - STROKE_WIDTH, stroke: colors_1.LIGHT_TEXT, fill: "none", strokeWidth: STROKE_WIDTH, cx: exports.RENDER_STATUS_INDICATOR_SIZE / 2, cy: exports.RENDER_STATUS_INDICATOR_SIZE / 2, strokeDasharray: `${circumference} ${circumference}`, strokeMiterlimit: 0, strokeDashoffset: (1 - progress) * circumference }) }));
};
exports.CircularProgress = CircularProgress;

View File

@@ -0,0 +1 @@
export declare const ClientRenderQueueProcessor: React.FC;

View File

@@ -0,0 +1,148 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ClientRenderQueueProcessor = void 0;
const web_renderer_1 = require("@remotion/web-renderer");
const react_1 = require("react");
const context_1 = require("./context");
const downloadBlob = (blob, filename) => {
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
const cleanFilename = filename.includes('/')
? filename.substring(filename.lastIndexOf('/') + 1)
: filename;
a.download = cleanFilename;
a.click();
URL.revokeObjectURL(url);
};
const ClientRenderQueueProcessor = () => {
const { getAbortController, getCompositionForJob, updateClientJobProgress, markClientJobDone, markClientJobFailed, markClientJobCancelled, setProcessJobCallback, } = (0, react_1.useContext)(context_1.RenderQueueContext);
const processStillJob = (0, react_1.useCallback)(async (job, signal) => {
var _a, _b;
const compositionRef = getCompositionForJob(job.id);
if (!compositionRef) {
throw new Error(`Composition not found for job ${job.id}`);
}
const { blob } = await (0, web_renderer_1.renderStillOnWeb)({
composition: {
component: compositionRef.component,
width: compositionRef.width,
height: compositionRef.height,
fps: compositionRef.fps,
durationInFrames: compositionRef.durationInFrames,
defaultProps: compositionRef.defaultProps,
calculateMetadata: (_a = compositionRef.calculateMetadata) !== null && _a !== void 0 ? _a : undefined,
id: job.compositionId,
},
frame: job.frame,
imageFormat: job.imageFormat,
inputProps: job.inputProps,
delayRenderTimeoutInMilliseconds: job.delayRenderTimeout,
mediaCacheSizeInBytes: job.mediaCacheSizeInBytes,
logLevel: job.logLevel,
licenseKey: (_b = job.licenseKey) !== null && _b !== void 0 ? _b : undefined,
scale: job.scale,
signal,
});
return {
getBlob: () => Promise.resolve(blob),
width: compositionRef.width,
height: compositionRef.height,
};
}, [getCompositionForJob]);
const processVideoJob = (0, react_1.useCallback)(async (job, signal, onProgress) => {
var _a, _b;
const compositionRef = getCompositionForJob(job.id);
if (!compositionRef) {
throw new Error(`Composition not found for job ${job.id}`);
}
const totalFrames = job.endFrame - job.startFrame + 1;
const { getBlob } = await (0, web_renderer_1.renderMediaOnWeb)({
composition: {
component: compositionRef.component,
width: compositionRef.width,
height: compositionRef.height,
fps: compositionRef.fps,
durationInFrames: compositionRef.durationInFrames,
defaultProps: compositionRef.defaultProps,
calculateMetadata: (_a = compositionRef.calculateMetadata) !== null && _a !== void 0 ? _a : undefined,
id: job.compositionId,
},
inputProps: job.inputProps,
delayRenderTimeoutInMilliseconds: job.delayRenderTimeout,
mediaCacheSizeInBytes: job.mediaCacheSizeInBytes,
logLevel: job.logLevel,
videoCodec: job.videoCodec,
audioCodec: job.audioCodec,
audioBitrate: job.audioBitrate,
container: job.container,
videoBitrate: job.videoBitrate,
hardwareAcceleration: job.hardwareAcceleration,
keyframeIntervalInSeconds: job.keyframeIntervalInSeconds,
frameRange: [job.startFrame, job.endFrame],
transparent: job.transparent,
muted: job.muted,
scale: job.scale,
signal,
onProgress: (progress) => {
onProgress(job.id, {
renderedFrames: progress.renderedFrames,
encodedFrames: progress.encodedFrames,
totalFrames,
});
},
outputTarget: 'web-fs',
licenseKey: (_b = job.licenseKey) !== null && _b !== void 0 ? _b : undefined,
});
return {
getBlob,
width: compositionRef.width,
height: compositionRef.height,
};
}, [getCompositionForJob]);
const processJob = (0, react_1.useCallback)(async (job) => {
const abortController = getAbortController(job.id);
try {
let result;
if (job.type === 'client-still') {
result = await processStillJob(job, abortController.signal);
}
else if (job.type === 'client-video') {
result = await processVideoJob(job, abortController.signal, updateClientJobProgress);
}
else {
throw new Error(`Unknown job type`);
}
const blob = await result.getBlob();
downloadBlob(blob, job.outName);
const metadata = {
width: result.width,
height: result.height,
sizeInBytes: blob.size,
};
markClientJobDone(job.id, result.getBlob, metadata);
}
catch (err) {
if (abortController.signal.aborted) {
markClientJobCancelled(job.id);
}
else {
markClientJobFailed(job.id, err);
}
}
}, [
getAbortController,
processStillJob,
processVideoJob,
updateClientJobProgress,
markClientJobDone,
markClientJobFailed,
markClientJobCancelled,
]);
(0, react_1.useEffect)(() => {
setProcessJobCallback(processJob);
return () => setProcessJobCallback(null);
}, [processJob, setProcessJobCallback]);
return null;
};
exports.ClientRenderQueueProcessor = ClientRenderQueueProcessor;

View File

@@ -0,0 +1,2 @@
import React from 'react';
export declare const RenderQueueCancelledMessage: React.FC;

View File

@@ -0,0 +1,15 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RenderQueueCancelledMessage = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const colors_1 = require("../../helpers/colors");
const item_style_1 = require("./item-style");
const cancelledStyle = {
...item_style_1.renderQueueItemSubtitleStyle,
color: colors_1.LIGHT_TEXT,
cursor: 'default',
};
const RenderQueueCancelledMessage = () => {
return (0, jsx_runtime_1.jsx)("span", { style: cancelledStyle, children: "Cancelled" });
};
exports.RenderQueueCancelledMessage = RenderQueueCancelledMessage;

View File

@@ -0,0 +1,5 @@
import type { RenderJob } from '@remotion/studio-shared';
export declare const supportsCopyingToClipboard: (job: RenderJob) => boolean;
export declare const RenderQueueCopyToClipboard: React.FC<{
job: RenderJob;
}>;

View File

@@ -0,0 +1,57 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RenderQueueCopyToClipboard = exports.supportsCopyingToClipboard = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
const get_asset_metadata_1 = require("../../helpers/get-asset-metadata");
const clipboard_1 = require("../../icons/clipboard");
const InlineAction_1 = require("../InlineAction");
const NotificationCenter_1 = require("../Notifications/NotificationCenter");
const revealIconStyle = {
height: 12,
color: 'currentColor',
};
const supportsCopyingToClipboard = (job) => {
if (job.status !== 'done') {
return false;
}
if (job.type !== 'still') {
return false;
}
if (job.imageFormat === 'png') {
return true;
}
if (job.imageFormat === 'jpeg') {
return true;
}
return false;
};
exports.supportsCopyingToClipboard = supportsCopyingToClipboard;
const RenderQueueCopyToClipboard = ({ job }) => {
const renderCopyAction = (0, react_1.useCallback)((color) => {
return (0, jsx_runtime_1.jsx)(clipboard_1.ClipboardIcon, { style: revealIconStyle, color: color });
}, []);
const onClick = (0, react_1.useCallback)(async (e) => {
e.stopPropagation();
try {
const src = `${get_asset_metadata_1.remotion_outputsBase}/${job.outName}`;
const content = await fetch(src);
const contentType = content.headers.get('content-type');
if (!contentType) {
throw new Error('Expected content-type header');
}
const blob = await content.blob();
await navigator.clipboard.write([
new ClipboardItem({
[contentType]: blob,
}),
]);
(0, NotificationCenter_1.showNotification)('Copied to clipboard!', 1000);
}
catch (err) {
(0, NotificationCenter_1.showNotification)(`Could not copy to clipboard: ${err.message}`, 2000);
}
}, [job.outName]);
return ((0, jsx_runtime_1.jsx)(InlineAction_1.InlineAction, { title: "Copy to clipboard", renderAction: renderCopyAction, onClick: onClick }));
};
exports.RenderQueueCopyToClipboard = RenderQueueCopyToClipboard;

View File

@@ -0,0 +1,5 @@
import React from 'react';
import type { AnyRenderJob } from './context';
export declare const RenderQueueError: React.FC<{
readonly job: AnyRenderJob;
}>;

View File

@@ -0,0 +1,26 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RenderQueueError = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
const modals_1 = require("../../state/modals");
const z_index_1 = require("../../state/z-index");
const item_style_1 = require("./item-style");
const outputLocation = {
...item_style_1.renderQueueItemSubtitleStyle,
};
const RenderQueueError = ({ job }) => {
const { setSelectedModal } = (0, react_1.useContext)(modals_1.ModalsContext);
const { tabIndex } = (0, z_index_1.useZIndex)();
const onClick = (0, react_1.useCallback)(() => {
setSelectedModal({
type: 'render-progress',
jobId: job.id,
});
}, [job.id, setSelectedModal]);
if (job.status !== 'failed') {
throw new Error('should not have rendered this component');
}
return ((0, jsx_runtime_1.jsx)("button", { onClick: onClick, type: "button", style: outputLocation, tabIndex: tabIndex, title: job.error.message, children: job.error.message }));
};
exports.RenderQueueError = RenderQueueError;

View File

@@ -0,0 +1,6 @@
import React from 'react';
import type { AnyRenderJob } from './context';
export declare const RenderQueueItem: React.FC<{
readonly job: AnyRenderJob;
readonly selected: boolean;
}>;

View File

@@ -0,0 +1,111 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RenderQueueItem = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
const remotion_1 = require("remotion");
const colors_1 = require("../../helpers/colors");
const url_state_1 = require("../../helpers/url-state");
const layout_1 = require("../layout");
const context_1 = require("./context");
const RenderQueueCancelledMessage_1 = require("./RenderQueueCancelledMessage");
const RenderQueueCopyToClipboard_1 = require("./RenderQueueCopyToClipboard");
const RenderQueueError_1 = require("./RenderQueueError");
const RenderQueueItemCancelButton_1 = require("./RenderQueueItemCancelButton");
const RenderQueueItemStatus_1 = require("./RenderQueueItemStatus");
const RenderQueueOpenInFolder_1 = require("./RenderQueueOpenInFolder");
const RenderQueueOutputName_1 = require("./RenderQueueOutputName");
const RenderQueueProgressMessage_1 = require("./RenderQueueProgressMessage");
const RenderQueueRemoveItem_1 = require("./RenderQueueRemoveItem");
const RenderQueueRepeat_1 = require("./RenderQueueRepeat");
const container = {
padding: 12,
display: 'flex',
flexDirection: 'row',
paddingBottom: 10,
paddingRight: 4,
};
const title = {
fontSize: 13,
lineHeight: 1,
};
const right = {
flex: 1,
display: 'flex',
flexDirection: 'column',
overflow: 'hidden',
};
const subtitle = {
maxWidth: '100%',
flex: 1,
display: 'flex',
overflow: 'hidden',
};
const SELECTED_CLASSNAME = '__remotion_selected_classname';
const RenderQueueItem = ({ job, selected }) => {
const [hovered, setHovered] = (0, react_1.useState)(false);
const { setCanvasContent } = (0, react_1.useContext)(remotion_1.Internals.CompositionSetters);
const isClientJob = (0, context_1.isClientRenderJob)(job);
const onPointerEnter = (0, react_1.useCallback)(() => {
setHovered(true);
}, []);
const onPointerLeave = (0, react_1.useCallback)(() => {
setHovered(false);
}, []);
const isHoverable = job.status === 'done' && (isClientJob || job.type !== 'sequence');
const containerStyle = (0, react_1.useMemo)(() => {
return {
...container,
backgroundColor: (0, colors_1.getBackgroundFromHoverState)({
hovered: isHoverable && hovered,
selected,
}),
userSelect: 'none',
WebkitUserSelect: 'none',
};
}, [hovered, isHoverable, selected]);
const scrollCurrentIntoView = (0, react_1.useCallback)(() => {
var _a;
(_a = document
.querySelector(`.${SELECTED_CLASSNAME}`)) === null || _a === void 0 ? void 0 : _a.scrollIntoView({ behavior: 'smooth' });
}, []);
const onClick = (0, react_1.useCallback)(() => {
if (job.status !== 'done') {
return;
}
if (isClientJob) {
const clientJob = job;
setCanvasContent({
type: 'output-blob',
displayName: job.outName,
getBlob: clientJob.getBlob,
width: clientJob.metadata.width,
height: clientJob.metadata.height,
sizeInBytes: clientJob.metadata.sizeInBytes,
});
return;
}
// Cannot show folders
if (job.type === 'sequence') {
return;
}
setCanvasContent((c) => {
const isAlreadySelected = c && c.type === 'output' && c.path === `/${job.outName}`;
if (isAlreadySelected && !selected) {
scrollCurrentIntoView();
return c;
}
return { type: 'output', path: `/${job.outName}` };
});
(0, url_state_1.pushUrl)(`/outputs/${job.outName}`);
}, [job, isClientJob, scrollCurrentIntoView, selected, setCanvasContent]);
(0, react_1.useEffect)(() => {
if (selected) {
scrollCurrentIntoView();
}
}, [scrollCurrentIntoView, selected]);
return ((0, jsx_runtime_1.jsxs)(layout_1.Row, { onPointerEnter: onPointerEnter, onPointerLeave: onPointerLeave, style: containerStyle, align: "center", onClick: onClick, className: selected ? SELECTED_CLASSNAME : undefined, children: [(0, jsx_runtime_1.jsx)(RenderQueueItemStatus_1.RenderQueueItemStatus, { job: job }), (0, jsx_runtime_1.jsx)(layout_1.Spacing, { x: 1 }), (0, jsx_runtime_1.jsxs)("div", { style: right, children: [(0, jsx_runtime_1.jsx)("div", { style: title, children: job.compositionId }), (0, jsx_runtime_1.jsx)("div", { style: subtitle, children: job.status === 'done' ? ((0, jsx_runtime_1.jsx)(RenderQueueOutputName_1.RenderQueueOutputName, { job: job })) : job.status === 'failed' ? ((0, jsx_runtime_1.jsx)(RenderQueueError_1.RenderQueueError, { job: job })) : job.status === 'running' ? ((0, jsx_runtime_1.jsx)(RenderQueueProgressMessage_1.RenderQueueProgressMessage, { job: job })) : job.status === 'cancelled' ? ((0, jsx_runtime_1.jsx)(RenderQueueCancelledMessage_1.RenderQueueCancelledMessage, {})) : null })] }), (0, jsx_runtime_1.jsx)(layout_1.Spacing, { x: 1 }), !isClientJob && (0, RenderQueueCopyToClipboard_1.supportsCopyingToClipboard)(job) ? ((0, jsx_runtime_1.jsx)(RenderQueueCopyToClipboard_1.RenderQueueCopyToClipboard, { job: job })) : null, job.status === 'done' ||
job.status === 'failed' ||
job.status === 'cancelled' ? ((0, jsx_runtime_1.jsx)(RenderQueueRepeat_1.RenderQueueRepeatItem, { job: job })) : null, job.status === 'running' ? ((0, jsx_runtime_1.jsx)(RenderQueueItemCancelButton_1.RenderQueueCancelButton, { job: job })) : ((0, jsx_runtime_1.jsx)(RenderQueueRemoveItem_1.RenderQueueRemoveItem, { job: job })), job.status === 'done' && !isClientJob ? ((0, jsx_runtime_1.jsx)(RenderQueueOpenInFolder_1.RenderQueueOpenInFinderItem, { job: job })) : null] }));
};
exports.RenderQueueItem = RenderQueueItem;

View File

@@ -0,0 +1,5 @@
import React from 'react';
import type { AnyRenderJob } from './context';
export declare const RenderQueueCancelButton: React.FC<{
readonly job: AnyRenderJob;
}>;

View File

@@ -0,0 +1,34 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RenderQueueCancelButton = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
const InlineAction_1 = require("../InlineAction");
const NotificationCenter_1 = require("../Notifications/NotificationCenter");
const actions_1 = require("./actions");
const context_1 = require("./context");
const RenderQueueCancelButton = ({ job }) => {
const isClientJob = (0, context_1.isClientRenderJob)(job);
const { cancelClientJob } = (0, react_1.useContext)(context_1.RenderQueueContext);
const onClick = (0, react_1.useCallback)((e) => {
e.stopPropagation();
if (isClientJob) {
cancelClientJob(job.id);
return;
}
(0, actions_1.cancelRenderJob)(job).catch((err) => {
(0, NotificationCenter_1.showNotification)(`Could not cancel job: ${err.message}`, 2000);
});
}, [job, isClientJob, cancelClientJob]);
const icon = (0, react_1.useMemo)(() => {
return {
height: 14,
color: 'currentColor',
};
}, []);
const renderAction = (0, react_1.useCallback)((color) => {
return ((0, jsx_runtime_1.jsx)("svg", { style: icon, xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 512 512", children: (0, jsx_runtime_1.jsx)("path", { fill: color, d: "M367.2 412.5L99.5 144.8C77.1 176.1 64 214.5 64 256c0 106 86 192 192 192c41.5 0 79.9-13.1 111.2-35.5zm45.3-45.3C434.9 335.9 448 297.5 448 256c0-106-86-192-192-192c-41.5 0-79.9 13.1-111.2 35.5L412.5 367.2zM512 256c0 141.4-114.6 256-256 256S0 397.4 0 256S114.6 0 256 0S512 114.6 512 256z" }) }));
}, [icon]);
return (0, jsx_runtime_1.jsx)(InlineAction_1.InlineAction, { renderAction: renderAction, onClick: onClick });
};
exports.RenderQueueCancelButton = RenderQueueCancelButton;

View File

@@ -0,0 +1,5 @@
import React from 'react';
import type { AnyRenderJob } from './context';
export declare const RenderQueueItemStatus: React.FC<{
readonly job: AnyRenderJob;
}>;

View File

@@ -0,0 +1,96 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.RenderQueueItemStatus = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = __importStar(require("react"));
const colors_1 = require("../../helpers/colors");
const modals_1 = require("../../state/modals");
const CircularProgress_1 = require("./CircularProgress");
const context_1 = require("./context");
const iconStyle = {
height: CircularProgress_1.RENDER_STATUS_INDICATOR_SIZE,
width: CircularProgress_1.RENDER_STATUS_INDICATOR_SIZE,
};
const invisibleStyle = {
appearance: 'none',
border: 'none',
padding: 0,
cursor: 'pointer',
display: 'flex',
};
const RenderQueueItemStatus = ({ job }) => {
const { setSelectedModal } = (0, react_1.useContext)(modals_1.ModalsContext);
const [hovered, setHovered] = react_1.default.useState(false);
const isClientJob = (0, context_1.isClientRenderJob)(job);
const onPointerEnter = (0, react_1.useCallback)(() => {
setHovered(true);
}, []);
const onPointerLeave = (0, react_1.useCallback)(() => {
setHovered(false);
}, []);
const onClick = (0, react_1.useCallback)((e) => {
e.stopPropagation();
setSelectedModal({
type: 'render-progress',
jobId: job.id,
});
}, [job.id, setSelectedModal]);
if (job.status === 'failed') {
return ((0, jsx_runtime_1.jsx)("button", { type: "button", style: invisibleStyle, onClick: onClick, children: (0, jsx_runtime_1.jsx)("svg", { style: iconStyle, viewBox: "0 0 512 512", children: (0, jsx_runtime_1.jsx)("path", { fill: colors_1.FAIL_COLOR, d: "M0 160V352L160 512H352L512 352V160L352 0H160L0 160zm353.9 32l-17 17-47 47 47 47 17 17L320 353.9l-17-17-47-47-47 47-17 17L158.1 320l17-17 47-47-47-47-17-17L192 158.1l17 17 47 47 47-47 17-17L353.9 192z" }) }) }));
}
if (job.status === 'idle') {
return ((0, jsx_runtime_1.jsx)("svg", { style: iconStyle, viewBox: "0 0 512 512", children: (0, jsx_runtime_1.jsx)("path", { fill: colors_1.LIGHT_TEXT, d: "M256 512C114.6 512 0 397.4 0 256S114.6 0 256 0S512 114.6 512 256s-114.6 256-256 256zM232 120V256c0 8 4 15.5 10.7 20l96 64c11 7.4 25.9 4.4 33.3-6.7s4.4-25.9-6.7-33.3L280 243.2V120c0-13.3-10.7-24-24-24s-24 10.7-24 24z" }) }));
}
if (job.status === 'done') {
return ((0, jsx_runtime_1.jsx)("button", { type: "button", style: invisibleStyle, onPointerEnter: onPointerEnter, onPointerLeave: onPointerLeave, onClick: onClick, children: (0, jsx_runtime_1.jsx)("svg", { style: iconStyle, viewBox: "0 0 512 512", children: (0, jsx_runtime_1.jsx)("path", { fill: hovered ? 'white' : colors_1.LIGHT_TEXT, d: "M256 512c141.4 0 256-114.6 256-256S397.4 0 256 0S0 114.6 0 256S114.6 512 256 512zM369 209L241 337l-17 17-17-17-64-64-17-17L160 222.1l17 17 47 47L335 175l17-17L385.9 192l-17 17z" }) }) }));
}
if (job.status === 'running') {
let progressValue;
if (isClientJob) {
const { renderedFrames, totalFrames } = job.progress;
progressValue = totalFrames > 0 ? renderedFrames / totalFrames : 0;
}
else {
progressValue = job.progress.value;
}
return ((0, jsx_runtime_1.jsx)("button", { type: "button", style: invisibleStyle, onClick: onClick, children: (0, jsx_runtime_1.jsx)(CircularProgress_1.CircularProgress, { progress: Math.max(0.07, progressValue) }) }));
}
if (job.status === 'cancelled') {
return ((0, jsx_runtime_1.jsx)("svg", { style: iconStyle, viewBox: "0 0 512 512", children: (0, jsx_runtime_1.jsx)("path", { fill: colors_1.FAIL_COLOR, d: "M0 160V352L160 512H352L512 352V160L352 0H160L0 160zm353.9 32l-17 17-47 47 47 47 17 17L320 353.9l-17-17-47-47-47 47-17 17L158.1 320l17-17 47-47-47-47-17-17L192 158.1l17 17 47 47 47-47 17-17L353.9 192z" }) }));
}
throw new Error('Unknown job status');
};
exports.RenderQueueItemStatus = RenderQueueItemStatus;

View File

@@ -0,0 +1,5 @@
import type { RenderJob } from '@remotion/studio-shared';
import React from 'react';
export declare const RenderQueueOpenInFinderItem: React.FC<{
readonly job: RenderJob;
}>;

View File

@@ -0,0 +1,28 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RenderQueueOpenInFinderItem = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
const folder_1 = require("../../icons/folder");
const InlineAction_1 = require("../InlineAction");
const NotificationCenter_1 = require("../Notifications/NotificationCenter");
const actions_1 = require("./actions");
const RenderQueueOpenInFinderItem = ({ job }) => {
const onClick = (0, react_1.useCallback)((e) => {
e.stopPropagation();
(0, actions_1.openInFileExplorer)({ directory: job.outName }).catch((err) => {
(0, NotificationCenter_1.showNotification)(`Could not open file: ${err.message}`, 2000);
});
}, [job.outName]);
const icon = (0, react_1.useMemo)(() => {
return {
height: 12,
color: 'currentColor',
};
}, []);
const renderAction = (0, react_1.useCallback)((color) => {
return (0, jsx_runtime_1.jsx)(folder_1.ExpandedFolderIconSolid, { style: icon, color: color });
}, [icon]);
return (0, jsx_runtime_1.jsx)(InlineAction_1.InlineAction, { renderAction: renderAction, onClick: onClick });
};
exports.RenderQueueOpenInFinderItem = RenderQueueOpenInFinderItem;

View File

@@ -0,0 +1,5 @@
import React from 'react';
import type { AnyRenderJob } from './context';
export declare const RenderQueueOutputName: React.FC<{
readonly job: AnyRenderJob;
}>;

View File

@@ -0,0 +1,32 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RenderQueueOutputName = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
const context_1 = require("./context");
const item_style_1 = require("./item-style");
const RenderQueueOutputName = ({ job }) => {
const isClientJob = (0, context_1.isClientRenderJob)(job);
const deletedOutputLocation = isClientJob
? false
: job.deletedOutputLocation;
const style = (0, react_1.useMemo)(() => {
return {
...item_style_1.renderQueueItemSubtitleStyle,
textDecoration: deletedOutputLocation ? 'line-through' : 'none',
color: item_style_1.renderQueueItemSubtitleStyle.color,
cursor: 'inherit',
};
}, [deletedOutputLocation]);
const getTitle = () => {
if (isClientJob) {
return `Downloaded as ${job.outName}`;
}
if (deletedOutputLocation) {
return 'File was deleted';
}
return job.outName;
};
return ((0, jsx_runtime_1.jsx)("span", { style: style, title: getTitle(), children: job.outName }));
};
exports.RenderQueueOutputName = RenderQueueOutputName;

View File

@@ -0,0 +1,5 @@
import React from 'react';
import type { AnyRenderJob } from './context';
export declare const RenderQueueProgressMessage: React.FC<{
readonly job: AnyRenderJob;
}>;

View File

@@ -0,0 +1,31 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RenderQueueProgressMessage = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
const modals_1 = require("../../state/modals");
const z_index_1 = require("../../state/z-index");
const context_1 = require("./context");
const item_style_1 = require("./item-style");
const outputLocation = {
...item_style_1.renderQueueItemSubtitleStyle,
};
const RenderQueueProgressMessage = ({ job }) => {
if (job.status !== 'running') {
throw new Error('should not have rendered this component');
}
const { setSelectedModal } = (0, react_1.useContext)(modals_1.ModalsContext);
const { tabIndex } = (0, z_index_1.useZIndex)();
const isClientJob = (0, context_1.isClientRenderJob)(job);
const onClick = (0, react_1.useCallback)(() => {
setSelectedModal({
type: 'render-progress',
jobId: job.id,
});
}, [job.id, setSelectedModal]);
const message = isClientJob
? `Rendering frame ${job.progress.renderedFrames}/${job.progress.totalFrames}`
: job.progress.message;
return ((0, jsx_runtime_1.jsx)("button", { onClick: onClick, type: "button", style: outputLocation, tabIndex: tabIndex, title: message, children: message }));
};
exports.RenderQueueProgressMessage = RenderQueueProgressMessage;

View File

@@ -0,0 +1,5 @@
import React from 'react';
import type { AnyRenderJob } from './context';
export declare const RenderQueueRemoveItem: React.FC<{
readonly job: AnyRenderJob;
}>;

View File

@@ -0,0 +1,48 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RenderQueueRemoveItem = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
const remotion_1 = require("remotion");
const InlineAction_1 = require("../InlineAction");
const NotificationCenter_1 = require("../Notifications/NotificationCenter");
const actions_1 = require("./actions");
const context_1 = require("./context");
const RenderQueueRemoveItem = ({ job }) => {
const isClientJob = (0, context_1.isClientRenderJob)(job);
const { removeClientJob } = (0, react_1.useContext)(context_1.RenderQueueContext);
const { canvasContent } = (0, react_1.useContext)(remotion_1.Internals.CompositionManager);
const { setCanvasContent } = (0, react_1.useContext)(remotion_1.Internals.CompositionSetters);
const onClick = (0, react_1.useCallback)((e) => {
e.stopPropagation();
if (isClientJob) {
if (canvasContent &&
canvasContent.type === 'output-blob' &&
job.status === 'done' &&
canvasContent.getBlob === job.getBlob) {
setCanvasContent(null);
}
removeClientJob(job.id);
(0, NotificationCenter_1.showNotification)('Removed job', 2000);
return;
}
(0, actions_1.removeRenderJob)(job)
.then(() => {
(0, NotificationCenter_1.showNotification)('Removed job', 2000);
})
.catch((err) => {
(0, NotificationCenter_1.showNotification)(`Could not remove item: ${err.message}`, 2000);
});
}, [job, isClientJob, removeClientJob, canvasContent, setCanvasContent]);
const icon = (0, react_1.useMemo)(() => {
return {
height: 16,
color: 'currentColor',
};
}, []);
const renderAction = (0, react_1.useCallback)((color) => {
return ((0, jsx_runtime_1.jsx)("svg", { style: icon, xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 320 512", children: (0, jsx_runtime_1.jsx)("path", { fill: color, d: "M310.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L160 210.7 54.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L114.7 256 9.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 301.3 265.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L205.3 256 310.6 150.6z" }) }));
}, [icon]);
return (0, jsx_runtime_1.jsx)(InlineAction_1.InlineAction, { renderAction: renderAction, onClick: onClick });
};
exports.RenderQueueRemoveItem = RenderQueueRemoveItem;

View File

@@ -0,0 +1,5 @@
import React from 'react';
import type { AnyRenderJob } from './context';
export declare const RenderQueueRepeatItem: React.FC<{
readonly job: AnyRenderJob;
}>;

View File

@@ -0,0 +1,48 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RenderQueueRepeatItem = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
const mobile_layout_1 = require("../../helpers/mobile-layout");
const retry_payload_1 = require("../../helpers/retry-payload");
const modals_1 = require("../../state/modals");
const sidebar_1 = require("../../state/sidebar");
const InlineAction_1 = require("../InlineAction");
const context_1 = require("./context");
const RenderQueueRepeatItem = ({ job }) => {
const { setSelectedModal } = (0, react_1.useContext)(modals_1.ModalsContext);
const isMobileLayout = (0, mobile_layout_1.useMobileLayout)();
const { setSidebarCollapsedState } = (0, react_1.useContext)(sidebar_1.SidebarContext);
const isClientJob = (0, context_1.isClientRenderJob)(job);
const onClick = (0, react_1.useCallback)((e) => {
e.stopPropagation();
if (isClientJob) {
const retryPayload = (0, retry_payload_1.makeClientRetryPayload)(job);
setSelectedModal(retryPayload);
}
else {
const retryPayload = (0, retry_payload_1.makeRetryPayload)(job);
setSelectedModal(retryPayload);
}
if (isMobileLayout) {
setSidebarCollapsedState({ left: 'collapsed', right: 'collapsed' });
}
}, [
isMobileLayout,
job,
isClientJob,
setSelectedModal,
setSidebarCollapsedState,
]);
const icon = (0, react_1.useMemo)(() => {
return {
height: 12,
color: 'currentColor',
};
}, []);
const renderAction = (0, react_1.useCallback)((color) => {
return ((0, jsx_runtime_1.jsx)("svg", { style: icon, viewBox: "0 0 512 512", children: (0, jsx_runtime_1.jsx)("path", { fill: color, d: "M386.3 160H336c-17.7 0-32 14.3-32 32s14.3 32 32 32H464c17.7 0 32-14.3 32-32V64c0-17.7-14.3-32-32-32s-32 14.3-32 32v51.2L414.4 97.6c-87.5-87.5-229.3-87.5-316.8 0s-87.5 229.3 0 316.8s229.3 87.5 316.8 0c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0c-62.5 62.5-163.8 62.5-226.3 0s-62.5-163.8 0-226.3s163.8-62.5 226.3 0L386.3 160z" }) }));
}, [icon]);
return (0, jsx_runtime_1.jsx)(InlineAction_1.InlineAction, { onClick: onClick, renderAction: renderAction });
};
exports.RenderQueueRepeatItem = RenderQueueRepeatItem;

View File

@@ -0,0 +1,2 @@
import React from 'react';
export declare const SuccessIcon: React.FC;

View File

@@ -0,0 +1,14 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SuccessIcon = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const colors_1 = require("../../helpers/colors");
const CircularProgress_1 = require("./CircularProgress");
const iconStyle = {
height: CircularProgress_1.RENDER_STATUS_INDICATOR_SIZE,
width: CircularProgress_1.RENDER_STATUS_INDICATOR_SIZE,
};
const SuccessIcon = () => {
return ((0, jsx_runtime_1.jsx)("svg", { style: iconStyle, viewBox: "0 0 512 512", children: (0, jsx_runtime_1.jsx)("path", { fill: colors_1.LIGHT_TEXT, d: "M256 512c141.4 0 256-114.6 256-256S397.4 0 256 0S0 114.6 0 256S114.6 512 256 512zM369 209L241 337l-17 17-17-17-64-64-17-17L160 222.1l17 17 47 47L335 175l17-17L385.9 192l-17 17z" }) }));
};
exports.SuccessIcon = SuccessIcon;

View File

@@ -0,0 +1,116 @@
import type { AudioCodec, ChromeMode, Codec, ColorSpace, LogLevel, PixelFormat, StillImageFormat, VideoImageFormat, X264Preset } from '@remotion/renderer';
import type { HardwareAccelerationOption } from '@remotion/renderer/client';
import type { CanUpdateDefaultPropsResponse, EnumPath, RecastCodemod, RenderJob, RequiredChromiumOptions, VisualControlChange } from '@remotion/studio-shared';
import type { _InternalTypes } from 'remotion';
export declare const addStillRenderJob: ({ compositionId, outName, imageFormat, jpegQuality, frame, scale, logLevel, chromiumOptions, delayRenderTimeout, envVariables, inputProps, offthreadVideoCacheSizeInBytes, offthreadVideoThreads, multiProcessOnLinux, beepOnFinish, metadata, chromeMode, mediaCacheSizeInBytes, }: {
compositionId: string;
outName: string;
imageFormat: StillImageFormat;
jpegQuality: number;
frame: number;
scale: number;
logLevel: LogLevel;
chromiumOptions: RequiredChromiumOptions;
delayRenderTimeout: number;
envVariables: Record<string, string>;
inputProps: Record<string, unknown>;
offthreadVideoCacheSizeInBytes: number | null;
offthreadVideoThreads: number | null;
multiProcessOnLinux: boolean;
beepOnFinish: boolean;
metadata: Record<string, string> | null;
chromeMode: ChromeMode;
mediaCacheSizeInBytes: number | null;
}) => Promise<undefined>;
export declare const addSequenceRenderJob: ({ compositionId, outName, imageFormat, startFrame, endFrame, scale, logLevel, chromiumOptions, delayRenderTimeout, envVariables, inputProps, concurrency, offthreadVideoCacheSizeInBytes, offthreadVideoThreads, jpegQuality, disallowParallelEncoding, multiProcessOnLinux, beepOnFinish, repro, metadata, chromeMode, mediaCacheSizeInBytes, }: {
compositionId: string;
outName: string;
imageFormat: VideoImageFormat;
jpegQuality: number;
startFrame: number;
endFrame: number;
scale: number;
logLevel: LogLevel;
chromiumOptions: RequiredChromiumOptions;
concurrency: number;
delayRenderTimeout: number;
envVariables: Record<string, string>;
inputProps: Record<string, unknown>;
offthreadVideoCacheSizeInBytes: number | null;
offthreadVideoThreads: number | null;
disallowParallelEncoding: boolean;
multiProcessOnLinux: boolean;
beepOnFinish: boolean;
repro: boolean;
metadata: Record<string, string> | null;
chromeMode: ChromeMode;
mediaCacheSizeInBytes: number | null;
}) => Promise<undefined>;
export declare const addVideoRenderJob: ({ compositionId, outName, imageFormat, jpegQuality, scale, logLevel, codec, concurrency, crf, startFrame, endFrame, muted, enforceAudioTrack, proResProfile, x264Preset, pixelFormat, audioBitrate, videoBitrate, everyNthFrame, numberOfGifLoops, delayRenderTimeout, audioCodec, disallowParallelEncoding, chromiumOptions, envVariables, inputProps, offthreadVideoCacheSizeInBytes, offthreadVideoThreads, colorSpace, multiProcessOnLinux, encodingMaxRate, encodingBufferSize, beepOnFinish, repro, forSeamlessAacConcatenation, separateAudioTo, metadata, hardwareAcceleration, chromeMode, mediaCacheSizeInBytes, }: {
compositionId: string;
outName: string;
imageFormat: VideoImageFormat;
jpegQuality: number | null;
scale: number;
logLevel: LogLevel;
codec: Codec;
concurrency: number;
crf: number | null;
startFrame: number;
endFrame: number;
muted: boolean;
enforceAudioTrack: boolean;
proResProfile: _InternalTypes["ProResProfile"] | null;
x264Preset: X264Preset | null;
pixelFormat: PixelFormat;
audioBitrate: string | null;
videoBitrate: string | null;
everyNthFrame: number;
numberOfGifLoops: number | null;
delayRenderTimeout: number;
audioCodec: AudioCodec;
disallowParallelEncoding: boolean;
chromiumOptions: RequiredChromiumOptions;
envVariables: Record<string, string>;
inputProps: Record<string, unknown>;
offthreadVideoCacheSizeInBytes: number | null;
offthreadVideoThreads: number | null;
colorSpace: ColorSpace;
multiProcessOnLinux: boolean;
encodingMaxRate: string | null;
encodingBufferSize: string | null;
beepOnFinish: boolean;
repro: boolean;
forSeamlessAacConcatenation: boolean;
separateAudioTo: string | null;
metadata: Record<string, string> | null;
hardwareAcceleration: HardwareAccelerationOption;
chromeMode: ChromeMode;
mediaCacheSizeInBytes: number | null;
}) => Promise<undefined>;
export declare const unsubscribeFromFileExistenceWatcher: ({ file, clientId, }: {
file: string;
clientId: string;
}) => Promise<undefined>;
export declare const subscribeToFileExistenceWatcher: ({ file, clientId, }: {
file: string;
clientId: string;
}) => Promise<boolean>;
export declare const openInFileExplorer: ({ directory }: {
directory: string;
}) => Promise<void>;
export declare const applyCodemod: ({ codemod, dryRun, signal, }: {
codemod: RecastCodemod;
dryRun: boolean;
signal: AbortController["signal"];
}) => Promise<import("@remotion/studio-shared").ApplyCodemodResponse>;
export declare const removeRenderJob: (job: RenderJob) => Promise<undefined>;
export declare const cancelRenderJob: (job: RenderJob) => Promise<import("@remotion/studio-shared").CancelRenderResponse>;
export declare const updateAvailable: (signal: AbortSignal) => Promise<import("@remotion/studio-shared").UpdateAvailableResponse>;
export declare const getProjectInfo: (signal: AbortSignal) => Promise<import("@remotion/studio-shared").ProjectInfoResponse>;
export declare const callUpdateDefaultPropsApi: (compositionId: string, defaultProps: Record<string, unknown>, enumPaths: EnumPath[]) => Promise<import("@remotion/studio-shared").UpdateDefaultPropsResponse>;
export declare const canUpdateDefaultProps: (compositionId: string, readOnlyStudio: boolean) => Promise<CanUpdateDefaultPropsResponse>;
export declare const applyVisualControlChange: ({ fileName, changes, }: {
fileName: string;
changes: VisualControlChange[];
}) => Promise<import("@remotion/studio-shared").ApplyVisualControlResponse>;

View File

@@ -0,0 +1,193 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.applyVisualControlChange = exports.canUpdateDefaultProps = exports.callUpdateDefaultPropsApi = exports.getProjectInfo = exports.updateAvailable = exports.cancelRenderJob = exports.removeRenderJob = exports.applyCodemod = exports.openInFileExplorer = exports.subscribeToFileExistenceWatcher = exports.unsubscribeFromFileExistenceWatcher = exports.addVideoRenderJob = exports.addSequenceRenderJob = exports.addStillRenderJob = void 0;
const no_react_1 = require("remotion/no-react");
const call_api_1 = require("../call-api");
const addStillRenderJob = ({ compositionId, outName, imageFormat, jpegQuality, frame, scale, logLevel, chromiumOptions, delayRenderTimeout, envVariables, inputProps, offthreadVideoCacheSizeInBytes, offthreadVideoThreads, multiProcessOnLinux, beepOnFinish, metadata, chromeMode, mediaCacheSizeInBytes, }) => {
return (0, call_api_1.callApi)('/api/render', {
compositionId,
type: 'still',
outName,
imageFormat,
jpegQuality,
frame,
scale,
logLevel,
chromiumOptions,
delayRenderTimeout,
envVariables,
serializedInputPropsWithCustomSchema: no_react_1.NoReactInternals.serializeJSONWithSpecialTypes({
data: inputProps,
staticBase: window.remotion_staticBase,
indent: undefined,
}).serializedString,
offthreadVideoCacheSizeInBytes,
offthreadVideoThreads,
multiProcessOnLinux,
beepOnFinish,
metadata,
chromeMode,
mediaCacheSizeInBytes,
});
};
exports.addStillRenderJob = addStillRenderJob;
const addSequenceRenderJob = ({ compositionId, outName, imageFormat, startFrame, endFrame, scale, logLevel, chromiumOptions, delayRenderTimeout, envVariables, inputProps, concurrency, offthreadVideoCacheSizeInBytes, offthreadVideoThreads, jpegQuality, disallowParallelEncoding, multiProcessOnLinux, beepOnFinish, repro, metadata, chromeMode, mediaCacheSizeInBytes, }) => {
return (0, call_api_1.callApi)('/api/render', {
compositionId,
type: 'sequence',
outName,
imageFormat,
jpegQuality,
scale,
startFrame,
endFrame,
logLevel,
chromiumOptions,
delayRenderTimeout,
envVariables,
concurrency,
serializedInputPropsWithCustomSchema: no_react_1.NoReactInternals.serializeJSONWithSpecialTypes({
data: inputProps,
staticBase: window.remotion_staticBase,
indent: undefined,
}).serializedString,
offthreadVideoCacheSizeInBytes,
offthreadVideoThreads,
disallowParallelEncoding,
multiProcessOnLinux,
beepOnFinish,
repro,
metadata,
chromeMode,
mediaCacheSizeInBytes,
});
};
exports.addSequenceRenderJob = addSequenceRenderJob;
const addVideoRenderJob = ({ compositionId, outName, imageFormat, jpegQuality, scale, logLevel, codec, concurrency, crf, startFrame, endFrame, muted, enforceAudioTrack, proResProfile, x264Preset, pixelFormat, audioBitrate, videoBitrate, everyNthFrame, numberOfGifLoops, delayRenderTimeout, audioCodec, disallowParallelEncoding, chromiumOptions, envVariables, inputProps, offthreadVideoCacheSizeInBytes, offthreadVideoThreads, colorSpace, multiProcessOnLinux, encodingMaxRate, encodingBufferSize, beepOnFinish, repro, forSeamlessAacConcatenation, separateAudioTo, metadata, hardwareAcceleration, chromeMode, mediaCacheSizeInBytes, }) => {
return (0, call_api_1.callApi)('/api/render', {
compositionId,
type: 'video',
outName,
imageFormat,
jpegQuality,
scale,
logLevel,
codec,
concurrency,
crf,
endFrame,
startFrame,
muted,
enforceAudioTrack,
proResProfile,
x264Preset,
pixelFormat,
audioBitrate,
videoBitrate,
everyNthFrame,
numberOfGifLoops,
delayRenderTimeout,
audioCodec,
disallowParallelEncoding,
chromiumOptions,
envVariables,
serializedInputPropsWithCustomSchema: no_react_1.NoReactInternals.serializeJSONWithSpecialTypes({
data: inputProps,
staticBase: window.remotion_staticBase,
indent: undefined,
}).serializedString,
offthreadVideoCacheSizeInBytes,
offthreadVideoThreads,
colorSpace,
multiProcessOnLinux,
encodingBufferSize,
encodingMaxRate,
beepOnFinish,
repro,
forSeamlessAacConcatenation,
separateAudioTo,
metadata,
hardwareAcceleration,
chromeMode,
mediaCacheSizeInBytes,
});
};
exports.addVideoRenderJob = addVideoRenderJob;
const unsubscribeFromFileExistenceWatcher = ({ file, clientId, }) => {
return (0, call_api_1.callApi)('/api/unsubscribe-from-file-existence', { file, clientId });
};
exports.unsubscribeFromFileExistenceWatcher = unsubscribeFromFileExistenceWatcher;
const subscribeToFileExistenceWatcher = async ({ file, clientId, }) => {
const { exists } = await (0, call_api_1.callApi)('/api/subscribe-to-file-existence', {
file,
clientId,
});
return exists;
};
exports.subscribeToFileExistenceWatcher = subscribeToFileExistenceWatcher;
const openInFileExplorer = ({ directory }) => {
const body = {
directory,
};
return (0, call_api_1.callApi)('/api/open-in-file-explorer', body);
};
exports.openInFileExplorer = openInFileExplorer;
const applyCodemod = ({ codemod, dryRun, signal, }) => {
const body = {
codemod,
dryRun,
};
return (0, call_api_1.callApi)('/api/apply-codemod', body, signal);
};
exports.applyCodemod = applyCodemod;
const removeRenderJob = (job) => {
return (0, call_api_1.callApi)('/api/remove-render', {
jobId: job.id,
});
};
exports.removeRenderJob = removeRenderJob;
const cancelRenderJob = (job) => {
return (0, call_api_1.callApi)('/api/cancel', {
jobId: job.id,
});
};
exports.cancelRenderJob = cancelRenderJob;
const updateAvailable = (signal) => {
return (0, call_api_1.callApi)('/api/update-available', {}, signal);
};
exports.updateAvailable = updateAvailable;
const getProjectInfo = (signal) => {
return (0, call_api_1.callApi)('/api/project-info', {}, signal);
};
exports.getProjectInfo = getProjectInfo;
const callUpdateDefaultPropsApi = (compositionId, defaultProps, enumPaths) => {
return (0, call_api_1.callApi)('/api/update-default-props', {
compositionId,
defaultProps: no_react_1.NoReactInternals.serializeJSONWithSpecialTypes({
data: defaultProps,
indent: undefined,
staticBase: window.remotion_staticBase,
}).serializedString,
enumPaths,
});
};
exports.callUpdateDefaultPropsApi = callUpdateDefaultPropsApi;
const canUpdateDefaultProps = (compositionId, readOnlyStudio) => {
if (readOnlyStudio) {
return Promise.resolve({
canUpdate: false,
reason: 'Read-only studio',
});
}
return (0, call_api_1.callApi)('/api/can-update-default-props', {
compositionId,
});
};
exports.canUpdateDefaultProps = canUpdateDefaultProps;
const applyVisualControlChange = ({ fileName, changes, }) => {
return (0, call_api_1.callApi)('/api/apply-visual-control-change', {
fileName,
changes,
});
};
exports.applyVisualControlChange = applyVisualControlChange;

View File

@@ -0,0 +1,21 @@
import type { ComponentType } from 'react';
import type { CalculateMetadataFunction } from 'remotion';
import type { ClientStillRenderJob, ClientVideoRenderJob } from './client-side-render-types';
export type CompositionRef = {
component: ComponentType<Record<string, unknown>>;
calculateMetadata: CalculateMetadataFunction<Record<string, unknown>> | null;
width: number;
height: number;
fps: number;
durationInFrames: number;
defaultProps: Record<string, unknown>;
};
export declare const registerCompositionForJob: (jobId: string, compositionRef: CompositionRef) => void;
export declare const getCompositionForJob: (jobId: string) => CompositionRef | undefined;
export declare const cleanupCompositionForJob: (jobId: string) => void;
export declare const generateJobId: () => string;
export declare const getAbortController: (jobId: string) => AbortController;
export declare const deleteAbortController: (jobId: string) => void;
export declare const cancelAbortController: (jobId: string) => void;
export type AddClientStillJobParams = Omit<ClientStillRenderJob, 'id' | 'startedAt' | 'status'>;
export type AddClientVideoJobParams = Omit<ClientVideoRenderJob, 'id' | 'startedAt' | 'status'>;

View File

@@ -0,0 +1,41 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.cancelAbortController = exports.deleteAbortController = exports.getAbortController = exports.generateJobId = exports.cleanupCompositionForJob = exports.getCompositionForJob = exports.registerCompositionForJob = void 0;
const compositionRegistry = new Map();
const registerCompositionForJob = (jobId, compositionRef) => {
compositionRegistry.set(jobId, compositionRef);
};
exports.registerCompositionForJob = registerCompositionForJob;
const getCompositionForJob = (jobId) => {
return compositionRegistry.get(jobId);
};
exports.getCompositionForJob = getCompositionForJob;
const cleanupCompositionForJob = (jobId) => {
compositionRegistry.delete(jobId);
};
exports.cleanupCompositionForJob = cleanupCompositionForJob;
const generateJobId = () => {
return `client-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
};
exports.generateJobId = generateJobId;
const clientJobAbortControllers = new Map();
const getAbortController = (jobId) => {
let controller = clientJobAbortControllers.get(jobId);
if (!controller) {
controller = new AbortController();
clientJobAbortControllers.set(jobId, controller);
}
return controller;
};
exports.getAbortController = getAbortController;
const deleteAbortController = (jobId) => {
clientJobAbortControllers.delete(jobId);
};
exports.deleteAbortController = deleteAbortController;
const cancelAbortController = (jobId) => {
const controller = clientJobAbortControllers.get(jobId);
if (controller) {
controller.abort();
}
};
exports.cancelAbortController = cancelAbortController;

View File

@@ -0,0 +1,64 @@
import type { RenderStillOnWebImageFormat, WebRendererAudioCodec, WebRendererContainer, WebRendererQuality, WebRendererVideoCodec } from '@remotion/web-renderer';
import type { LogLevel } from 'remotion';
export type ClientRenderJobProgress = {
renderedFrames: number;
encodedFrames: number;
totalFrames: number;
};
export type GetBlobCallback = () => Promise<Blob>;
export type ClientRenderMetadata = {
width: number;
height: number;
sizeInBytes: number;
};
type ClientRenderJobDynamicStatus = {
status: 'idle';
} | {
status: 'running';
progress: ClientRenderJobProgress;
} | {
status: 'done';
getBlob: GetBlobCallback;
metadata: ClientRenderMetadata;
} | {
status: 'cancelled';
} | {
status: 'failed';
error: {
message: string;
stack: string | undefined;
};
};
type ClientRenderJobBase = {
id: string;
startedAt: number;
compositionId: string;
outName: string;
inputProps: Record<string, unknown>;
delayRenderTimeout: number;
mediaCacheSizeInBytes: number | null;
logLevel: LogLevel;
licenseKey: string | null;
scale: number;
};
export type ClientStillRenderJob = ClientRenderJobBase & {
type: 'client-still';
imageFormat: RenderStillOnWebImageFormat;
frame: number;
} & ClientRenderJobDynamicStatus;
export type ClientVideoRenderJob = ClientRenderJobBase & {
type: 'client-video';
container: WebRendererContainer;
videoCodec: WebRendererVideoCodec;
audioCodec: WebRendererAudioCodec;
startFrame: number;
endFrame: number;
audioBitrate: WebRendererQuality;
videoBitrate: WebRendererQuality;
hardwareAcceleration: string;
keyframeIntervalInSeconds: number;
transparent: boolean;
muted: boolean;
} & ClientRenderJobDynamicStatus;
export type ClientRenderJob = ClientStillRenderJob | ClientVideoRenderJob;
export {};

View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@@ -0,0 +1,35 @@
import type { RenderJob } from '@remotion/studio-shared';
import React from 'react';
import { type AddClientStillJobParams, type AddClientVideoJobParams, type CompositionRef } from './client-render-queue';
import type { ClientRenderJob, ClientRenderJobProgress, ClientRenderMetadata, GetBlobCallback } from './client-side-render-types';
declare global {
interface Window {
remotion_initialRenderQueue: RenderJob[] | null;
}
}
export type AnyRenderJob = RenderJob | ClientRenderJob;
export declare const isClientRenderJob: (job: AnyRenderJob) => job is ClientRenderJob;
type RenderQueueContextType = {
jobs: AnyRenderJob[];
serverJobs: RenderJob[];
clientJobs: ClientRenderJob[];
addClientStillJob: (params: AddClientStillJobParams, compositionRef: CompositionRef) => string;
addClientVideoJob: (params: AddClientVideoJobParams, compositionRef: CompositionRef) => string;
updateClientJobProgress: (jobId: string, progress: ClientRenderJobProgress) => void;
markClientJobDone: (jobId: string, getBlob: GetBlobCallback, metadata: ClientRenderMetadata) => void;
markClientJobFailed: (jobId: string, error: Error) => void;
markClientJobCancelled: (jobId: string) => void;
removeClientJob: (jobId: string) => void;
cancelClientJob: (jobId: string) => void;
setProcessJobCallback: (callback: ((job: ClientRenderJob) => Promise<void>) | null) => void;
getAbortController: (jobId: string) => AbortController;
getCompositionForJob: (jobId: string) => CompositionRef | undefined;
};
export declare const RenderQueueContext: React.Context<RenderQueueContextType>;
export declare const renderJobsRef: React.RefObject<{
updateRenderJobs: (jobs: RenderJob[]) => void;
} | null>;
export declare const RenderQueueContextProvider: React.FC<{
readonly children: React.ReactNode;
}>;
export {};

View File

@@ -0,0 +1,202 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.RenderQueueContextProvider = exports.renderJobsRef = exports.RenderQueueContext = exports.isClientRenderJob = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = __importStar(require("react"));
const client_render_queue_1 = require("./client-render-queue");
const isClientRenderJob = (job) => {
return job.type === 'client-still' || job.type === 'client-video';
};
exports.isClientRenderJob = isClientRenderJob;
const noopString = () => '';
const noop = () => undefined;
exports.RenderQueueContext = react_1.default.createContext({
jobs: [],
serverJobs: [],
clientJobs: [],
addClientStillJob: noopString,
addClientVideoJob: noopString,
updateClientJobProgress: noop,
markClientJobDone: noop,
markClientJobFailed: noop,
markClientJobCancelled: noop,
removeClientJob: noop,
cancelClientJob: noop,
setProcessJobCallback: noop,
getAbortController: () => new AbortController(),
getCompositionForJob: () => undefined,
});
exports.renderJobsRef = (0, react_1.createRef)();
const RenderQueueContextProvider = ({ children }) => {
var _a;
const [serverJobs, setServerJobs] = (0, react_1.useState)((_a = window.remotion_initialRenderQueue) !== null && _a !== void 0 ? _a : []);
const [clientJobs, setClientJobs] = (0, react_1.useState)([]);
const [currentlyProcessing, setCurrentlyProcessing] = (0, react_1.useState)(null);
const processJobCallbackRef = (0, react_1.useRef)(null);
// Process next job when state changes
(0, react_1.useEffect)(() => {
if (currentlyProcessing) {
return;
}
const nextJob = clientJobs.find((job) => job.status === 'idle');
if (!nextJob || !processJobCallbackRef.current) {
return;
}
setCurrentlyProcessing(nextJob.id);
setClientJobs((prev) => prev.map((job) => job.id === nextJob.id
? {
...job,
status: 'running',
progress: { renderedFrames: 0, encodedFrames: 0, totalFrames: 0 },
}
: job));
processJobCallbackRef.current(nextJob);
}, [clientJobs, currentlyProcessing]);
const addClientStillJob = (0, react_1.useCallback)((params, compositionRef) => {
const id = (0, client_render_queue_1.generateJobId)();
(0, client_render_queue_1.registerCompositionForJob)(id, compositionRef);
const newJob = {
...params,
id,
startedAt: Date.now(),
status: 'idle',
};
setClientJobs((prev) => [...prev, newJob]);
return id;
}, []);
const addClientVideoJob = (0, react_1.useCallback)((params, compositionRef) => {
const id = (0, client_render_queue_1.generateJobId)();
(0, client_render_queue_1.registerCompositionForJob)(id, compositionRef);
const newJob = {
...params,
id,
startedAt: Date.now(),
status: 'idle',
};
setClientJobs((prev) => [...prev, newJob]);
return id;
}, []);
const updateClientJobProgress = (0, react_1.useCallback)((jobId, progress) => {
setClientJobs((prev) => prev.map((job) => job.id === jobId
? { ...job, status: 'running', progress }
: job));
}, []);
const markClientJobDone = (0, react_1.useCallback)((jobId, getBlob, metadata) => {
(0, client_render_queue_1.deleteAbortController)(jobId);
(0, client_render_queue_1.cleanupCompositionForJob)(jobId);
setClientJobs((prev) => prev.map((job) => job.id === jobId
? { ...job, status: 'done', getBlob, metadata }
: job));
setCurrentlyProcessing(null);
}, []);
const markClientJobFailed = (0, react_1.useCallback)((jobId, error) => {
(0, client_render_queue_1.deleteAbortController)(jobId);
(0, client_render_queue_1.cleanupCompositionForJob)(jobId);
setClientJobs((prev) => prev.map((job) => job.id === jobId
? {
...job,
status: 'failed',
error: { message: error.message, stack: error.stack },
}
: job));
setCurrentlyProcessing(null);
}, []);
const markClientJobCancelled = (0, react_1.useCallback)((jobId) => {
(0, client_render_queue_1.deleteAbortController)(jobId);
(0, client_render_queue_1.cleanupCompositionForJob)(jobId);
setClientJobs((prev) => prev.map((job) => job.id === jobId
? {
...job,
status: 'cancelled',
}
: job));
setCurrentlyProcessing(null);
}, []);
const removeClientJob = (0, react_1.useCallback)((jobId) => {
setClientJobs((prev) => {
const jobToRemove = prev.find((j) => j.id === jobId);
if ((jobToRemove === null || jobToRemove === void 0 ? void 0 : jobToRemove.status) === 'running') {
return prev;
}
(0, client_render_queue_1.deleteAbortController)(jobId);
(0, client_render_queue_1.cleanupCompositionForJob)(jobId);
return prev.filter((job) => job.id !== jobId);
});
}, []);
const cancelClientJob = (0, react_1.useCallback)((jobId) => {
(0, client_render_queue_1.cancelAbortController)(jobId);
}, []);
const setProcessJobCallback = (0, react_1.useCallback)((callback) => {
processJobCallbackRef.current = callback;
}, []);
(0, react_1.useImperativeHandle)(exports.renderJobsRef, () => ({
updateRenderJobs: (newJobs) => {
setServerJobs(newJobs);
},
}), []);
const value = (0, react_1.useMemo)(() => {
return {
jobs: [...serverJobs, ...clientJobs],
serverJobs,
clientJobs,
addClientStillJob,
addClientVideoJob,
updateClientJobProgress,
markClientJobDone,
markClientJobFailed,
markClientJobCancelled,
removeClientJob,
cancelClientJob,
setProcessJobCallback,
getAbortController: client_render_queue_1.getAbortController,
getCompositionForJob: client_render_queue_1.getCompositionForJob,
};
}, [
serverJobs,
clientJobs,
addClientStillJob,
addClientVideoJob,
updateClientJobProgress,
markClientJobDone,
markClientJobFailed,
markClientJobCancelled,
removeClientJob,
cancelClientJob,
setProcessJobCallback,
]);
return ((0, jsx_runtime_1.jsx)(exports.RenderQueueContext.Provider, { value: value, children: children }));
};
exports.RenderQueueContextProvider = RenderQueueContextProvider;

View File

@@ -0,0 +1,2 @@
import React from 'react';
export declare const RenderQueue: React.FC;

View File

@@ -0,0 +1,128 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.RenderQueue = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = __importStar(require("react"));
const remotion_1 = require("remotion");
const client_id_1 = require("../../helpers/client-id");
const colors_1 = require("../../helpers/colors");
const is_menu_item_1 = require("../Menu/is-menu-item");
const layout_1 = require("../layout");
const RenderQueueItem_1 = require("./RenderQueueItem");
const context_1 = require("./context");
const separatorStyle = {
borderBottom: `1px solid ${colors_1.BORDER_COLOR}`,
};
const errorExplanation = {
fontSize: 14,
color: colors_1.LIGHT_TEXT,
fontFamily: 'sans-serif',
lineHeight: 1.5,
};
const explainer = {
display: 'flex',
flex: 1,
flexDirection: 'column',
padding: '0 12px',
justifyContent: 'center',
alignItems: 'center',
textAlign: 'center',
background: colors_1.BACKGROUND,
};
const renderQueue = {
background: colors_1.BACKGROUND,
flex: 1,
overflowY: 'auto',
};
const RenderQueue = () => {
const connectionStatus = (0, react_1.useContext)(client_id_1.StudioServerConnectionCtx)
.previewServerState.type;
const { jobs } = (0, react_1.useContext)(context_1.RenderQueueContext);
const { canvasContent } = (0, react_1.useContext)(remotion_1.Internals.CompositionManager);
const previousJobCount = react_1.default.useRef(jobs.length);
const jobCount = jobs.length;
const divRef = react_1.default.useRef(null);
(0, react_1.useEffect)(() => {
if (!divRef.current) {
return;
}
// Scroll down to bottom of render queue if new jobs have been added
if (jobCount > previousJobCount.current) {
divRef.current.scrollTo({
top: divRef.current.scrollHeight,
behavior: 'smooth',
});
}
previousJobCount.current = jobCount;
}, [jobCount]);
const selectedJob = (0, react_1.useMemo)(() => {
if (!canvasContent) {
return -1;
}
if (canvasContent.type === 'output-blob') {
for (let i = 0; i < jobs.length; i++) {
const job = jobs[i];
if ((0, context_1.isClientRenderJob)(job) && job.status === 'done') {
if (canvasContent.getBlob === job.getBlob) {
return i;
}
}
}
return -1;
}
if (canvasContent.type === 'output') {
for (let i = 0; i < jobs.length; i++) {
const job = jobs[i];
if (!(0, context_1.isClientRenderJob)(job) &&
job.status === 'done' &&
canvasContent.path === `/${job.outName}`) {
return i;
}
}
}
return -1;
}, [canvasContent, jobs]);
if (connectionStatus === 'disconnected') {
return ((0, jsx_runtime_1.jsxs)("div", { style: explainer, children: [(0, jsx_runtime_1.jsx)(layout_1.Spacing, { y: 5 }), (0, jsx_runtime_1.jsx)("div", { style: errorExplanation, children: "The studio server has disconnected." }), (0, jsx_runtime_1.jsx)(layout_1.Spacing, { y: 2, block: true })] }));
}
if (jobCount === 0) {
return ((0, jsx_runtime_1.jsxs)("div", { style: explainer, children: [(0, jsx_runtime_1.jsx)(layout_1.Spacing, { y: 5 }), (0, jsx_runtime_1.jsx)("div", { style: errorExplanation, children: "No renders in the queue." }), (0, jsx_runtime_1.jsx)(layout_1.Spacing, { y: 2, block: true })] }));
}
return ((0, jsx_runtime_1.jsx)("div", { ref: divRef, style: renderQueue, className: ['css-reset', is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME].join(' '), children: jobs.map((j, index) => {
return ((0, jsx_runtime_1.jsx)("div", { style: index === jobs.length - 1 ? undefined : separatorStyle, children: (0, jsx_runtime_1.jsx)(RenderQueueItem_1.RenderQueueItem, { selected: selectedJob === index, job: j }) }, j.id));
}) }));
};
exports.RenderQueue = RenderQueue;

View File

@@ -0,0 +1,2 @@
import type React from 'react';
export declare const renderQueueItemSubtitleStyle: React.CSSProperties;

View File

@@ -0,0 +1,21 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.renderQueueItemSubtitleStyle = void 0;
const colors_1 = require("../../helpers/colors");
const layout_1 = require("../layout");
exports.renderQueueItemSubtitleStyle = {
fontSize: 13,
color: colors_1.LIGHT_TEXT,
appearance: 'none',
border: 'none',
padding: 0,
cursor: 'pointer',
lineHeight: 1.2,
textAlign: 'left',
whiteSpace: 'nowrap',
marginRight: layout_1.SPACING_UNIT,
overflowX: 'hidden',
// size smaller than viewport causes actual ellipse
maxWidth: 500,
textOverflow: 'ellipsis',
};