@remotion/studio-server 4.0.487 → 4.0.489
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/better-opn/index.d.ts +1 -0
- package/dist/better-opn/index.js +20 -12
- package/dist/index.d.ts +3 -1
- package/dist/preview-server/api-routes.js +2 -0
- package/dist/preview-server/element-install-state.d.ts +14 -0
- package/dist/preview-server/element-install-state.js +43 -0
- package/dist/preview-server/live-events.d.ts +1 -1
- package/dist/preview-server/live-events.js +4 -2
- package/dist/preview-server/routes/update-element-install-target.d.ts +3 -0
- package/dist/preview-server/routes/update-element-install-target.js +28 -0
- package/dist/preview-server/start-server.d.ts +3 -1
- package/dist/preview-server/start-server.js +2 -0
- package/dist/preview-server/validate-same-origin.js +17 -4
- package/dist/routes.d.ts +3 -2
- package/dist/routes.js +172 -3
- package/dist/start-studio.d.ts +4 -2
- package/dist/start-studio.js +3 -1
- package/package.json +6 -6
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export declare const getChromiumBrowsersToTry: (processes: string) => ("Arc" | "Brave Browser" | "Chromium" | "Google Chrome" | "Google Chrome Canary" | "Microsoft Edge" | "Vivaldi")[];
|
|
1
2
|
export declare const openBrowser: ({ url, browserFlag, browserArgs, }: {
|
|
2
3
|
url: string;
|
|
3
4
|
browserFlag: string | undefined;
|
package/dist/better-opn/index.js
CHANGED
|
@@ -1,9 +1,26 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// Copied from https://github.com/michaellzc/better-opn#readme
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.openBrowser = void 0;
|
|
4
|
+
exports.openBrowser = exports.getChromiumBrowsersToTry = void 0;
|
|
5
5
|
const node_child_process_1 = require("node:child_process");
|
|
6
6
|
const open = require("open");
|
|
7
|
+
const supportedChromiumBrowsers = [
|
|
8
|
+
'Google Chrome',
|
|
9
|
+
'Google Chrome Canary',
|
|
10
|
+
'Microsoft Edge',
|
|
11
|
+
'Brave Browser',
|
|
12
|
+
'Vivaldi',
|
|
13
|
+
'Chromium',
|
|
14
|
+
'Arc',
|
|
15
|
+
];
|
|
16
|
+
const getChromiumBrowsersToTry = (processes) => {
|
|
17
|
+
const processNames = new Set(processes
|
|
18
|
+
.split('\n')
|
|
19
|
+
.map((line) => line.trim())
|
|
20
|
+
.filter(Boolean));
|
|
21
|
+
return supportedChromiumBrowsers.filter((browser) => processNames.has(browser));
|
|
22
|
+
};
|
|
23
|
+
exports.getChromiumBrowsersToTry = getChromiumBrowsersToTry;
|
|
7
24
|
const normalizeURLToMatch = (target) => {
|
|
8
25
|
// We may encounter URL parse error but want to fallback to default behavior
|
|
9
26
|
try {
|
|
@@ -26,17 +43,8 @@ const startBrowserProcess = async ({ browser, url, args, }) => {
|
|
|
26
43
|
if (shouldTryOpenChromiumWithAppleScript) {
|
|
27
44
|
let appleScriptDenied = false;
|
|
28
45
|
// Will use the first open browser found from list
|
|
29
|
-
const supportedChromiumBrowsers = [
|
|
30
|
-
'Google Chrome',
|
|
31
|
-
'Google Chrome Canary',
|
|
32
|
-
'Microsoft Edge',
|
|
33
|
-
'Brave Browser',
|
|
34
|
-
'Vivaldi',
|
|
35
|
-
'Chromium',
|
|
36
|
-
'Arc',
|
|
37
|
-
];
|
|
38
46
|
const processes = await new Promise((resolve, reject) => {
|
|
39
|
-
(0, node_child_process_1.exec)('ps cax', (err, stdout) => {
|
|
47
|
+
(0, node_child_process_1.exec)('ps -cax -o comm=', (err, stdout) => {
|
|
40
48
|
if (err) {
|
|
41
49
|
reject(err);
|
|
42
50
|
}
|
|
@@ -45,7 +53,7 @@ const startBrowserProcess = async ({ browser, url, args, }) => {
|
|
|
45
53
|
}
|
|
46
54
|
});
|
|
47
55
|
});
|
|
48
|
-
const browsersToTry =
|
|
56
|
+
const browsersToTry = (0, exports.getChromiumBrowsersToTry)(processes);
|
|
49
57
|
for (const chromiumBrowser of browsersToTry) {
|
|
50
58
|
if (appleScriptDenied) {
|
|
51
59
|
continue;
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export { ApiRoutes, CopyStillToClipboardRequest, getDefaultOutLocation, OpenInFi
|
|
|
2
2
|
export type { AggregateRenderProgress, BundlingState, CopyingState, DownloadProgress, HotMiddlewareOptions, JobProgressCallback, ModuleMap, PackageManager, ProjectInfo, RenderingProgressInput, RenderJob, RenderJobWithCleanup, RequiredChromiumOptions, StitchingProgressInput, UiOpenGlOptions, } from '@remotion/studio-shared';
|
|
3
3
|
import { AnsiDiff } from './ansi-diff';
|
|
4
4
|
export declare const StudioServerInternals: {
|
|
5
|
-
startStudio: ({ browserArgs, browserFlag, shouldOpenBrowser, fullEntryPath, logLevel, getCurrentInputProps, getEnvVariables, desiredPort, maxTimelineTracks, remotionRoot, keyboardShortcutsEnabled, experimentalClientSideRenderingEnabled, relativePublicDir, webpackOverride, poll, getRenderDefaults, getRenderQueue, numberOfAudioTags, queueMethods, previewEntry, gitSource, bufferStateDelayInMilliseconds, binariesDirectory, forceIPv4, audioLatencyHint, previewSampleRate, enableCrossSiteIsolation, askAIEnabled, forceNew, rspack, }: {
|
|
5
|
+
startStudio: ({ browserArgs, browserFlag, shouldOpenBrowser, fullEntryPath, logLevel, getCurrentInputProps, getEnvVariables, desiredPort, maxTimelineTracks, remotionRoot, keyboardShortcutsEnabled, experimentalClientSideRenderingEnabled, relativePublicDir, webpackOverride, poll, getRenderDefaults, getRenderQueue, numberOfAudioTags, queueMethods, previewEntry, gitSource, bufferStateDelayInMilliseconds, binariesDirectory, forceIPv4, audioLatencyHint, previewSampleRate, enableCrossSiteIsolation, askAIEnabled, interactivityEnabled, forceNew, rspack, getStudioRuntimeConfig, }: {
|
|
6
6
|
browserArgs: string;
|
|
7
7
|
browserFlag: string;
|
|
8
8
|
logLevel: "error" | "info" | "trace" | "verbose" | "warn";
|
|
@@ -31,8 +31,10 @@ export declare const StudioServerInternals: {
|
|
|
31
31
|
binariesDirectory: string | null;
|
|
32
32
|
forceIPv4: boolean;
|
|
33
33
|
askAIEnabled: boolean;
|
|
34
|
+
interactivityEnabled: boolean;
|
|
34
35
|
forceNew: boolean;
|
|
35
36
|
rspack: boolean;
|
|
37
|
+
getStudioRuntimeConfig: () => import("@remotion/studio-shared").StudioRuntimeConfig;
|
|
36
38
|
}) => Promise<import("./start-studio").StartStudioResult>;
|
|
37
39
|
getRemotionVersion: () => any;
|
|
38
40
|
waitForLiveEventsListener: () => Promise<import("./preview-server/live-events").LiveEventsServer>;
|
|
@@ -47,6 +47,7 @@ const unsubscribe_from_sequence_props_1 = require("./routes/unsubscribe-from-seq
|
|
|
47
47
|
const update_available_1 = require("./routes/update-available");
|
|
48
48
|
const update_default_props_1 = require("./routes/update-default-props");
|
|
49
49
|
const update_effect_keyframe_settings_1 = require("./routes/update-effect-keyframe-settings");
|
|
50
|
+
const update_element_install_target_1 = require("./routes/update-element-install-target");
|
|
50
51
|
const update_sequence_keyframe_settings_1 = require("./routes/update-sequence-keyframe-settings");
|
|
51
52
|
exports.allApiRoutes = {
|
|
52
53
|
'/api/composition-component-info': composition_component_info_1.compositionComponentInfoHandler,
|
|
@@ -92,6 +93,7 @@ exports.allApiRoutes = {
|
|
|
92
93
|
'/api/install-package': install_dependency_1.handleInstallPackage,
|
|
93
94
|
'/api/insert-jsx-element': insert_jsx_element_1.insertJsxElementHandler,
|
|
94
95
|
'/api/insert-element': insert_element_1.insertElementHandler,
|
|
96
|
+
'/api/update-element-install-target': update_element_install_target_1.updateElementInstallTargetHandler,
|
|
95
97
|
'/api/download-remote-asset': download_remote_asset_1.downloadRemoteAssetHandler,
|
|
96
98
|
'/api/undo': undo_1.undoHandler,
|
|
97
99
|
'/api/redo': redo_1.redoHandler,
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const ELEMENT_INSTALL_TARGET_MAX_AGE = 5000;
|
|
2
|
+
export type ElementInstallTarget = {
|
|
3
|
+
requestId: string | null;
|
|
4
|
+
clientId: string;
|
|
5
|
+
compositionFile: string | null;
|
|
6
|
+
compositionId: string | null;
|
|
7
|
+
canInstall: boolean;
|
|
8
|
+
lastFocusedAt: number | null;
|
|
9
|
+
readOnly: boolean;
|
|
10
|
+
updatedAt: number;
|
|
11
|
+
};
|
|
12
|
+
export declare const updateElementInstallTarget: (newTarget: Omit<ElementInstallTarget, "updatedAt">) => void;
|
|
13
|
+
export declare const getElementInstallTarget: (requestId: string | null) => ElementInstallTarget | null;
|
|
14
|
+
export declare const clearElementInstallStateForTests: () => void;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.clearElementInstallStateForTests = exports.getElementInstallTarget = exports.updateElementInstallTarget = exports.ELEMENT_INSTALL_TARGET_MAX_AGE = void 0;
|
|
4
|
+
exports.ELEMENT_INSTALL_TARGET_MAX_AGE = 5000;
|
|
5
|
+
const targetsByClientId = new Map();
|
|
6
|
+
const compareTargets = (a, b) => {
|
|
7
|
+
var _a, _b;
|
|
8
|
+
const aFocusedAt = (_a = a.lastFocusedAt) !== null && _a !== void 0 ? _a : 0;
|
|
9
|
+
const bFocusedAt = (_b = b.lastFocusedAt) !== null && _b !== void 0 ? _b : 0;
|
|
10
|
+
if (aFocusedAt !== bFocusedAt) {
|
|
11
|
+
return aFocusedAt - bFocusedAt;
|
|
12
|
+
}
|
|
13
|
+
return a.updatedAt - b.updatedAt;
|
|
14
|
+
};
|
|
15
|
+
const updateElementInstallTarget = (newTarget) => {
|
|
16
|
+
targetsByClientId.set(newTarget.clientId, {
|
|
17
|
+
...newTarget,
|
|
18
|
+
updatedAt: Date.now(),
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
exports.updateElementInstallTarget = updateElementInstallTarget;
|
|
22
|
+
const getElementInstallTarget = (requestId) => {
|
|
23
|
+
const now = Date.now();
|
|
24
|
+
let bestTarget = null;
|
|
25
|
+
for (const [clientId, currentTarget] of targetsByClientId) {
|
|
26
|
+
if (now - currentTarget.updatedAt >= exports.ELEMENT_INSTALL_TARGET_MAX_AGE) {
|
|
27
|
+
targetsByClientId.delete(clientId);
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (requestId !== null && currentTarget.requestId !== requestId) {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (bestTarget === null || compareTargets(currentTarget, bestTarget) > 0) {
|
|
34
|
+
bestTarget = currentTarget;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return bestTarget;
|
|
38
|
+
};
|
|
39
|
+
exports.getElementInstallTarget = getElementInstallTarget;
|
|
40
|
+
const clearElementInstallStateForTests = () => {
|
|
41
|
+
targetsByClientId.clear();
|
|
42
|
+
};
|
|
43
|
+
exports.clearElementInstallStateForTests = clearElementInstallStateForTests;
|
|
@@ -2,7 +2,7 @@ import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
|
2
2
|
import type { EventSourceEvent } from '@remotion/studio-shared';
|
|
3
3
|
export type LiveEventsServer = {
|
|
4
4
|
sendEventToClient: (event: EventSourceEvent) => void;
|
|
5
|
-
sendEventToClientId: (clientId: string, event: EventSourceEvent) =>
|
|
5
|
+
sendEventToClientId: (clientId: string, event: EventSourceEvent) => boolean;
|
|
6
6
|
router: (request: IncomingMessage, response: ServerResponse) => Promise<void>;
|
|
7
7
|
closeConnections: () => Promise<void>;
|
|
8
8
|
addNewClientListener: (cb: () => void) => () => void;
|
|
@@ -63,9 +63,11 @@ const makeLiveEventsRouter = (logLevel, getInitialUndoRedoState) => {
|
|
|
63
63
|
};
|
|
64
64
|
const sendEventToClientId = (clientId, event) => {
|
|
65
65
|
const client = clients.find((c) => c.id === clientId);
|
|
66
|
-
if (client) {
|
|
67
|
-
|
|
66
|
+
if (!client) {
|
|
67
|
+
return false;
|
|
68
68
|
}
|
|
69
|
+
client.response.write(serializeMessage(event));
|
|
70
|
+
return true;
|
|
69
71
|
};
|
|
70
72
|
const addNewClientListener = (cb) => {
|
|
71
73
|
newClientListeners.push(cb);
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { UpdateElementInstallTargetRequest, UpdateElementInstallTargetResponse } from '@remotion/studio-shared';
|
|
2
|
+
import type { ApiHandler } from '../api-types';
|
|
3
|
+
export declare const updateElementInstallTargetHandler: ApiHandler<UpdateElementInstallTargetRequest, UpdateElementInstallTargetResponse>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.updateElementInstallTargetHandler = void 0;
|
|
4
|
+
const element_install_state_1 = require("../element-install-state");
|
|
5
|
+
const isFiniteTimestamp = (value) => {
|
|
6
|
+
return value === null || (Number.isFinite(value) && value > 0);
|
|
7
|
+
};
|
|
8
|
+
const updateElementInstallTargetHandler = ({ input }) => {
|
|
9
|
+
if (typeof input.clientId !== 'string' || input.clientId.length === 0) {
|
|
10
|
+
throw new Error('Invalid client ID');
|
|
11
|
+
}
|
|
12
|
+
if (input.requestId !== null && typeof input.requestId !== 'string') {
|
|
13
|
+
throw new Error('Invalid request ID');
|
|
14
|
+
}
|
|
15
|
+
if (!isFiniteTimestamp(input.lastFocusedAt)) {
|
|
16
|
+
throw new Error('Invalid focus timestamp');
|
|
17
|
+
}
|
|
18
|
+
if (input.compositionFile !== null &&
|
|
19
|
+
typeof input.compositionFile !== 'string') {
|
|
20
|
+
throw new Error('Invalid composition file');
|
|
21
|
+
}
|
|
22
|
+
if (input.compositionId !== null && typeof input.compositionId !== 'string') {
|
|
23
|
+
throw new Error('Invalid composition ID');
|
|
24
|
+
}
|
|
25
|
+
(0, element_install_state_1.updateElementInstallTarget)(input);
|
|
26
|
+
return Promise.resolve({});
|
|
27
|
+
};
|
|
28
|
+
exports.updateElementInstallTargetHandler = updateElementInstallTargetHandler;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { WebpackOverrideFn } from '@remotion/bundler';
|
|
2
|
-
import type { GitSource, RenderDefaults, RenderJob } from '@remotion/studio-shared';
|
|
2
|
+
import type { GitSource, RenderDefaults, RenderJob, StudioRuntimeConfig } from '@remotion/studio-shared';
|
|
3
3
|
import type { QueueMethods } from './api-types';
|
|
4
4
|
import type { LiveEventsServer } from './live-events';
|
|
5
5
|
export type StartServerResult = {
|
|
@@ -41,6 +41,8 @@ export declare const startServer: (options: {
|
|
|
41
41
|
previewSampleRate: number | null;
|
|
42
42
|
enableCrossSiteIsolation: boolean;
|
|
43
43
|
askAIEnabled: boolean;
|
|
44
|
+
interactivityEnabled: boolean;
|
|
44
45
|
forceNew: boolean;
|
|
45
46
|
rspack: boolean;
|
|
47
|
+
getStudioRuntimeConfig: () => StudioRuntimeConfig;
|
|
46
48
|
}) => Promise<StartServerResult>;
|
|
@@ -45,6 +45,7 @@ const startServer = async (options) => {
|
|
|
45
45
|
poll: options.poll,
|
|
46
46
|
bufferStateDelayInMilliseconds: options.bufferStateDelayInMilliseconds,
|
|
47
47
|
askAIEnabled: options.askAIEnabled,
|
|
48
|
+
interactivityEnabled: options.interactivityEnabled,
|
|
48
49
|
extraPlugins: [watchIgnorePlugin],
|
|
49
50
|
};
|
|
50
51
|
let compiler;
|
|
@@ -104,6 +105,7 @@ const startServer = async (options) => {
|
|
|
104
105
|
audioLatencyHint: options.audioLatencyHint,
|
|
105
106
|
previewSampleRate: options.previewSampleRate,
|
|
106
107
|
enableCrossSiteIsolation: options.enableCrossSiteIsolation,
|
|
108
|
+
getStudioRuntimeConfig: options.getStudioRuntimeConfig,
|
|
107
109
|
});
|
|
108
110
|
})
|
|
109
111
|
.catch((err) => {
|
|
@@ -3,11 +3,24 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.validateSameOrigin = void 0;
|
|
4
4
|
const validateSameOrigin = (req) => {
|
|
5
5
|
const { origin, host } = req.headers;
|
|
6
|
-
if (origin) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
throw new Error('Request from different origin not allowed');
|
|
6
|
+
if (typeof origin !== 'string') {
|
|
7
|
+
if (req.method === 'GET' || req.method === 'HEAD') {
|
|
8
|
+
return;
|
|
10
9
|
}
|
|
10
|
+
throw new Error('Request without Origin header not allowed');
|
|
11
|
+
}
|
|
12
|
+
if (typeof host !== 'string') {
|
|
13
|
+
throw new Error('Request without Host header not allowed');
|
|
14
|
+
}
|
|
15
|
+
let originUrl;
|
|
16
|
+
try {
|
|
17
|
+
originUrl = new URL(origin);
|
|
18
|
+
}
|
|
19
|
+
catch (_a) {
|
|
20
|
+
throw new Error('Request from different origin not allowed');
|
|
21
|
+
}
|
|
22
|
+
if (originUrl.origin !== `http://${host}`) {
|
|
23
|
+
throw new Error('Request from different origin not allowed');
|
|
11
24
|
}
|
|
12
25
|
};
|
|
13
26
|
exports.validateSameOrigin = validateSameOrigin;
|
package/dist/routes.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
-
import type { GitSource, RenderDefaults, RenderJob } from '@remotion/studio-shared';
|
|
2
|
+
import type { GitSource, RenderDefaults, RenderJob, StudioRuntimeConfig } from '@remotion/studio-shared';
|
|
3
3
|
import type { QueueMethods } from './preview-server/api-types';
|
|
4
4
|
import type { LiveEventsServer } from './preview-server/live-events';
|
|
5
|
-
export declare const handleRoutes: ({ staticHash, staticHashPrefix, outputHash, outputHashPrefix, request, response, liveEventsServer, getCurrentInputProps, getEnvVariables, remotionRoot, entryPoint, publicDir, logLevel, getRenderQueue, getRenderDefaults, numberOfAudioTags, queueMethods: methods, gitSource, binariesDirectory, audioLatencyHint, previewSampleRate, enableCrossSiteIsolation, }: {
|
|
5
|
+
export declare const handleRoutes: ({ staticHash, staticHashPrefix, outputHash, outputHashPrefix, request, response, liveEventsServer, getCurrentInputProps, getEnvVariables, remotionRoot, entryPoint, publicDir, logLevel, getRenderQueue, getRenderDefaults, numberOfAudioTags, queueMethods: methods, gitSource, binariesDirectory, audioLatencyHint, previewSampleRate, enableCrossSiteIsolation, getStudioRuntimeConfig, }: {
|
|
6
6
|
staticHash: string;
|
|
7
7
|
staticHashPrefix: string;
|
|
8
8
|
outputHash: string;
|
|
@@ -25,4 +25,5 @@ export declare const handleRoutes: ({ staticHash, staticHashPrefix, outputHash,
|
|
|
25
25
|
audioLatencyHint: AudioContextLatencyCategory | null;
|
|
26
26
|
previewSampleRate: number | null;
|
|
27
27
|
enableCrossSiteIsolation: boolean;
|
|
28
|
+
getStudioRuntimeConfig: () => StudioRuntimeConfig;
|
|
28
29
|
}) => Promise<void>;
|
package/dist/routes.js
CHANGED
|
@@ -46,15 +46,19 @@ const get_file_source_1 = require("./helpers/get-file-source");
|
|
|
46
46
|
const get_installed_installable_packages_1 = require("./helpers/get-installed-installable-packages");
|
|
47
47
|
const resolve_output_path_1 = require("./helpers/resolve-output-path");
|
|
48
48
|
const api_routes_1 = require("./preview-server/api-routes");
|
|
49
|
+
const element_install_state_1 = require("./preview-server/element-install-state");
|
|
49
50
|
const get_package_manager_1 = require("./preview-server/get-package-manager");
|
|
50
51
|
const get_static_file_fallback_hint_1 = require("./preview-server/get-static-file-fallback-hint");
|
|
51
52
|
const handler_1 = require("./preview-server/handler");
|
|
53
|
+
const parse_body_1 = require("./preview-server/parse-body");
|
|
52
54
|
const public_folder_1 = require("./preview-server/public-folder");
|
|
53
55
|
const open_in_editor_1 = require("./preview-server/routes/open-in-editor");
|
|
54
56
|
const serve_static_1 = require("./preview-server/serve-static");
|
|
55
57
|
const validate_same_origin_1 = require("./preview-server/validate-same-origin");
|
|
56
58
|
const watch_ignore_next_change_1 = require("./preview-server/watch-ignore-next-change");
|
|
57
59
|
const loggedStaticFileHints = new Set();
|
|
60
|
+
const ELEMENT_INSTALL_FOCUS_MAX_AGE = 5 * 60 * 1000;
|
|
61
|
+
const ELEMENT_INSTALL_TARGET_RESPONSE_WAIT = 250;
|
|
58
62
|
const static404 = (response) => {
|
|
59
63
|
response.writeHead(404);
|
|
60
64
|
response.end('The static/ prefix has been changed, this URL is no longer valid.');
|
|
@@ -78,7 +82,148 @@ const handleRemotionConfig = (response, remotionRoot) => {
|
|
|
78
82
|
response.end(JSON.stringify(body));
|
|
79
83
|
return Promise.resolve();
|
|
80
84
|
};
|
|
81
|
-
const
|
|
85
|
+
const isAllowedElementInstallOrigin = (origin) => {
|
|
86
|
+
if (!origin) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
const url = new URL(origin);
|
|
91
|
+
return ((url.protocol === 'https:' &&
|
|
92
|
+
(url.hostname === 'remotion.dev' ||
|
|
93
|
+
url.hostname === 'www.remotion.dev')) ||
|
|
94
|
+
(url.protocol === 'http:' &&
|
|
95
|
+
(url.hostname === 'localhost' || url.hostname === '127.0.0.1')));
|
|
96
|
+
}
|
|
97
|
+
catch (_a) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
const setElementInstallCorsHeaders = ({ request, response, }) => {
|
|
102
|
+
const { origin } = request.headers;
|
|
103
|
+
if (isAllowedElementInstallOrigin(origin)) {
|
|
104
|
+
response.setHeader('Access-Control-Allow-Origin', origin);
|
|
105
|
+
response.setHeader('Vary', 'Origin');
|
|
106
|
+
response.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
107
|
+
response.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
108
|
+
response.setHeader('Access-Control-Max-Age', '600');
|
|
109
|
+
response.setHeader('Access-Control-Allow-Private-Network', 'true');
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
const handleElementInstallOptions = ({ request, response, }) => {
|
|
113
|
+
setElementInstallCorsHeaders({ request, response });
|
|
114
|
+
response.writeHead(isAllowedElementInstallOrigin(request.headers.origin) ? 204 : 403);
|
|
115
|
+
response.end();
|
|
116
|
+
return Promise.resolve();
|
|
117
|
+
};
|
|
118
|
+
const handleElementInstallTarget = ({ liveEventsServer, request, response, remotionRoot, gitSource, }) => {
|
|
119
|
+
if (!isAllowedElementInstallOrigin(request.headers.origin)) {
|
|
120
|
+
response.writeHead(403);
|
|
121
|
+
response.end(JSON.stringify({ success: false, reason: 'Origin not allowed' }));
|
|
122
|
+
return Promise.resolve();
|
|
123
|
+
}
|
|
124
|
+
const requestId = `${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
125
|
+
liveEventsServer.sendEventToClient({
|
|
126
|
+
type: 'request-element-install-target',
|
|
127
|
+
requestId,
|
|
128
|
+
});
|
|
129
|
+
return new Promise((resolve) => {
|
|
130
|
+
setTimeout(() => {
|
|
131
|
+
var _a, _b, _c, _d, _e;
|
|
132
|
+
const target = (0, element_install_state_1.getElementInstallTarget)(requestId);
|
|
133
|
+
const now = Date.now();
|
|
134
|
+
const targetIsLive = target !== null &&
|
|
135
|
+
now - target.updatedAt < element_install_state_1.ELEMENT_INSTALL_TARGET_MAX_AGE;
|
|
136
|
+
const host = (_a = request.headers.host) !== null && _a !== void 0 ? _a : null;
|
|
137
|
+
const port = (_b = host === null || host === void 0 ? void 0 : host.split(':').at(-1)) !== null && _b !== void 0 ? _b : null;
|
|
138
|
+
setElementInstallCorsHeaders({ request, response });
|
|
139
|
+
response.writeHead(200, { 'Content-Type': 'application/json' });
|
|
140
|
+
response.end(JSON.stringify({
|
|
141
|
+
type: 'remotion-studio',
|
|
142
|
+
projectName: (0, studio_shared_1.getProjectName)({
|
|
143
|
+
basename: node_path_1.default.basename,
|
|
144
|
+
gitSource,
|
|
145
|
+
resolvedRemotionRoot: remotionRoot,
|
|
146
|
+
}),
|
|
147
|
+
port: port === null ? null : Number(port),
|
|
148
|
+
lastFocusedAt: (_c = target === null || target === void 0 ? void 0 : target.lastFocusedAt) !== null && _c !== void 0 ? _c : null,
|
|
149
|
+
canInstall: target !== null && target.canInstall && targetIsLive,
|
|
150
|
+
activeCompositionId: (_d = target === null || target === void 0 ? void 0 : target.compositionId) !== null && _d !== void 0 ? _d : null,
|
|
151
|
+
readOnly: (_e = target === null || target === void 0 ? void 0 : target.readOnly) !== null && _e !== void 0 ? _e : false,
|
|
152
|
+
}));
|
|
153
|
+
resolve();
|
|
154
|
+
}, ELEMENT_INSTALL_TARGET_RESPONSE_WAIT);
|
|
155
|
+
});
|
|
156
|
+
};
|
|
157
|
+
const handleRequestElementInstall = async ({ liveEventsServer, request, response, }) => {
|
|
158
|
+
if (!isAllowedElementInstallOrigin(request.headers.origin)) {
|
|
159
|
+
response.writeHead(403);
|
|
160
|
+
response.end(JSON.stringify({ success: false, reason: 'Origin not allowed' }));
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
setElementInstallCorsHeaders({ request, response });
|
|
164
|
+
response.setHeader('Content-Type', 'application/json');
|
|
165
|
+
try {
|
|
166
|
+
const body = await (0, parse_body_1.parseRequestBody)(request);
|
|
167
|
+
const parsed = (0, studio_shared_1.parseElementDragData)(JSON.stringify({
|
|
168
|
+
type: 'remotion-element',
|
|
169
|
+
version: 1,
|
|
170
|
+
element: body.element,
|
|
171
|
+
}));
|
|
172
|
+
if (parsed === null) {
|
|
173
|
+
response.writeHead(400);
|
|
174
|
+
response.end(JSON.stringify({ success: false, reason: 'Invalid Element payload' }));
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const target = (0, element_install_state_1.getElementInstallTarget)(null);
|
|
178
|
+
const now = Date.now();
|
|
179
|
+
const targetIsLive = target !== null &&
|
|
180
|
+
now - target.updatedAt < element_install_state_1.ELEMENT_INSTALL_TARGET_MAX_AGE;
|
|
181
|
+
const targetWasRecentlyFocused = target !== null &&
|
|
182
|
+
target.lastFocusedAt !== null &&
|
|
183
|
+
now - target.lastFocusedAt < ELEMENT_INSTALL_FOCUS_MAX_AGE;
|
|
184
|
+
if (target === null ||
|
|
185
|
+
!target.canInstall ||
|
|
186
|
+
target.compositionFile === null ||
|
|
187
|
+
target.compositionId === null ||
|
|
188
|
+
!targetIsLive ||
|
|
189
|
+
!targetWasRecentlyFocused) {
|
|
190
|
+
response.writeHead(409);
|
|
191
|
+
response.end(JSON.stringify({
|
|
192
|
+
success: false,
|
|
193
|
+
reason: 'No focused writable Remotion Studio composition',
|
|
194
|
+
}));
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const installRequest = {
|
|
198
|
+
id: `${Date.now()}-${Math.random().toString(16).slice(2)}`,
|
|
199
|
+
clientId: target.clientId,
|
|
200
|
+
createdAt: Date.now(),
|
|
201
|
+
compositionFile: target.compositionFile,
|
|
202
|
+
compositionId: target.compositionId,
|
|
203
|
+
element: parsed.element,
|
|
204
|
+
position: null,
|
|
205
|
+
};
|
|
206
|
+
const delivered = liveEventsServer.sendEventToClientId(target.clientId, {
|
|
207
|
+
type: 'element-install-request',
|
|
208
|
+
request: installRequest,
|
|
209
|
+
});
|
|
210
|
+
if (delivered === false) {
|
|
211
|
+
response.writeHead(409);
|
|
212
|
+
response.end(JSON.stringify({
|
|
213
|
+
success: false,
|
|
214
|
+
reason: 'The selected Remotion Studio tab is no longer connected',
|
|
215
|
+
}));
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
response.writeHead(200);
|
|
219
|
+
response.end(JSON.stringify({ success: true, status: 'sent' }));
|
|
220
|
+
}
|
|
221
|
+
catch (err) {
|
|
222
|
+
response.writeHead(500);
|
|
223
|
+
response.end(JSON.stringify({ success: false, reason: err.message }));
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
const handleFallback = async ({ remotionRoot, hash, response, request, getCurrentInputProps, getEnvVariables, publicDir, getRenderQueue, getRenderDefaults, numberOfAudioTags, audioLatencyHint, previewSampleRate, gitSource, logLevel, enableCrossSiteIsolation, getStudioRuntimeConfig, }) => {
|
|
82
227
|
var _a;
|
|
83
228
|
const acceptsHtml = ((_a = request.headers.accept) !== null && _a !== void 0 ? _a : '').includes('text/html');
|
|
84
229
|
if (request.method === 'GET' && acceptsHtml) {
|
|
@@ -143,14 +288,16 @@ const handleFallback = async ({ remotionRoot, hash, response, request, getCurren
|
|
|
143
288
|
mode: 'dev',
|
|
144
289
|
audioLatencyHint: audioLatencyHint !== null && audioLatencyHint !== void 0 ? audioLatencyHint : 'playback',
|
|
145
290
|
sampleRate: previewSampleRate,
|
|
291
|
+
studioRuntimeConfig: getStudioRuntimeConfig(),
|
|
146
292
|
}));
|
|
147
293
|
};
|
|
148
|
-
const handleFileSource = async ({ method, remotionRoot, search, response, }) => {
|
|
294
|
+
const handleFileSource = async ({ method, remotionRoot, search, response, request, }) => {
|
|
149
295
|
if (method === 'OPTIONS') {
|
|
150
296
|
response.writeHead(200);
|
|
151
297
|
response.end();
|
|
152
298
|
return Promise.resolve();
|
|
153
299
|
}
|
|
300
|
+
(0, validate_same_origin_1.validateSameOrigin)(request);
|
|
154
301
|
if (!search.startsWith('?')) {
|
|
155
302
|
throw new Error('query must start with ?');
|
|
156
303
|
}
|
|
@@ -244,7 +391,7 @@ const handleBeep = (_, response) => {
|
|
|
244
391
|
readStream.pipe(response);
|
|
245
392
|
return Promise.resolve();
|
|
246
393
|
};
|
|
247
|
-
const handleRoutes = ({ staticHash, staticHashPrefix, outputHash, outputHashPrefix, request, response, liveEventsServer, getCurrentInputProps, getEnvVariables, remotionRoot, entryPoint, publicDir, logLevel, getRenderQueue, getRenderDefaults, numberOfAudioTags, queueMethods: methods, gitSource, binariesDirectory, audioLatencyHint, previewSampleRate, enableCrossSiteIsolation, }) => {
|
|
394
|
+
const handleRoutes = ({ staticHash, staticHashPrefix, outputHash, outputHashPrefix, request, response, liveEventsServer, getCurrentInputProps, getEnvVariables, remotionRoot, entryPoint, publicDir, logLevel, getRenderQueue, getRenderDefaults, numberOfAudioTags, queueMethods: methods, gitSource, binariesDirectory, audioLatencyHint, previewSampleRate, enableCrossSiteIsolation, getStudioRuntimeConfig, }) => {
|
|
248
395
|
const url = new URL(request.url, 'http://localhost');
|
|
249
396
|
if (url.pathname === '/api/file-source') {
|
|
250
397
|
return handleFileSource({
|
|
@@ -252,6 +399,7 @@ const handleRoutes = ({ staticHash, staticHashPrefix, outputHash, outputHashPref
|
|
|
252
399
|
search: url.search,
|
|
253
400
|
method: request.method,
|
|
254
401
|
response,
|
|
402
|
+
request,
|
|
255
403
|
});
|
|
256
404
|
}
|
|
257
405
|
if (url.pathname === `${staticHash}/api/add-asset`) {
|
|
@@ -270,6 +418,26 @@ const handleRoutes = ({ staticHash, staticHashPrefix, outputHash, outputHashPref
|
|
|
270
418
|
remotionRoot,
|
|
271
419
|
});
|
|
272
420
|
}
|
|
421
|
+
if (url.pathname === '/api/element-install-target' ||
|
|
422
|
+
url.pathname === '/api/request-element-install') {
|
|
423
|
+
if (request.method === 'OPTIONS') {
|
|
424
|
+
return handleElementInstallOptions({ request, response });
|
|
425
|
+
}
|
|
426
|
+
if (url.pathname === '/api/element-install-target') {
|
|
427
|
+
return handleElementInstallTarget({
|
|
428
|
+
liveEventsServer,
|
|
429
|
+
request,
|
|
430
|
+
response,
|
|
431
|
+
remotionRoot,
|
|
432
|
+
gitSource,
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
return handleRequestElementInstall({
|
|
436
|
+
liveEventsServer,
|
|
437
|
+
request,
|
|
438
|
+
response,
|
|
439
|
+
});
|
|
440
|
+
}
|
|
273
441
|
for (const [key, value] of Object.entries(api_routes_1.allApiRoutes)) {
|
|
274
442
|
if (url.pathname === key) {
|
|
275
443
|
return (0, handler_1.handleRequest)({
|
|
@@ -341,6 +509,7 @@ const handleRoutes = ({ staticHash, staticHashPrefix, outputHash, outputHashPref
|
|
|
341
509
|
audioLatencyHint,
|
|
342
510
|
previewSampleRate,
|
|
343
511
|
enableCrossSiteIsolation,
|
|
512
|
+
getStudioRuntimeConfig,
|
|
344
513
|
});
|
|
345
514
|
};
|
|
346
515
|
exports.handleRoutes = handleRoutes;
|
package/dist/start-studio.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { WebpackOverrideFn } from '@remotion/bundler';
|
|
2
|
-
import type { GitSource, RenderDefaults, RenderJob } from '@remotion/studio-shared';
|
|
2
|
+
import type { GitSource, RenderDefaults, RenderJob, StudioRuntimeConfig } from '@remotion/studio-shared';
|
|
3
3
|
import type { QueueMethods } from './preview-server/api-types';
|
|
4
4
|
export type StartStudioResult = {
|
|
5
5
|
type: 'restarted';
|
|
6
6
|
} | {
|
|
7
7
|
type: 'already-running';
|
|
8
8
|
};
|
|
9
|
-
export declare const startStudio: ({ browserArgs, browserFlag, shouldOpenBrowser, fullEntryPath, logLevel, getCurrentInputProps, getEnvVariables, desiredPort, maxTimelineTracks, remotionRoot, keyboardShortcutsEnabled, experimentalClientSideRenderingEnabled, relativePublicDir, webpackOverride, poll, getRenderDefaults, getRenderQueue, numberOfAudioTags, queueMethods, previewEntry, gitSource, bufferStateDelayInMilliseconds, binariesDirectory, forceIPv4, audioLatencyHint, previewSampleRate, enableCrossSiteIsolation, askAIEnabled, forceNew, rspack, }: {
|
|
9
|
+
export declare const startStudio: ({ browserArgs, browserFlag, shouldOpenBrowser, fullEntryPath, logLevel, getCurrentInputProps, getEnvVariables, desiredPort, maxTimelineTracks, remotionRoot, keyboardShortcutsEnabled, experimentalClientSideRenderingEnabled, relativePublicDir, webpackOverride, poll, getRenderDefaults, getRenderQueue, numberOfAudioTags, queueMethods, previewEntry, gitSource, bufferStateDelayInMilliseconds, binariesDirectory, forceIPv4, audioLatencyHint, previewSampleRate, enableCrossSiteIsolation, askAIEnabled, interactivityEnabled, forceNew, rspack, getStudioRuntimeConfig, }: {
|
|
10
10
|
browserArgs: string;
|
|
11
11
|
browserFlag: string;
|
|
12
12
|
logLevel: "error" | "info" | "trace" | "verbose" | "warn";
|
|
@@ -35,6 +35,8 @@ export declare const startStudio: ({ browserArgs, browserFlag, shouldOpenBrowser
|
|
|
35
35
|
binariesDirectory: string | null;
|
|
36
36
|
forceIPv4: boolean;
|
|
37
37
|
askAIEnabled: boolean;
|
|
38
|
+
interactivityEnabled: boolean;
|
|
38
39
|
forceNew: boolean;
|
|
39
40
|
rspack: boolean;
|
|
41
|
+
getStudioRuntimeConfig: () => StudioRuntimeConfig;
|
|
40
42
|
}) => Promise<StartStudioResult>;
|
package/dist/start-studio.js
CHANGED
|
@@ -19,7 +19,7 @@ const public_folder_1 = require("./preview-server/public-folder");
|
|
|
19
19
|
const start_server_1 = require("./preview-server/start-server");
|
|
20
20
|
const server_ready_1 = require("./server-ready");
|
|
21
21
|
const watch_root_file_1 = require("./watch-root-file");
|
|
22
|
-
const startStudio = async ({ browserArgs, browserFlag, shouldOpenBrowser, fullEntryPath, logLevel, getCurrentInputProps, getEnvVariables, desiredPort, maxTimelineTracks, remotionRoot, keyboardShortcutsEnabled, experimentalClientSideRenderingEnabled, relativePublicDir, webpackOverride, poll, getRenderDefaults, getRenderQueue, numberOfAudioTags, queueMethods, previewEntry, gitSource, bufferStateDelayInMilliseconds, binariesDirectory, forceIPv4, audioLatencyHint, previewSampleRate, enableCrossSiteIsolation, askAIEnabled, forceNew, rspack, }) => {
|
|
22
|
+
const startStudio = async ({ browserArgs, browserFlag, shouldOpenBrowser, fullEntryPath, logLevel, getCurrentInputProps, getEnvVariables, desiredPort, maxTimelineTracks, remotionRoot, keyboardShortcutsEnabled, experimentalClientSideRenderingEnabled, relativePublicDir, webpackOverride, poll, getRenderDefaults, getRenderQueue, numberOfAudioTags, queueMethods, previewEntry, gitSource, bufferStateDelayInMilliseconds, binariesDirectory, forceIPv4, audioLatencyHint, previewSampleRate, enableCrossSiteIsolation, askAIEnabled, interactivityEnabled, forceNew, rspack, getStudioRuntimeConfig, }) => {
|
|
23
23
|
try {
|
|
24
24
|
if (typeof Bun === 'undefined') {
|
|
25
25
|
process.title = 'node (npx remotion studio)';
|
|
@@ -93,8 +93,10 @@ const startStudio = async ({ browserArgs, browserFlag, shouldOpenBrowser, fullEn
|
|
|
93
93
|
previewSampleRate,
|
|
94
94
|
enableCrossSiteIsolation,
|
|
95
95
|
askAIEnabled,
|
|
96
|
+
interactivityEnabled,
|
|
96
97
|
forceNew,
|
|
97
98
|
rspack,
|
|
99
|
+
getStudioRuntimeConfig,
|
|
98
100
|
});
|
|
99
101
|
if (result.type === 'already-running') {
|
|
100
102
|
renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, `Already running on port ${result.port}.`);
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"url": "https://github.com/remotion-dev/remotion/tree/main/packages/studio-server"
|
|
4
4
|
},
|
|
5
5
|
"name": "@remotion/studio-server",
|
|
6
|
-
"version": "4.0.
|
|
6
|
+
"version": "4.0.489",
|
|
7
7
|
"description": "Run a Remotion Studio with a server backend",
|
|
8
8
|
"main": "dist",
|
|
9
9
|
"scripts": {
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
"@babel/parser": "7.24.1",
|
|
28
28
|
"semver": "7.5.3",
|
|
29
29
|
"prettier": "3.8.1",
|
|
30
|
-
"remotion": "4.0.
|
|
30
|
+
"remotion": "4.0.489",
|
|
31
31
|
"recast": "0.23.11",
|
|
32
|
-
"@remotion/bundler": "4.0.
|
|
33
|
-
"@remotion/renderer": "4.0.
|
|
34
|
-
"@remotion/studio-shared": "4.0.
|
|
32
|
+
"@remotion/bundler": "4.0.489",
|
|
33
|
+
"@remotion/renderer": "4.0.489",
|
|
34
|
+
"@remotion/studio-shared": "4.0.489",
|
|
35
35
|
"memfs": "3.4.3",
|
|
36
36
|
"open": "8.4.2"
|
|
37
37
|
},
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"ast-types": "0.16.1",
|
|
40
40
|
"react": "19.2.3",
|
|
41
41
|
"@types/semver": "7.5.3",
|
|
42
|
-
"@remotion/eslint-config-internal": "4.0.
|
|
42
|
+
"@remotion/eslint-config-internal": "4.0.489",
|
|
43
43
|
"eslint": "9.19.0",
|
|
44
44
|
"@types/node": "20.12.14",
|
|
45
45
|
"@typescript/native-preview": "7.0.0-dev.20260217.1"
|