opencode-nanobanana 0.1.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/.ralph-events.json +151 -0
- package/.ralph-last-branch +1 -0
- package/.ralph-monitor-state.json +7 -0
- package/.ralph-monitor.pid +1 -0
- package/.ralph-timing.json +26 -0
- package/README.md +708 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/platforms/android.d.ts +94 -0
- package/dist/platforms/android.d.ts.map +1 -0
- package/dist/platforms/android.js +123 -0
- package/dist/platforms/android.js.map +1 -0
- package/dist/platforms/ios.d.ts +51 -0
- package/dist/platforms/ios.d.ts.map +1 -0
- package/dist/platforms/ios.js +149 -0
- package/dist/platforms/ios.js.map +1 -0
- package/dist/platforms/macos.d.ts +33 -0
- package/dist/platforms/macos.d.ts.map +1 -0
- package/dist/platforms/macos.js +50 -0
- package/dist/platforms/macos.js.map +1 -0
- package/dist/platforms/watchos.d.ts +36 -0
- package/dist/platforms/watchos.d.ts.map +1 -0
- package/dist/platforms/watchos.js +113 -0
- package/dist/platforms/watchos.js.map +1 -0
- package/dist/platforms/web.d.ts +64 -0
- package/dist/platforms/web.d.ts.map +1 -0
- package/dist/platforms/web.js +96 -0
- package/dist/platforms/web.js.map +1 -0
- package/dist/providers/gemini.d.ts +41 -0
- package/dist/providers/gemini.d.ts.map +1 -0
- package/dist/providers/gemini.js +177 -0
- package/dist/providers/gemini.js.map +1 -0
- package/dist/tools/analyze/compare.d.ts +12 -0
- package/dist/tools/analyze/compare.d.ts.map +1 -0
- package/dist/tools/analyze/compare.js +83 -0
- package/dist/tools/analyze/compare.js.map +1 -0
- package/dist/tools/analyze/mockup.d.ts +12 -0
- package/dist/tools/analyze/mockup.d.ts.map +1 -0
- package/dist/tools/analyze/mockup.js +88 -0
- package/dist/tools/analyze/mockup.js.map +1 -0
- package/dist/tools/analyze/screenshot.d.ts +12 -0
- package/dist/tools/analyze/screenshot.d.ts.map +1 -0
- package/dist/tools/analyze/screenshot.js +61 -0
- package/dist/tools/analyze/screenshot.js.map +1 -0
- package/dist/tools/app-assets/app-icon.d.ts +9 -0
- package/dist/tools/app-assets/app-icon.d.ts.map +1 -0
- package/dist/tools/app-assets/app-icon.js +133 -0
- package/dist/tools/app-assets/app-icon.js.map +1 -0
- package/dist/tools/app-assets/device-mockup.d.ts +9 -0
- package/dist/tools/app-assets/device-mockup.d.ts.map +1 -0
- package/dist/tools/app-assets/device-mockup.js +139 -0
- package/dist/tools/app-assets/device-mockup.js.map +1 -0
- package/dist/tools/app-assets/launch-images.d.ts +3 -0
- package/dist/tools/app-assets/launch-images.d.ts.map +1 -0
- package/dist/tools/app-assets/launch-images.js +171 -0
- package/dist/tools/app-assets/launch-images.js.map +1 -0
- package/dist/tools/app-assets/resize-devices.d.ts +14 -0
- package/dist/tools/app-assets/resize-devices.d.ts.map +1 -0
- package/dist/tools/app-assets/resize-devices.js +296 -0
- package/dist/tools/app-assets/resize-devices.js.map +1 -0
- package/dist/tools/app-assets/screenshots.d.ts +14 -0
- package/dist/tools/app-assets/screenshots.d.ts.map +1 -0
- package/dist/tools/app-assets/screenshots.js +186 -0
- package/dist/tools/app-assets/screenshots.js.map +1 -0
- package/dist/tools/core/edit-image.d.ts +12 -0
- package/dist/tools/core/edit-image.d.ts.map +1 -0
- package/dist/tools/core/edit-image.js +102 -0
- package/dist/tools/core/edit-image.js.map +1 -0
- package/dist/tools/core/generate-image.d.ts +12 -0
- package/dist/tools/core/generate-image.d.ts.map +1 -0
- package/dist/tools/core/generate-image.js +96 -0
- package/dist/tools/core/generate-image.js.map +1 -0
- package/dist/tools/core/restore-image.d.ts +12 -0
- package/dist/tools/core/restore-image.d.ts.map +1 -0
- package/dist/tools/core/restore-image.js +104 -0
- package/dist/tools/core/restore-image.js.map +1 -0
- package/dist/tools/design/mockup-to-code.d.ts +3 -0
- package/dist/tools/design/mockup-to-code.d.ts.map +1 -0
- package/dist/tools/design/mockup-to-code.js +311 -0
- package/dist/tools/design/mockup-to-code.js.map +1 -0
- package/dist/tools/design/sketch-to-code.d.ts +3 -0
- package/dist/tools/design/sketch-to-code.d.ts.map +1 -0
- package/dist/tools/design/sketch-to-code.js +325 -0
- package/dist/tools/design/sketch-to-code.js.map +1 -0
- package/dist/tools/docs/architecture-diagram.d.ts +12 -0
- package/dist/tools/docs/architecture-diagram.d.ts.map +1 -0
- package/dist/tools/docs/architecture-diagram.js +179 -0
- package/dist/tools/docs/architecture-diagram.js.map +1 -0
- package/dist/tools/docs/readme-banner.d.ts +6 -0
- package/dist/tools/docs/readme-banner.d.ts.map +1 -0
- package/dist/tools/docs/readme-banner.js +108 -0
- package/dist/tools/docs/readme-banner.js.map +1 -0
- package/dist/tools/docs/sequence-diagram.d.ts +12 -0
- package/dist/tools/docs/sequence-diagram.d.ts.map +1 -0
- package/dist/tools/docs/sequence-diagram.js +161 -0
- package/dist/tools/docs/sequence-diagram.js.map +1 -0
- package/dist/tools/docs/social-preview.d.ts +11 -0
- package/dist/tools/docs/social-preview.d.ts.map +1 -0
- package/dist/tools/docs/social-preview.js +111 -0
- package/dist/tools/docs/social-preview.js.map +1 -0
- package/dist/tools/video/extend-video.d.ts +14 -0
- package/dist/tools/video/extend-video.d.ts.map +1 -0
- package/dist/tools/video/extend-video.js +39 -0
- package/dist/tools/video/extend-video.js.map +1 -0
- package/dist/tools/video/generate-video.d.ts +14 -0
- package/dist/tools/video/generate-video.d.ts.map +1 -0
- package/dist/tools/video/generate-video.js +39 -0
- package/dist/tools/video/generate-video.js.map +1 -0
- package/dist/tools/video/image-to-video.d.ts +15 -0
- package/dist/tools/video/image-to-video.d.ts.map +1 -0
- package/dist/tools/video/image-to-video.js +42 -0
- package/dist/tools/video/image-to-video.js.map +1 -0
- package/dist/tools/video/storyboard-video.d.ts +91 -0
- package/dist/tools/video/storyboard-video.d.ts.map +1 -0
- package/dist/tools/video/storyboard-video.js +230 -0
- package/dist/tools/video/storyboard-video.js.map +1 -0
- package/dist/utils/ffmpeg.d.ts +30 -0
- package/dist/utils/ffmpeg.d.ts.map +1 -0
- package/dist/utils/ffmpeg.js +205 -0
- package/dist/utils/ffmpeg.js.map +1 -0
- package/dist/utils/file-handler.d.ts +7 -0
- package/dist/utils/file-handler.d.ts.map +1 -0
- package/dist/utils/file-handler.js +10 -0
- package/dist/utils/file-handler.js.map +1 -0
- package/dist/utils/image-processing.d.ts +7 -0
- package/dist/utils/image-processing.d.ts.map +1 -0
- package/dist/utils/image-processing.js +10 -0
- package/dist/utils/image-processing.js.map +1 -0
- package/docs/PLUGIN-VERIFICATION.md +182 -0
- package/logs/notifications.jsonl +46 -0
- package/package.json +61 -0
- package/prd.json +216 -0
- package/progress.txt +145 -0
- package/ralph-report.html +297 -0
- package/src/index.ts +23 -0
- package/src/platforms/android/.gitkeep +0 -0
- package/src/platforms/ios/.gitkeep +0 -0
- package/src/platforms/web/.gitkeep +0 -0
- package/src/providers/.gitkeep +0 -0
- package/src/providers/gemini.ts +288 -0
- package/src/tools/core/.gitkeep +0 -0
- package/src/tools/platform/.gitkeep +0 -0
- package/src/tools/video/extend-video.ts +71 -0
- package/src/tools/video/generate-video.ts +70 -0
- package/src/tools/video/image-to-video.ts +76 -0
- package/src/tools/video/storyboard-video.ts +325 -0
- package/src/utils/.gitkeep +0 -0
- package/src/utils/ffmpeg.ts +266 -0
- package/src/utils/file-handler.ts +10 -0
- package/src/utils/image-processing.ts +10 -0
- package/templates/.gitkeep +0 -0
- package/test-analyze-screenshot.ts +50 -0
- package/test-app-icons.ts +55 -0
- package/test-cat-sunset.ts +30 -0
- package/test-full-plugin.ts +88 -0
- package/test-icon-gen.ts +30 -0
- package/test-output/test-edit.png +0 -0
- package/test-output/test-generate.png +0 -0
- package/test-output/test-video.mp4 +0 -0
- package/test-plugin-load.js +45 -0
- package/test-princess-emma-continue.ts +35 -0
- package/test-princess-emma-full.ts +38 -0
- package/test-princess-emma-short.ts +32 -0
- package/test-princess-emma-with-reference.ts +34 -0
- package/test-princess-emma.ts +38 -0
- package/test-product-ad.ts +66 -0
- package/test-ralph-droid.ts +30 -0
- package/test-social-preview.ts +61 -0
- package/test-veo31-live.ts +187 -0
- package/test-video-gen.ts +40 -0
- package/test-video-veo.ts +73 -0
- package/test-zurich-video.ts +64 -0
- package/tests/.gitkeep +0 -0
- package/tests/providers/gemini.test.ts +388 -0
- package/tests/utils/ffmpeg.test.ts +328 -0
- package/tests/video/storyboard.test.ts +469 -0
- package/tsconfig.json +25 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { GeminiProvider } from '../../providers/gemini.js';
|
|
2
|
+
import { readFile, writeFile } from 'fs/promises';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { tmpdir } from 'os';
|
|
5
|
+
export async function extendVideo(options) {
|
|
6
|
+
const startTime = Date.now();
|
|
7
|
+
const { videoPath, prompt, aspectRatio = '16:9', resolution = '720p', outputPath, apiKey, } = options;
|
|
8
|
+
console.log('š¬ Extending video...');
|
|
9
|
+
console.log(` Video: ${videoPath}`);
|
|
10
|
+
console.log(` Prompt: ${prompt}`);
|
|
11
|
+
console.log(` Resolution: ${resolution}`);
|
|
12
|
+
console.log(` Aspect Ratio: ${aspectRatio}`);
|
|
13
|
+
const provider = new GeminiProvider(apiKey);
|
|
14
|
+
try {
|
|
15
|
+
console.log(' Loading video...');
|
|
16
|
+
const videoBuffer = await readFile(videoPath);
|
|
17
|
+
console.log(' Generating extension...');
|
|
18
|
+
const result = await provider.extendVideo(videoBuffer, prompt, {
|
|
19
|
+
aspectRatio,
|
|
20
|
+
resolution,
|
|
21
|
+
});
|
|
22
|
+
const finalOutputPath = outputPath || join(tmpdir(), `extended-${Date.now()}.mp4`);
|
|
23
|
+
console.log(' Saving extended video...');
|
|
24
|
+
await writeFile(finalOutputPath, result.buffer);
|
|
25
|
+
const totalTime = Date.now() - startTime;
|
|
26
|
+
console.log(`⨠Video extension complete!`);
|
|
27
|
+
console.log(` Output: ${finalOutputPath}`);
|
|
28
|
+
console.log(` Generation time: ${(totalTime / 1000).toFixed(2)}s`);
|
|
29
|
+
return {
|
|
30
|
+
videoPath: finalOutputPath,
|
|
31
|
+
generationTime: totalTime,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
console.error('ā Video extension failed:', error);
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=extend-video.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extend-video.js","sourceRoot":"","sources":["../../../src/tools/video/extend-video.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AAgB5B,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,OAA2B;IAE3B,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAE7B,MAAM,EACJ,SAAS,EACT,MAAM,EACN,WAAW,GAAG,MAAM,EACpB,UAAU,GAAG,MAAM,EACnB,UAAU,EACV,MAAM,GACP,GAAG,OAAO,CAAC;IAEZ,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;IACrC,OAAO,CAAC,GAAG,CAAC,aAAa,SAAS,EAAE,CAAC,CAAC;IACtC,OAAO,CAAC,GAAG,CAAC,cAAc,MAAM,EAAE,CAAC,CAAC;IACpC,OAAO,CAAC,GAAG,CAAC,kBAAkB,UAAU,EAAE,CAAC,CAAC;IAC5C,OAAO,CAAC,GAAG,CAAC,oBAAoB,WAAW,EAAE,CAAC,CAAC;IAE/C,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAC,MAAM,CAAC,CAAC;IAE5C,IAAI,CAAC;QACH,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;QACnC,MAAM,WAAW,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC,CAAC;QAE9C,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;QAC1C,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE;YAC7D,WAAW;YACX,UAAU;SACX,CAAC,CAAC;QAEH,MAAM,eAAe,GAAG,UAAU,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE,YAAY,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAEnF,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;QAC3C,MAAM,SAAS,CAAC,eAAe,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;QAEhD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;QAEzC,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;QAC3C,OAAO,CAAC,GAAG,CAAC,cAAc,eAAe,EAAE,CAAC,CAAC;QAC7C,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAErE,OAAO;YACL,SAAS,EAAE,eAAe;YAC1B,cAAc,EAAE,SAAS;SAC1B,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,2BAA2B,EAAE,KAAK,CAAC,CAAC;QAClD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface GenerateVideoOptions {
|
|
2
|
+
prompt: string;
|
|
3
|
+
aspectRatio?: '16:9' | '9:16';
|
|
4
|
+
resolution?: '720p' | '1080p';
|
|
5
|
+
duration?: 4 | 6 | 8;
|
|
6
|
+
outputPath?: string;
|
|
7
|
+
apiKey: string;
|
|
8
|
+
}
|
|
9
|
+
export interface GenerateVideoResult {
|
|
10
|
+
videoPath: string;
|
|
11
|
+
generationTime: number;
|
|
12
|
+
}
|
|
13
|
+
export declare function generateVideo(options: GenerateVideoOptions): Promise<GenerateVideoResult>;
|
|
14
|
+
//# sourceMappingURL=generate-video.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-video.d.ts","sourceRoot":"","sources":["../../../src/tools/video/generate-video.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9B,QAAQ,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,wBAAsB,aAAa,CACjC,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,mBAAmB,CAAC,CAgD9B"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { GeminiProvider } from '../../providers/gemini.js';
|
|
2
|
+
import { writeFile } from 'fs/promises';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { tmpdir } from 'os';
|
|
5
|
+
export async function generateVideo(options) {
|
|
6
|
+
const startTime = Date.now();
|
|
7
|
+
const { prompt, aspectRatio = '16:9', resolution = '720p', duration = 8, outputPath, apiKey, } = options;
|
|
8
|
+
console.log('š¬ Generating video...');
|
|
9
|
+
console.log(` Prompt: ${prompt}`);
|
|
10
|
+
console.log(` Resolution: ${resolution}`);
|
|
11
|
+
console.log(` Duration: ${duration}s`);
|
|
12
|
+
console.log(` Aspect Ratio: ${aspectRatio}`);
|
|
13
|
+
console.log(` Audio: native (Veo 3.0)`);
|
|
14
|
+
const provider = new GeminiProvider(apiKey);
|
|
15
|
+
try {
|
|
16
|
+
console.log(' Generating with Veo 3.0...');
|
|
17
|
+
const result = await provider.generateVideo(prompt, {
|
|
18
|
+
aspectRatio,
|
|
19
|
+
resolution,
|
|
20
|
+
duration,
|
|
21
|
+
});
|
|
22
|
+
const finalOutputPath = outputPath || join(tmpdir(), `video-${Date.now()}.mp4`);
|
|
23
|
+
console.log(' Saving video...');
|
|
24
|
+
await writeFile(finalOutputPath, result.buffer);
|
|
25
|
+
const totalTime = Date.now() - startTime;
|
|
26
|
+
console.log(`⨠Video generation complete!`);
|
|
27
|
+
console.log(` Output: ${finalOutputPath}`);
|
|
28
|
+
console.log(` Generation time: ${(totalTime / 1000).toFixed(2)}s`);
|
|
29
|
+
return {
|
|
30
|
+
videoPath: finalOutputPath,
|
|
31
|
+
generationTime: totalTime,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
console.error('ā Video generation failed:', error);
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=generate-video.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-video.js","sourceRoot":"","sources":["../../../src/tools/video/generate-video.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AAgB5B,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,OAA6B;IAE7B,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAE7B,MAAM,EACJ,MAAM,EACN,WAAW,GAAG,MAAM,EACpB,UAAU,GAAG,MAAM,EACnB,QAAQ,GAAG,CAAC,EACZ,UAAU,EACV,MAAM,GACP,GAAG,OAAO,CAAC;IAEZ,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;IACtC,OAAO,CAAC,GAAG,CAAC,cAAc,MAAM,EAAE,CAAC,CAAC;IACpC,OAAO,CAAC,GAAG,CAAC,kBAAkB,UAAU,EAAE,CAAC,CAAC;IAC5C,OAAO,CAAC,GAAG,CAAC,gBAAgB,QAAQ,GAAG,CAAC,CAAC;IACzC,OAAO,CAAC,GAAG,CAAC,oBAAoB,WAAW,EAAE,CAAC,CAAC;IAC/C,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;IAE1C,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAC,MAAM,CAAC,CAAC;IAE5C,IAAI,CAAC;QACH,OAAO,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAC;QAC7C,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,aAAa,CAAC,MAAM,EAAE;YAClD,WAAW;YACX,UAAU;YACV,QAAQ;SACT,CAAC,CAAC;QAEH,MAAM,eAAe,GAAG,UAAU,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE,SAAS,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAEhF,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;QAClC,MAAM,SAAS,CAAC,eAAe,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;QAEhD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;QAEzC,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;QAC5C,OAAO,CAAC,GAAG,CAAC,cAAc,eAAe,EAAE,CAAC,CAAC;QAC7C,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAErE,OAAO;YACL,SAAS,EAAE,eAAe;YAC1B,cAAc,EAAE,SAAS;SAC1B,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,4BAA4B,EAAE,KAAK,CAAC,CAAC;QACnD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface ImageToVideoOptions {
|
|
2
|
+
imagePath: string;
|
|
3
|
+
prompt: string;
|
|
4
|
+
aspectRatio?: '16:9' | '9:16';
|
|
5
|
+
resolution?: '720p' | '1080p';
|
|
6
|
+
duration?: 4 | 6 | 8;
|
|
7
|
+
outputPath?: string;
|
|
8
|
+
apiKey: string;
|
|
9
|
+
}
|
|
10
|
+
export interface ImageToVideoResult {
|
|
11
|
+
videoPath: string;
|
|
12
|
+
generationTime: number;
|
|
13
|
+
}
|
|
14
|
+
export declare function imageToVideo(options: ImageToVideoOptions): Promise<ImageToVideoResult>;
|
|
15
|
+
//# sourceMappingURL=image-to-video.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-to-video.d.ts","sourceRoot":"","sources":["../../../src/tools/video/image-to-video.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9B,QAAQ,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,wBAAsB,YAAY,CAChC,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,kBAAkB,CAAC,CAqD7B"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { GeminiProvider } from '../../providers/gemini.js';
|
|
2
|
+
import { readFile, writeFile } from 'fs/promises';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { tmpdir } from 'os';
|
|
5
|
+
export async function imageToVideo(options) {
|
|
6
|
+
const startTime = Date.now();
|
|
7
|
+
const { imagePath, prompt, aspectRatio = '16:9', resolution = '720p', duration = 8, outputPath, apiKey, } = options;
|
|
8
|
+
console.log('š¬ Animating image to video...');
|
|
9
|
+
console.log(` Image: ${imagePath}`);
|
|
10
|
+
console.log(` Prompt: ${prompt}`);
|
|
11
|
+
console.log(` Resolution: ${resolution}`);
|
|
12
|
+
console.log(` Duration: ${duration}s`);
|
|
13
|
+
console.log(` Aspect Ratio: ${aspectRatio}`);
|
|
14
|
+
console.log(` Audio: native (Veo 3.0)`);
|
|
15
|
+
const provider = new GeminiProvider(apiKey);
|
|
16
|
+
try {
|
|
17
|
+
console.log(' Loading image...');
|
|
18
|
+
const imageBuffer = await readFile(imagePath);
|
|
19
|
+
console.log(' Animating with Veo 3.0...');
|
|
20
|
+
const result = await provider.animateImage(imageBuffer, prompt, {
|
|
21
|
+
aspectRatio,
|
|
22
|
+
resolution,
|
|
23
|
+
duration,
|
|
24
|
+
});
|
|
25
|
+
const finalOutputPath = outputPath || join(tmpdir(), `animated-${Date.now()}.mp4`);
|
|
26
|
+
console.log(' Saving video...');
|
|
27
|
+
await writeFile(finalOutputPath, result.buffer);
|
|
28
|
+
const totalTime = Date.now() - startTime;
|
|
29
|
+
console.log(`⨠Image animation complete!`);
|
|
30
|
+
console.log(` Output: ${finalOutputPath}`);
|
|
31
|
+
console.log(` Generation time: ${(totalTime / 1000).toFixed(2)}s`);
|
|
32
|
+
return {
|
|
33
|
+
videoPath: finalOutputPath,
|
|
34
|
+
generationTime: totalTime,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
console.error('ā Image animation failed:', error);
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=image-to-video.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-to-video.js","sourceRoot":"","sources":["../../../src/tools/video/image-to-video.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AAiB5B,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,OAA4B;IAE5B,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAE7B,MAAM,EACJ,SAAS,EACT,MAAM,EACN,WAAW,GAAG,MAAM,EACpB,UAAU,GAAG,MAAM,EACnB,QAAQ,GAAG,CAAC,EACZ,UAAU,EACV,MAAM,GACP,GAAG,OAAO,CAAC;IAEZ,OAAO,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC;IAC9C,OAAO,CAAC,GAAG,CAAC,aAAa,SAAS,EAAE,CAAC,CAAC;IACtC,OAAO,CAAC,GAAG,CAAC,cAAc,MAAM,EAAE,CAAC,CAAC;IACpC,OAAO,CAAC,GAAG,CAAC,kBAAkB,UAAU,EAAE,CAAC,CAAC;IAC5C,OAAO,CAAC,GAAG,CAAC,gBAAgB,QAAQ,GAAG,CAAC,CAAC;IACzC,OAAO,CAAC,GAAG,CAAC,oBAAoB,WAAW,EAAE,CAAC,CAAC;IAC/C,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;IAE1C,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAC,MAAM,CAAC,CAAC;IAE5C,IAAI,CAAC;QACH,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;QACnC,MAAM,WAAW,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC,CAAC;QAE9C,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;QAC5C,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE;YAC9D,WAAW;YACX,UAAU;YACV,QAAQ;SACT,CAAC,CAAC;QAEH,MAAM,eAAe,GAAG,UAAU,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE,YAAY,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAEnF,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;QAClC,MAAM,SAAS,CAAC,eAAe,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;QAEhD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;QAEzC,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;QAC3C,OAAO,CAAC,GAAG,CAAC,cAAc,eAAe,EAAE,CAAC,CAAC;QAC7C,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAErE,OAAO;YACL,SAAS,EAAE,eAAe;YAC1B,cAAc,EAAE,SAAS;SAC1B,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,2BAA2B,EAAE,KAAK,CAAC,CAAC;QAClD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Storyboard Video Generation Tool
|
|
3
|
+
*
|
|
4
|
+
* Generates a multi-scene video by creating individual scenes in parallel
|
|
5
|
+
* and stitching them together with transitions using FFmpeg.
|
|
6
|
+
*/
|
|
7
|
+
export interface StoryboardVideoOptions {
|
|
8
|
+
scenes: string[];
|
|
9
|
+
style?: string;
|
|
10
|
+
characterDescription?: string;
|
|
11
|
+
referenceImages?: string[];
|
|
12
|
+
aspectRatio?: '16:9' | '9:16';
|
|
13
|
+
transition?: 'cut' | 'crossfade' | 'fade';
|
|
14
|
+
transitionDuration?: number;
|
|
15
|
+
backgroundMusic?: string;
|
|
16
|
+
musicVolume?: number;
|
|
17
|
+
outputPath?: string;
|
|
18
|
+
apiKey: string;
|
|
19
|
+
}
|
|
20
|
+
export interface StoryboardVideoResult {
|
|
21
|
+
/** Path to the final stitched video */
|
|
22
|
+
videoPath: string;
|
|
23
|
+
/** Total generation time in milliseconds */
|
|
24
|
+
totalTime: number;
|
|
25
|
+
/** Per-scene generation times */
|
|
26
|
+
sceneTimes: Array<{
|
|
27
|
+
scene: number;
|
|
28
|
+
time: number;
|
|
29
|
+
}>;
|
|
30
|
+
/** Number of scenes successfully generated */
|
|
31
|
+
successCount: number;
|
|
32
|
+
/** Number of scenes that failed */
|
|
33
|
+
failureCount: number;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Generate a multi-scene storyboard video
|
|
37
|
+
*
|
|
38
|
+
* This function generates multiple video scenes in parallel and stitches them
|
|
39
|
+
* together with transitions to create a cohesive video narrative.
|
|
40
|
+
*
|
|
41
|
+
* @param options - Configuration options for the storyboard video
|
|
42
|
+
* @returns Result object with video path and timing information
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* Basic usage:
|
|
46
|
+
* ```typescript
|
|
47
|
+
* const result = await generateStoryboardVideo({
|
|
48
|
+
* apiKey: 'your-api-key',
|
|
49
|
+
* scenes: [
|
|
50
|
+
* 'A serene mountain landscape at sunrise',
|
|
51
|
+
* 'A hiker reaching the summit',
|
|
52
|
+
* 'Panoramic view from the peak'
|
|
53
|
+
* ],
|
|
54
|
+
* style: 'cinematic',
|
|
55
|
+
* transition: 'crossfade',
|
|
56
|
+
* outputPath: './mountain-journey.mp4'
|
|
57
|
+
* });
|
|
58
|
+
* ```
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* With character consistency:
|
|
62
|
+
* ```typescript
|
|
63
|
+
* const result = await generateStoryboardVideo({
|
|
64
|
+
* apiKey: 'your-api-key',
|
|
65
|
+
* characterDescription: 'A young woman with long brown hair wearing a red jacket',
|
|
66
|
+
* referenceImages: ['./character-ref.jpg'],
|
|
67
|
+
* scenes: [
|
|
68
|
+
* 'Walking through a forest',
|
|
69
|
+
* 'Discovering a hidden waterfall',
|
|
70
|
+
* 'Setting up camp at sunset'
|
|
71
|
+
* ],
|
|
72
|
+
* style: 'cinematic',
|
|
73
|
+
* transition: 'crossfade'
|
|
74
|
+
* });
|
|
75
|
+
* ```
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* With background music:
|
|
79
|
+
* ```typescript
|
|
80
|
+
* const result = await generateStoryboardVideo({
|
|
81
|
+
* apiKey: 'your-api-key',
|
|
82
|
+
* scenes: ['Scene 1', 'Scene 2', 'Scene 3'],
|
|
83
|
+
* generateAudio: true, // Native Veo audio (default)
|
|
84
|
+
* backgroundMusic: './music.mp3', // Add background music
|
|
85
|
+
* musicVolume: 0.3, // 30% volume for background music
|
|
86
|
+
* transition: 'crossfade'
|
|
87
|
+
* });
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
export declare function generateStoryboardVideo(options: StoryboardVideoOptions): Promise<StoryboardVideoResult>;
|
|
91
|
+
//# sourceMappingURL=storyboard-video.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storyboard-video.d.ts","sourceRoot":"","sources":["../../../src/tools/video/storyboard-video.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAaH,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC9B,UAAU,CAAC,EAAE,KAAK,GAAG,WAAW,GAAG,MAAM,CAAC;IAC1C,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,qBAAqB;IACpC,uCAAuC;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,4CAA4C;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,iCAAiC;IACjC,UAAU,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnD,8CAA8C;IAC9C,YAAY,EAAE,MAAM,CAAC;IACrB,mCAAmC;IACnC,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,qBAAqB,CAAC,CA8NhC"}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Storyboard Video Generation Tool
|
|
3
|
+
*
|
|
4
|
+
* Generates a multi-scene video by creating individual scenes in parallel
|
|
5
|
+
* and stitching them together with transitions using FFmpeg.
|
|
6
|
+
*/
|
|
7
|
+
import { GeminiProvider } from '../../providers/gemini.js';
|
|
8
|
+
import { checkFfmpegInstalled, concatenateVideos, addAudioTrack, } from '../../utils/ffmpeg.js';
|
|
9
|
+
import { writeFile, unlink, readFile } from 'fs/promises';
|
|
10
|
+
import { join } from 'path';
|
|
11
|
+
import { tmpdir } from 'os';
|
|
12
|
+
/**
|
|
13
|
+
* Generate a multi-scene storyboard video
|
|
14
|
+
*
|
|
15
|
+
* This function generates multiple video scenes in parallel and stitches them
|
|
16
|
+
* together with transitions to create a cohesive video narrative.
|
|
17
|
+
*
|
|
18
|
+
* @param options - Configuration options for the storyboard video
|
|
19
|
+
* @returns Result object with video path and timing information
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* Basic usage:
|
|
23
|
+
* ```typescript
|
|
24
|
+
* const result = await generateStoryboardVideo({
|
|
25
|
+
* apiKey: 'your-api-key',
|
|
26
|
+
* scenes: [
|
|
27
|
+
* 'A serene mountain landscape at sunrise',
|
|
28
|
+
* 'A hiker reaching the summit',
|
|
29
|
+
* 'Panoramic view from the peak'
|
|
30
|
+
* ],
|
|
31
|
+
* style: 'cinematic',
|
|
32
|
+
* transition: 'crossfade',
|
|
33
|
+
* outputPath: './mountain-journey.mp4'
|
|
34
|
+
* });
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* With character consistency:
|
|
39
|
+
* ```typescript
|
|
40
|
+
* const result = await generateStoryboardVideo({
|
|
41
|
+
* apiKey: 'your-api-key',
|
|
42
|
+
* characterDescription: 'A young woman with long brown hair wearing a red jacket',
|
|
43
|
+
* referenceImages: ['./character-ref.jpg'],
|
|
44
|
+
* scenes: [
|
|
45
|
+
* 'Walking through a forest',
|
|
46
|
+
* 'Discovering a hidden waterfall',
|
|
47
|
+
* 'Setting up camp at sunset'
|
|
48
|
+
* ],
|
|
49
|
+
* style: 'cinematic',
|
|
50
|
+
* transition: 'crossfade'
|
|
51
|
+
* });
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* With background music:
|
|
56
|
+
* ```typescript
|
|
57
|
+
* const result = await generateStoryboardVideo({
|
|
58
|
+
* apiKey: 'your-api-key',
|
|
59
|
+
* scenes: ['Scene 1', 'Scene 2', 'Scene 3'],
|
|
60
|
+
* generateAudio: true, // Native Veo audio (default)
|
|
61
|
+
* backgroundMusic: './music.mp3', // Add background music
|
|
62
|
+
* musicVolume: 0.3, // 30% volume for background music
|
|
63
|
+
* transition: 'crossfade'
|
|
64
|
+
* });
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export async function generateStoryboardVideo(options) {
|
|
68
|
+
const startTime = Date.now();
|
|
69
|
+
// Validate inputs
|
|
70
|
+
if (!options.scenes || options.scenes.length === 0) {
|
|
71
|
+
throw new Error('At least one scene is required');
|
|
72
|
+
}
|
|
73
|
+
// Check FFmpeg availability
|
|
74
|
+
const ffmpegAvailable = await checkFfmpegInstalled();
|
|
75
|
+
if (!ffmpegAvailable) {
|
|
76
|
+
throw new Error('FFmpeg is not installed or not available in PATH. Please install FFmpeg to use this tool.');
|
|
77
|
+
}
|
|
78
|
+
// Initialize provider
|
|
79
|
+
const provider = new GeminiProvider(options.apiKey);
|
|
80
|
+
const { scenes, style, characterDescription, referenceImages, aspectRatio = '16:9', transition = 'crossfade', transitionDuration = 0.5, backgroundMusic, musicVolume = 0.3, outputPath = join(tmpdir(), `storyboard-${Date.now()}.mp4`), } = options;
|
|
81
|
+
// Load reference images if provided
|
|
82
|
+
let loadedReferences;
|
|
83
|
+
if (referenceImages && referenceImages.length > 0) {
|
|
84
|
+
if (referenceImages.length > 3) {
|
|
85
|
+
throw new Error('Maximum of 3 reference images allowed');
|
|
86
|
+
}
|
|
87
|
+
console.log(`šø Loading ${referenceImages.length} reference image(s)...`);
|
|
88
|
+
loadedReferences = await Promise.all(referenceImages.map(async (imagePath, index) => {
|
|
89
|
+
const buffer = await readFile(imagePath);
|
|
90
|
+
return {
|
|
91
|
+
buffer,
|
|
92
|
+
description: `Reference image ${index + 1} for character/scene consistency`,
|
|
93
|
+
};
|
|
94
|
+
}));
|
|
95
|
+
}
|
|
96
|
+
console.log(`\nš¬ Starting storyboard generation with ${scenes.length} scene(s)`);
|
|
97
|
+
console.log(`āļø Configuration:`);
|
|
98
|
+
console.log(` - Aspect ratio: ${aspectRatio}`);
|
|
99
|
+
console.log(` - Transition: ${transition} (${transitionDuration}s)`);
|
|
100
|
+
console.log(` - Audio: native (Veo 3.0)${backgroundMusic ? ' + background music' : ''}`);
|
|
101
|
+
if (characterDescription) {
|
|
102
|
+
console.log(`š¤ Using character description: "${characterDescription}"`);
|
|
103
|
+
}
|
|
104
|
+
if (loadedReferences) {
|
|
105
|
+
console.log(`š¼ļø Using ${loadedReferences.length} reference image(s) for consistency`);
|
|
106
|
+
}
|
|
107
|
+
console.log(`\nš¹ Generating scenes sequentially (to avoid rate limits)...`);
|
|
108
|
+
const sceneResults = [];
|
|
109
|
+
for (let index = 0; index < scenes.length; index++) {
|
|
110
|
+
const sceneDescription = scenes[index];
|
|
111
|
+
const sceneStartTime = Date.now();
|
|
112
|
+
console.log(` [${index + 1}/${scenes.length}] Generating: "${sceneDescription.slice(0, 50)}${sceneDescription.length > 50 ? '...' : ''}"`);
|
|
113
|
+
try {
|
|
114
|
+
let prompt = sceneDescription;
|
|
115
|
+
if (characterDescription) {
|
|
116
|
+
prompt = `${characterDescription}. ${prompt}`;
|
|
117
|
+
}
|
|
118
|
+
if (style) {
|
|
119
|
+
prompt = `${style} style: ${prompt}`;
|
|
120
|
+
}
|
|
121
|
+
let result;
|
|
122
|
+
if (loadedReferences && loadedReferences.length > 0) {
|
|
123
|
+
result = await provider.generateVideoWithReferences(prompt, loadedReferences, {
|
|
124
|
+
aspectRatio,
|
|
125
|
+
resolution: '720p',
|
|
126
|
+
duration: 8,
|
|
127
|
+
numberOfVideos: 1,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
result = await provider.generateVideo(prompt, {
|
|
132
|
+
aspectRatio,
|
|
133
|
+
resolution: '720p',
|
|
134
|
+
duration: 8,
|
|
135
|
+
numberOfVideos: 1,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
const tempPath = join(tmpdir(), `scene-${index}-${Date.now()}.mp4`);
|
|
139
|
+
await writeFile(tempPath, result.buffer);
|
|
140
|
+
const sceneTime = Date.now() - sceneStartTime;
|
|
141
|
+
console.log(` ā
[${index + 1}/${scenes.length}] Completed in ${(sceneTime / 1000).toFixed(1)}s`);
|
|
142
|
+
sceneResults.push({
|
|
143
|
+
index,
|
|
144
|
+
path: tempPath,
|
|
145
|
+
time: sceneTime,
|
|
146
|
+
success: true,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
const sceneTime = Date.now() - sceneStartTime;
|
|
151
|
+
console.error(` ā [${index + 1}/${scenes.length}] Failed after ${(sceneTime / 1000).toFixed(1)}s:`, error);
|
|
152
|
+
sceneResults.push({
|
|
153
|
+
index,
|
|
154
|
+
path: null,
|
|
155
|
+
time: sceneTime,
|
|
156
|
+
success: false,
|
|
157
|
+
error: error instanceof Error ? error.message : String(error),
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// Filter successful scenes
|
|
162
|
+
const successfulScenes = sceneResults.filter((result) => result.success && result.path !== null);
|
|
163
|
+
if (successfulScenes.length === 0) {
|
|
164
|
+
throw new Error('All scenes failed to generate. No video to stitch.');
|
|
165
|
+
}
|
|
166
|
+
if (successfulScenes.length < sceneResults.length) {
|
|
167
|
+
console.warn(`ā ļø ${sceneResults.length - successfulScenes.length} scene(s) failed. Continuing with ${successfulScenes.length} successful scene(s).`);
|
|
168
|
+
}
|
|
169
|
+
// Sort by original index to maintain scene order
|
|
170
|
+
successfulScenes.sort((a, b) => a.index - b.index);
|
|
171
|
+
const videoPaths = successfulScenes.map((s) => s.path);
|
|
172
|
+
// Stitch videos together
|
|
173
|
+
console.log(`\nšļø Stitching ${videoPaths.length} scene(s) together...`);
|
|
174
|
+
console.log(` - Transition: ${transition}`);
|
|
175
|
+
console.log(` - Duration: ${transitionDuration}s`);
|
|
176
|
+
const concatenateOptions = {
|
|
177
|
+
transition,
|
|
178
|
+
transitionDuration,
|
|
179
|
+
};
|
|
180
|
+
// Determine final output path based on whether we need to add background music
|
|
181
|
+
const stitchedVideoPath = backgroundMusic
|
|
182
|
+
? join(tmpdir(), `stitched-${Date.now()}.mp4`)
|
|
183
|
+
: outputPath;
|
|
184
|
+
try {
|
|
185
|
+
await concatenateVideos(videoPaths, stitchedVideoPath, concatenateOptions);
|
|
186
|
+
// Add background music if provided
|
|
187
|
+
if (backgroundMusic) {
|
|
188
|
+
console.log(`\nšµ Adding background music...`);
|
|
189
|
+
console.log(` - Volume: ${(musicVolume * 100).toFixed(0)}%`);
|
|
190
|
+
await addAudioTrack(stitchedVideoPath, backgroundMusic, outputPath, musicVolume);
|
|
191
|
+
console.log(` ā
Audio mixing complete`);
|
|
192
|
+
// Clean up temporary stitched video
|
|
193
|
+
await unlink(stitchedVideoPath).catch(() => { });
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
finally {
|
|
197
|
+
// Clean up temporary scene files
|
|
198
|
+
for (const videoPath of videoPaths) {
|
|
199
|
+
try {
|
|
200
|
+
await unlink(videoPath);
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
// Ignore cleanup errors
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
const totalTime = Date.now() - startTime;
|
|
208
|
+
// Calculate average scene generation time
|
|
209
|
+
const avgSceneTime = successfulScenes.length > 0
|
|
210
|
+
? successfulScenes.reduce((sum, s) => sum + s.time, 0) / successfulScenes.length
|
|
211
|
+
: 0;
|
|
212
|
+
console.log(`\n⨠Storyboard generation complete!`);
|
|
213
|
+
console.log(`š Summary:`);
|
|
214
|
+
console.log(` - Total time: ${(totalTime / 1000).toFixed(1)}s`);
|
|
215
|
+
console.log(` - Scenes generated: ${successfulScenes.length}/${sceneResults.length}`);
|
|
216
|
+
console.log(` - Average scene time: ${(avgSceneTime / 1000).toFixed(1)}s`);
|
|
217
|
+
console.log(` - Success rate: ${((successfulScenes.length / sceneResults.length) * 100).toFixed(0)}%`);
|
|
218
|
+
console.log(`š Output: ${outputPath}\n`);
|
|
219
|
+
return {
|
|
220
|
+
videoPath: outputPath,
|
|
221
|
+
totalTime,
|
|
222
|
+
sceneTimes: sceneResults.map((r) => ({
|
|
223
|
+
scene: r.index + 1,
|
|
224
|
+
time: r.time,
|
|
225
|
+
})),
|
|
226
|
+
successCount: successfulScenes.length,
|
|
227
|
+
failureCount: sceneResults.length - successfulScenes.length,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
//# sourceMappingURL=storyboard-video.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storyboard-video.js","sourceRoot":"","sources":["../../../src/tools/video/storyboard-video.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FFmpeg Utility Module
|
|
3
|
+
*
|
|
4
|
+
* Provides video processing operations using FFmpeg
|
|
5
|
+
*/
|
|
6
|
+
export interface ConcatenateOptions {
|
|
7
|
+
transition?: 'cut' | 'crossfade' | 'fade';
|
|
8
|
+
transitionDuration?: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Check if FFmpeg is installed and available
|
|
12
|
+
*/
|
|
13
|
+
export declare function checkFfmpegInstalled(): Promise<boolean>;
|
|
14
|
+
/**
|
|
15
|
+
* Get the duration of a video in seconds
|
|
16
|
+
*/
|
|
17
|
+
export declare function getVideoDuration(videoPath: string): Promise<number>;
|
|
18
|
+
/**
|
|
19
|
+
* Trim a video from startTime for the specified duration
|
|
20
|
+
*/
|
|
21
|
+
export declare function trimVideo(videoPath: string, startTime: number, duration: number, outputPath: string): Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* Add an audio track to a video
|
|
24
|
+
*/
|
|
25
|
+
export declare function addAudioTrack(videoPath: string, audioPath: string, outputPath: string, volume?: number): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Concatenate multiple videos with transitions
|
|
28
|
+
*/
|
|
29
|
+
export declare function concatenateVideos(videoPaths: string[], outputPath: string, options?: ConcatenateOptions): Promise<void>;
|
|
30
|
+
//# sourceMappingURL=ffmpeg.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ffmpeg.d.ts","sourceRoot":"","sources":["../../src/utils/ffmpeg.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,EAAE,KAAK,GAAG,WAAW,GAAG,MAAM,CAAC;IAC1C,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;GAEG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,OAAO,CAAC,CAO7D;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAiBzE;AAED;;GAEG;AACH,wBAAsB,SAAS,CAC7B,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAgBf;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,MAAM,GAAE,MAAY,GACnB,OAAO,CAAC,IAAI,CAAC,CAqBf;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,EAAE,EACpB,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,IAAI,CAAC,CA8Bf"}
|