@vibeframe/mcp-server 0.33.1 → 0.35.0
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/index.js +231 -28
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -62,6 +62,7 @@ __export(remotion_exports, {
|
|
|
62
62
|
ensureRemotionInstalled: () => ensureRemotionInstalled,
|
|
63
63
|
generateAnimatedCaptionComponent: () => generateAnimatedCaptionComponent,
|
|
64
64
|
generateCaptionComponent: () => generateCaptionComponent,
|
|
65
|
+
generateTextOverlayComponent: () => generateTextOverlayComponent,
|
|
65
66
|
renderAndComposite: () => renderAndComposite,
|
|
66
67
|
renderMotion: () => renderMotion,
|
|
67
68
|
renderWithEmbeddedImage: () => renderWithEmbeddedImage,
|
|
@@ -75,15 +76,19 @@ import { writeFile as writeFile4, mkdir as mkdir2, rm, copyFile } from "node:fs/
|
|
|
75
76
|
import { existsSync } from "node:fs";
|
|
76
77
|
import { join } from "node:path";
|
|
77
78
|
import { tmpdir } from "node:os";
|
|
79
|
+
import { createHash } from "node:crypto";
|
|
78
80
|
async function ensureRemotionInstalled() {
|
|
79
81
|
try {
|
|
80
|
-
await execSafe("npx", ["remotion", "--help"], { timeout:
|
|
82
|
+
await execSafe("npx", ["--yes", "remotion", "--help"], { timeout: 6e4 });
|
|
81
83
|
return null;
|
|
82
|
-
} catch {
|
|
84
|
+
} catch (error) {
|
|
85
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
86
|
+
console.error(`[Remotion] ensureRemotionInstalled failed: ${detail.slice(0, 300)}`);
|
|
83
87
|
return [
|
|
84
|
-
"Remotion CLI not found
|
|
85
|
-
|
|
86
|
-
"
|
|
88
|
+
"Remotion CLI not found or failed to initialize.",
|
|
89
|
+
` Debug: ${detail.slice(0, 200)}`,
|
|
90
|
+
" Fix: npm install -g @remotion/cli",
|
|
91
|
+
" Or ensure npx is available and can download @remotion/cli on demand."
|
|
87
92
|
].join("\n");
|
|
88
93
|
}
|
|
89
94
|
}
|
|
@@ -91,14 +96,14 @@ async function scaffoldRemotionProject(componentCode, componentName, opts) {
|
|
|
91
96
|
const dir = join(tmpdir(), `vibe_motion_${Date.now()}`);
|
|
92
97
|
await mkdir2(dir, { recursive: true });
|
|
93
98
|
const deps = {
|
|
94
|
-
remotion:
|
|
95
|
-
"@remotion/cli":
|
|
99
|
+
remotion: REMOTION_VERSION,
|
|
100
|
+
"@remotion/cli": REMOTION_VERSION,
|
|
96
101
|
react: "^18.0.0",
|
|
97
102
|
"react-dom": "^18.0.0",
|
|
98
103
|
"@types/react": "^18.0.0"
|
|
99
104
|
};
|
|
100
105
|
if (opts.useMediaPackage) {
|
|
101
|
-
deps["@remotion/media"] =
|
|
106
|
+
deps["@remotion/media"] = REMOTION_VERSION;
|
|
102
107
|
}
|
|
103
108
|
const packageJson = {
|
|
104
109
|
name: "vibe-motion-render",
|
|
@@ -106,7 +111,8 @@ async function scaffoldRemotionProject(componentCode, componentName, opts) {
|
|
|
106
111
|
private: true,
|
|
107
112
|
dependencies: deps
|
|
108
113
|
};
|
|
109
|
-
|
|
114
|
+
const packageJsonStr = JSON.stringify(packageJson, null, 2);
|
|
115
|
+
await writeFile4(join(dir, "package.json"), packageJsonStr);
|
|
110
116
|
const tsconfig = {
|
|
111
117
|
compilerOptions: {
|
|
112
118
|
target: "ES2020",
|
|
@@ -139,16 +145,48 @@ const Root = () => {
|
|
|
139
145
|
registerRoot(Root);
|
|
140
146
|
`;
|
|
141
147
|
await writeFile4(join(dir, "Root.tsx"), rootCode);
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
148
|
+
const depsHash = createHash("md5").update(packageJsonStr).digest("hex").slice(0, 12);
|
|
149
|
+
const cacheMarker = join(REMOTION_CACHE_DIR, `.deps-${depsHash}`);
|
|
150
|
+
const cachedModules = join(REMOTION_CACHE_DIR, "node_modules");
|
|
151
|
+
if (existsSync(cacheMarker) && existsSync(cachedModules)) {
|
|
152
|
+
const { symlink } = await import("node:fs/promises");
|
|
153
|
+
try {
|
|
154
|
+
await symlink(cachedModules, join(dir, "node_modules"), "dir");
|
|
155
|
+
} catch {
|
|
156
|
+
await this_npmInstall(dir);
|
|
157
|
+
}
|
|
158
|
+
} else {
|
|
159
|
+
await this_npmInstall(dir);
|
|
160
|
+
try {
|
|
161
|
+
await mkdir2(REMOTION_CACHE_DIR, { recursive: true });
|
|
162
|
+
await writeFile4(join(REMOTION_CACHE_DIR, "package.json"), packageJsonStr);
|
|
163
|
+
if (existsSync(join(dir, "node_modules"))) {
|
|
164
|
+
const { rename: rename3, symlink } = await import("node:fs/promises");
|
|
165
|
+
await rename3(join(dir, "node_modules"), cachedModules).catch(() => {
|
|
166
|
+
});
|
|
167
|
+
await symlink(cachedModules, join(dir, "node_modules"), "dir").catch(() => {
|
|
168
|
+
});
|
|
169
|
+
await writeFile4(cacheMarker, depsHash);
|
|
170
|
+
}
|
|
171
|
+
} catch {
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return dir;
|
|
175
|
+
}
|
|
176
|
+
async function this_npmInstall(dir) {
|
|
177
|
+
const { execFile: execFile2 } = await import("node:child_process");
|
|
178
|
+
const { promisify: promisify2 } = await import("node:util");
|
|
179
|
+
const execFileAsync2 = promisify2(execFile2);
|
|
180
|
+
try {
|
|
146
181
|
await execFileAsync2("npm", ["install", "--prefer-offline", "--no-audit", "--no-fund"], {
|
|
147
182
|
cwd: dir,
|
|
148
183
|
timeout: 18e4
|
|
149
184
|
});
|
|
185
|
+
} catch (error) {
|
|
186
|
+
const msg = error instanceof Error ? error.stderr || error.message : String(error);
|
|
187
|
+
console.error(`[Remotion] npm install failed: ${msg.slice(0, 300)}`);
|
|
188
|
+
throw error;
|
|
150
189
|
}
|
|
151
|
-
return dir;
|
|
152
190
|
}
|
|
153
191
|
async function renderMotion(options) {
|
|
154
192
|
const transparent = options.transparent !== false;
|
|
@@ -220,7 +258,12 @@ async function renderMotion(options) {
|
|
|
220
258
|
], { cwd: dir, timeout: 3e5 });
|
|
221
259
|
return { success: true, outputPath: mp4Out };
|
|
222
260
|
} catch (error) {
|
|
261
|
+
const errObj = error;
|
|
262
|
+
const stderr = errObj.stderr?.slice(0, 500) || "";
|
|
223
263
|
const msg = error instanceof Error ? error.message : String(error);
|
|
264
|
+
console.error(`[Remotion] Render failed:
|
|
265
|
+
${msg.slice(0, 300)}${stderr ? `
|
|
266
|
+
stderr: ${stderr}` : ""}`);
|
|
224
267
|
return { success: false, error: `Remotion render failed: ${msg}` };
|
|
225
268
|
} finally {
|
|
226
269
|
await rm(dir, { recursive: true, force: true }).catch(() => {
|
|
@@ -403,6 +446,98 @@ async function renderWithEmbeddedVideo(options) {
|
|
|
403
446
|
});
|
|
404
447
|
}
|
|
405
448
|
}
|
|
449
|
+
function generateTextOverlayComponent(options) {
|
|
450
|
+
const { texts, style, fontSize, fontColor, startTime, endTime, fadeDuration, width, height, videoFileName } = options;
|
|
451
|
+
const name = "TextOverlay";
|
|
452
|
+
const textsJSON = JSON.stringify(texts);
|
|
453
|
+
const styleMap = {
|
|
454
|
+
"lower-third": {
|
|
455
|
+
justify: "flex-end",
|
|
456
|
+
align: "flex-start",
|
|
457
|
+
padding: `paddingBottom: ${Math.round(height * 0.12)}, paddingLeft: ${Math.round(width * 0.05)},`,
|
|
458
|
+
extraCss: `backgroundColor: "rgba(0,0,0,0.5)", padding: "8px 20px", borderRadius: 4,`
|
|
459
|
+
},
|
|
460
|
+
"center-bold": {
|
|
461
|
+
justify: "center",
|
|
462
|
+
align: "center",
|
|
463
|
+
padding: "",
|
|
464
|
+
extraCss: `fontWeight: "bold" as const, textShadow: "3px 3px 6px rgba(0,0,0,0.9)",`
|
|
465
|
+
},
|
|
466
|
+
"subtitle": {
|
|
467
|
+
justify: "flex-end",
|
|
468
|
+
align: "center",
|
|
469
|
+
padding: `paddingBottom: ${Math.round(height * 0.08)},`,
|
|
470
|
+
extraCss: `backgroundColor: "rgba(0,0,0,0.6)", padding: "6px 16px", borderRadius: 4,`
|
|
471
|
+
},
|
|
472
|
+
"minimal": {
|
|
473
|
+
justify: "flex-start",
|
|
474
|
+
align: "flex-start",
|
|
475
|
+
padding: `paddingTop: ${Math.round(height * 0.05)}, paddingLeft: ${Math.round(width * 0.05)},`,
|
|
476
|
+
extraCss: `opacity: 0.85,`
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
const s = styleMap[style];
|
|
480
|
+
const scaledFontSize = style === "center-bold" ? Math.round(fontSize * 1.5) : fontSize;
|
|
481
|
+
const code = `import { AbsoluteFill, useCurrentFrame, useVideoConfig, interpolate, staticFile } from "remotion";
|
|
482
|
+
import { Video } from "@remotion/media";
|
|
483
|
+
|
|
484
|
+
const texts: string[] = ${textsJSON};
|
|
485
|
+
const START_TIME = ${startTime};
|
|
486
|
+
const END_TIME = ${endTime};
|
|
487
|
+
const FADE_DURATION = ${fadeDuration};
|
|
488
|
+
|
|
489
|
+
export const ${name} = () => {
|
|
490
|
+
const frame = useCurrentFrame();
|
|
491
|
+
const { fps } = useVideoConfig();
|
|
492
|
+
const currentTime = frame / fps;
|
|
493
|
+
|
|
494
|
+
const visible = currentTime >= START_TIME && currentTime <= END_TIME;
|
|
495
|
+
|
|
496
|
+
const opacity = visible
|
|
497
|
+
? interpolate(
|
|
498
|
+
currentTime,
|
|
499
|
+
[START_TIME, START_TIME + FADE_DURATION, END_TIME - FADE_DURATION, END_TIME],
|
|
500
|
+
[0, 1, 1, 0],
|
|
501
|
+
{ extrapolateLeft: "clamp", extrapolateRight: "clamp" }
|
|
502
|
+
)
|
|
503
|
+
: 0;
|
|
504
|
+
|
|
505
|
+
return (
|
|
506
|
+
<AbsoluteFill>
|
|
507
|
+
<Video src={staticFile("${videoFileName}")} style={{ width: "100%", height: "100%" }} muted />
|
|
508
|
+
{visible && (
|
|
509
|
+
<AbsoluteFill
|
|
510
|
+
style={{
|
|
511
|
+
display: "flex",
|
|
512
|
+
flexDirection: "column",
|
|
513
|
+
justifyContent: "${s.justify}",
|
|
514
|
+
alignItems: "${s.align}",
|
|
515
|
+
${s.padding}
|
|
516
|
+
opacity,
|
|
517
|
+
}}
|
|
518
|
+
>
|
|
519
|
+
<div
|
|
520
|
+
style={{
|
|
521
|
+
fontSize: ${scaledFontSize},
|
|
522
|
+
fontFamily: "Arial, Helvetica, sans-serif",
|
|
523
|
+
color: "${fontColor}",
|
|
524
|
+
lineHeight: 1.4,
|
|
525
|
+
maxWidth: "${Math.round(width * 0.9)}px",
|
|
526
|
+
${s.extraCss}
|
|
527
|
+
}}
|
|
528
|
+
>
|
|
529
|
+
{texts.map((text, i) => (
|
|
530
|
+
<div key={i}>{text}</div>
|
|
531
|
+
))}
|
|
532
|
+
</div>
|
|
533
|
+
</AbsoluteFill>
|
|
534
|
+
)}
|
|
535
|
+
</AbsoluteFill>
|
|
536
|
+
);
|
|
537
|
+
};
|
|
538
|
+
`;
|
|
539
|
+
return { code, name };
|
|
540
|
+
}
|
|
406
541
|
function generateCaptionComponent(options) {
|
|
407
542
|
const { segments, style, fontSize, fontColor, position, width, videoFileName } = options;
|
|
408
543
|
const name = videoFileName ? "VideoCaptioned" : "CaptionOverlay";
|
|
@@ -762,10 +897,13 @@ async function renderAndComposite(motionOpts, baseVideo, finalOutput) {
|
|
|
762
897
|
});
|
|
763
898
|
return compositeResult;
|
|
764
899
|
}
|
|
900
|
+
var REMOTION_VERSION, REMOTION_CACHE_DIR;
|
|
765
901
|
var init_remotion = __esm({
|
|
766
902
|
"../cli/src/utils/remotion.ts"() {
|
|
767
903
|
"use strict";
|
|
768
904
|
init_exec_safe();
|
|
905
|
+
REMOTION_VERSION = "4.0.447";
|
|
906
|
+
REMOTION_CACHE_DIR = join(tmpdir(), "vibe_remotion_cache");
|
|
769
907
|
}
|
|
770
908
|
});
|
|
771
909
|
|
|
@@ -2736,11 +2874,14 @@ IMPORTANT GUIDELINES:
|
|
|
2736
2874
|
- ALWAYS include the character description when the person appears
|
|
2737
2875
|
|
|
2738
2876
|
3. NARRATION LENGTH (CRITICAL for audio-video sync):
|
|
2739
|
-
-
|
|
2740
|
-
-
|
|
2741
|
-
-
|
|
2877
|
+
- Average TTS speaking rate is ~2.5 words/second
|
|
2878
|
+
- For duration=5: narration MUST be 10-12 words (4-5 seconds of speech)
|
|
2879
|
+
- For duration=10: narration MUST be 20-24 words (8-10 seconds of speech)
|
|
2880
|
+
- NEVER exceed the word limit \u2014 count every word before finalizing
|
|
2881
|
+
- ALWAYS leave 0.5-1s buffer (narration should be slightly shorter than video)
|
|
2742
2882
|
- If the script has a long paragraph, break it into 2-3 shorter scenes rather than one long narration
|
|
2743
2883
|
- This prevents freeze frames where video stops but narration continues
|
|
2884
|
+
- VALIDATION: After writing each narration, count the words. If over limit, shorten it.
|
|
2744
2885
|
|
|
2745
2886
|
4. NARRATION-VISUAL ALIGNMENT: The narration must directly describe what's visible:
|
|
2746
2887
|
- When narration mentions something specific, the visual must show it
|
|
@@ -6650,7 +6791,7 @@ var openaiImageProvider = new OpenAIImageProvider();
|
|
|
6650
6791
|
|
|
6651
6792
|
// ../ai-providers/dist/runway/RunwayProvider.js
|
|
6652
6793
|
var DEFAULT_MODEL2 = "gen4.5";
|
|
6653
|
-
var RunwayProvider = class {
|
|
6794
|
+
var RunwayProvider = class _RunwayProvider {
|
|
6654
6795
|
constructor() {
|
|
6655
6796
|
this.id = "runway";
|
|
6656
6797
|
this.name = "Runway";
|
|
@@ -6715,12 +6856,12 @@ var RunwayProvider = class {
|
|
|
6715
6856
|
if (options?.seed !== void 0) {
|
|
6716
6857
|
body.seed = options.seed;
|
|
6717
6858
|
}
|
|
6718
|
-
const response = await
|
|
6859
|
+
const response = await this.fetchWithRetry(`${this.baseUrl}/${endpoint}`, {
|
|
6719
6860
|
method: "POST",
|
|
6720
6861
|
headers: {
|
|
6721
6862
|
Authorization: `Bearer ${this.apiKey}`,
|
|
6722
6863
|
"Content-Type": "application/json",
|
|
6723
|
-
"X-Runway-Version":
|
|
6864
|
+
"X-Runway-Version": _RunwayProvider.API_VERSION
|
|
6724
6865
|
},
|
|
6725
6866
|
body: JSON.stringify(body)
|
|
6726
6867
|
});
|
|
@@ -6733,6 +6874,9 @@ var RunwayProvider = class {
|
|
|
6733
6874
|
} catch {
|
|
6734
6875
|
errorMessage = errorText;
|
|
6735
6876
|
}
|
|
6877
|
+
console.error(`[Runway] POST /${endpoint} -> ${response.status} ${response.statusText}
|
|
6878
|
+
Model: ${model}, Ratio: ${apiRatio}, Duration: ${body.duration}
|
|
6879
|
+
Response: ${errorText.slice(0, 500)}`);
|
|
6736
6880
|
return {
|
|
6737
6881
|
id: "",
|
|
6738
6882
|
status: "failed",
|
|
@@ -6781,7 +6925,7 @@ var RunwayProvider = class {
|
|
|
6781
6925
|
const response = await fetch(`${this.baseUrl}/tasks/${id}`, {
|
|
6782
6926
|
headers: {
|
|
6783
6927
|
Authorization: `Bearer ${this.apiKey}`,
|
|
6784
|
-
"X-Runway-Version":
|
|
6928
|
+
"X-Runway-Version": _RunwayProvider.API_VERSION
|
|
6785
6929
|
}
|
|
6786
6930
|
});
|
|
6787
6931
|
if (!response.ok) {
|
|
@@ -6833,7 +6977,7 @@ var RunwayProvider = class {
|
|
|
6833
6977
|
method: "POST",
|
|
6834
6978
|
headers: {
|
|
6835
6979
|
Authorization: `Bearer ${this.apiKey}`,
|
|
6836
|
-
"X-Runway-Version":
|
|
6980
|
+
"X-Runway-Version": _RunwayProvider.API_VERSION
|
|
6837
6981
|
}
|
|
6838
6982
|
});
|
|
6839
6983
|
return response.ok;
|
|
@@ -6853,7 +6997,7 @@ var RunwayProvider = class {
|
|
|
6853
6997
|
method: "DELETE",
|
|
6854
6998
|
headers: {
|
|
6855
6999
|
Authorization: `Bearer ${this.apiKey}`,
|
|
6856
|
-
"X-Runway-Version":
|
|
7000
|
+
"X-Runway-Version": _RunwayProvider.API_VERSION
|
|
6857
7001
|
}
|
|
6858
7002
|
});
|
|
6859
7003
|
return response.ok;
|
|
@@ -6882,6 +7026,24 @@ var RunwayProvider = class {
|
|
|
6882
7026
|
error: "Generation timed out"
|
|
6883
7027
|
};
|
|
6884
7028
|
}
|
|
7029
|
+
/**
|
|
7030
|
+
* Fetch with retry for transient errors (429, 503)
|
|
7031
|
+
*/
|
|
7032
|
+
async fetchWithRetry(url, init) {
|
|
7033
|
+
let lastResponse;
|
|
7034
|
+
for (let attempt = 0; attempt < _RunwayProvider.MAX_RETRIES; attempt++) {
|
|
7035
|
+
const response = await fetch(url, init);
|
|
7036
|
+
if (response.status !== 429 && response.status !== 503) {
|
|
7037
|
+
return response;
|
|
7038
|
+
}
|
|
7039
|
+
lastResponse = response;
|
|
7040
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
7041
|
+
const delayMs = retryAfter ? parseInt(retryAfter, 10) * 1e3 : 2 ** (attempt + 1) * 1e3;
|
|
7042
|
+
console.error(`[Runway] ${response.status} \u2014 retrying in ${delayMs / 1e3}s (attempt ${attempt + 1}/${_RunwayProvider.MAX_RETRIES})`);
|
|
7043
|
+
await this.sleep(delayMs);
|
|
7044
|
+
}
|
|
7045
|
+
return lastResponse;
|
|
7046
|
+
}
|
|
6885
7047
|
/**
|
|
6886
7048
|
* Clamp duration to valid range for the given model
|
|
6887
7049
|
*/
|
|
@@ -6908,6 +7070,8 @@ var RunwayProvider = class {
|
|
|
6908
7070
|
return new Promise((resolve13) => setTimeout(resolve13, ms));
|
|
6909
7071
|
}
|
|
6910
7072
|
};
|
|
7073
|
+
RunwayProvider.API_VERSION = "2024-11-06";
|
|
7074
|
+
RunwayProvider.MAX_RETRIES = 3;
|
|
6911
7075
|
var runwayProvider = new RunwayProvider();
|
|
6912
7076
|
|
|
6913
7077
|
// ../ai-providers/dist/kling/KlingProvider.js
|
|
@@ -9415,9 +9579,17 @@ async function applyTextOverlays(options) {
|
|
|
9415
9579
|
if (!commandExists("ffmpeg")) {
|
|
9416
9580
|
return { success: false, error: "FFmpeg not found. Please install FFmpeg." };
|
|
9417
9581
|
}
|
|
9582
|
+
let hasDrawtext = true;
|
|
9418
9583
|
try {
|
|
9419
9584
|
const { stdout } = await execSafe("ffmpeg", ["-filters"]);
|
|
9420
|
-
|
|
9585
|
+
hasDrawtext = stdout.includes("drawtext");
|
|
9586
|
+
} catch {
|
|
9587
|
+
}
|
|
9588
|
+
if (!hasDrawtext) {
|
|
9589
|
+
console.log("FFmpeg missing drawtext filter (libfreetype) \u2014 using Remotion fallback...");
|
|
9590
|
+
const { generateTextOverlayComponent: generateTextOverlayComponent2, renderWithEmbeddedVideo: renderWithEmbeddedVideo2, ensureRemotionInstalled: ensureRemotionInstalled2 } = await Promise.resolve().then(() => (init_remotion(), remotion_exports));
|
|
9591
|
+
const remotionErr = await ensureRemotionInstalled2();
|
|
9592
|
+
if (remotionErr) {
|
|
9421
9593
|
const platform = process.platform;
|
|
9422
9594
|
let hint = "";
|
|
9423
9595
|
if (platform === "darwin") {
|
|
@@ -9427,10 +9599,41 @@ async function applyTextOverlays(options) {
|
|
|
9427
9599
|
}
|
|
9428
9600
|
return {
|
|
9429
9601
|
success: false,
|
|
9430
|
-
error: `FFmpeg 'drawtext' filter not available
|
|
9431
|
-
|
|
9432
|
-
|
|
9433
|
-
|
|
9602
|
+
error: `FFmpeg 'drawtext' filter not available and Remotion fallback unavailable.
|
|
9603
|
+
${remotionErr}${hint}`
|
|
9604
|
+
};
|
|
9605
|
+
}
|
|
9606
|
+
const { width: width2, height: height2 } = await getVideoResolution(absVideoPath);
|
|
9607
|
+
const videoDuration2 = await getVideoDuration(absVideoPath);
|
|
9608
|
+
const baseFontSize2 = customFontSize || Math.round(height2 / 20);
|
|
9609
|
+
const endTime2 = options.endTime ?? videoDuration2;
|
|
9610
|
+
const fps = 30;
|
|
9611
|
+
const durationInFrames = Math.ceil(videoDuration2 * fps);
|
|
9612
|
+
const videoFileName = "source_video.mp4";
|
|
9613
|
+
const { code, name } = generateTextOverlayComponent2({
|
|
9614
|
+
texts,
|
|
9615
|
+
style,
|
|
9616
|
+
fontSize: baseFontSize2,
|
|
9617
|
+
fontColor,
|
|
9618
|
+
startTime,
|
|
9619
|
+
endTime: endTime2,
|
|
9620
|
+
fadeDuration,
|
|
9621
|
+
width: width2,
|
|
9622
|
+
height: height2,
|
|
9623
|
+
videoFileName
|
|
9624
|
+
});
|
|
9625
|
+
const renderResult = await renderWithEmbeddedVideo2({
|
|
9626
|
+
componentCode: code,
|
|
9627
|
+
componentName: name,
|
|
9628
|
+
width: width2,
|
|
9629
|
+
height: height2,
|
|
9630
|
+
fps,
|
|
9631
|
+
durationInFrames,
|
|
9632
|
+
videoPath: absVideoPath,
|
|
9633
|
+
videoFileName,
|
|
9634
|
+
outputPath: absOutputPath
|
|
9635
|
+
});
|
|
9636
|
+
return renderResult.success ? { success: true, outputPath: renderResult.outputPath || absOutputPath } : { success: false, error: renderResult.error || "Remotion render failed" };
|
|
9434
9637
|
}
|
|
9435
9638
|
const { width, height } = await getVideoResolution(absVideoPath);
|
|
9436
9639
|
const baseFontSize = customFontSize || Math.round(height / 20);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibeframe/mcp-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.35.0",
|
|
4
4
|
"description": "VibeFrame MCP Server - AI-native video editing via Model Context Protocol",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -57,8 +57,8 @@
|
|
|
57
57
|
"tsx": "^4.21.0",
|
|
58
58
|
"typescript": "^5.3.3",
|
|
59
59
|
"vitest": "^1.2.2",
|
|
60
|
-
"@vibeframe/
|
|
61
|
-
"@vibeframe/
|
|
60
|
+
"@vibeframe/core": "0.35.0",
|
|
61
|
+
"@vibeframe/cli": "0.35.0"
|
|
62
62
|
},
|
|
63
63
|
"engines": {
|
|
64
64
|
"node": ">=20"
|