@farazirfan/costar-server-executor 1.7.37 → 1.7.39
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/agent/agent.d.ts +90 -0
- package/dist/agent/agent.d.ts.map +1 -1
- package/dist/agent/agent.js +606 -0
- package/dist/agent/agent.js.map +1 -1
- package/dist/agent/pi-embedded-runner/run.d.ts.map +1 -1
- package/dist/agent/pi-embedded-runner/run.js +2 -1
- package/dist/agent/pi-embedded-runner/run.js.map +1 -1
- package/dist/agent/pi-embedded-runner/system-prompt.d.ts.map +1 -1
- package/dist/agent/pi-embedded-runner/system-prompt.js +16 -37
- package/dist/agent/pi-embedded-runner/system-prompt.js.map +1 -1
- package/dist/agent/pi-embedded-runner/tools.d.ts +4 -1
- package/dist/agent/pi-embedded-runner/tools.d.ts.map +1 -1
- package/dist/agent/pi-embedded-runner/tools.js +3 -1
- package/dist/agent/pi-embedded-runner/tools.js.map +1 -1
- package/dist/agent/pi-embedded-runner/types.d.ts +4 -0
- package/dist/agent/pi-embedded-runner/types.d.ts.map +1 -1
- package/dist/cli/env-loader.d.ts.map +1 -1
- package/dist/cli/env-loader.js +1 -0
- package/dist/cli/env-loader.js.map +1 -1
- package/dist/cli/setup.js +2 -2
- package/dist/cli/setup.js.map +1 -1
- package/dist/cron/normalize.d.ts +31 -0
- package/dist/cron/normalize.d.ts.map +1 -0
- package/dist/cron/normalize.js +211 -0
- package/dist/cron/normalize.js.map +1 -0
- package/dist/cron/scheduler.d.ts +33 -3
- package/dist/cron/scheduler.d.ts.map +1 -1
- package/dist/cron/scheduler.js +253 -48
- package/dist/cron/scheduler.js.map +1 -1
- package/dist/heartbeat/runner.d.ts +27 -12
- package/dist/heartbeat/runner.d.ts.map +1 -1
- package/dist/heartbeat/runner.js +82 -104
- package/dist/heartbeat/runner.js.map +1 -1
- package/dist/infra/heartbeat-events-filter.d.ts +29 -0
- package/dist/infra/heartbeat-events-filter.d.ts.map +1 -0
- package/dist/infra/heartbeat-events-filter.js +80 -0
- package/dist/infra/heartbeat-events-filter.js.map +1 -0
- package/dist/infra/index.d.ts +9 -0
- package/dist/infra/index.d.ts.map +1 -0
- package/dist/infra/index.js +9 -0
- package/dist/infra/index.js.map +1 -0
- package/dist/infra/system-events.d.ts +58 -2
- package/dist/infra/system-events.d.ts.map +1 -1
- package/dist/infra/system-events.js +80 -14
- package/dist/infra/system-events.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +6 -1
- package/dist/server.js.map +1 -1
- package/dist/services/platform-keys.d.ts +19 -0
- package/dist/services/platform-keys.d.ts.map +1 -0
- package/dist/services/platform-keys.js +74 -0
- package/dist/services/platform-keys.js.map +1 -0
- package/dist/subagent/registry.d.ts +96 -0
- package/dist/subagent/registry.d.ts.map +1 -0
- package/dist/subagent/registry.js +180 -0
- package/dist/subagent/registry.js.map +1 -0
- package/dist/tools/complete-turn.d.ts +2 -2
- package/dist/tools/complete-turn.js +10 -10
- package/dist/tools/complete-turn.js.map +1 -1
- package/dist/tools/contacts.d.ts +13 -0
- package/dist/tools/contacts.d.ts.map +1 -0
- package/dist/tools/contacts.js +80 -0
- package/dist/tools/contacts.js.map +1 -0
- package/dist/tools/cron.d.ts +17 -2
- package/dist/tools/cron.d.ts.map +1 -1
- package/dist/tools/cron.js +117 -35
- package/dist/tools/cron.js.map +1 -1
- package/dist/tools/google-maps.d.ts +6 -6
- package/dist/tools/google-maps.d.ts.map +1 -1
- package/dist/tools/google-maps.js +207 -262
- package/dist/tools/google-maps.js.map +1 -1
- package/dist/tools/index.d.ts +17 -7
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +40 -9
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/phone-call.d.ts +11 -0
- package/dist/tools/phone-call.d.ts.map +1 -0
- package/dist/tools/phone-call.js +151 -0
- package/dist/tools/phone-call.js.map +1 -0
- package/dist/tools/sessions-spawn.d.ts +33 -0
- package/dist/tools/sessions-spawn.d.ts.map +1 -0
- package/dist/tools/sessions-spawn.js +164 -0
- package/dist/tools/sessions-spawn.js.map +1 -0
- package/dist/tools/spotify.d.ts +12 -0
- package/dist/tools/spotify.d.ts.map +1 -0
- package/dist/tools/spotify.js +251 -0
- package/dist/tools/spotify.js.map +1 -0
- package/dist/tools/subagents.d.ts +23 -0
- package/dist/tools/subagents.d.ts.map +1 -0
- package/dist/tools/subagents.js +209 -0
- package/dist/tools/subagents.js.map +1 -0
- package/dist/tools/whatsapp.d.ts +13 -0
- package/dist/tools/whatsapp.d.ts.map +1 -0
- package/dist/tools/whatsapp.js +215 -0
- package/dist/tools/whatsapp.js.map +1 -0
- package/dist/tools/youtube.d.ts +12 -0
- package/dist/tools/youtube.d.ts.map +1 -0
- package/dist/tools/youtube.js +218 -0
- package/dist/tools/youtube.js.map +1 -0
- package/dist/utils/asterizk-auth.d.ts +43 -0
- package/dist/utils/asterizk-auth.d.ts.map +1 -0
- package/dist/utils/asterizk-auth.js +125 -0
- package/dist/utils/asterizk-auth.js.map +1 -0
- package/dist/web-server.d.ts.map +1 -1
- package/dist/web-server.js +132 -0
- package/dist/web-server.js.map +1 -1
- package/dist/workspace/index.d.ts +3 -4
- package/dist/workspace/index.d.ts.map +1 -1
- package/dist/workspace/index.js +3 -4
- package/dist/workspace/index.js.map +1 -1
- package/dist/workspace/templates.d.ts +8 -7
- package/dist/workspace/templates.d.ts.map +1 -1
- package/dist/workspace/templates.js +18 -127
- package/dist/workspace/templates.js.map +1 -1
- package/dist/workspace/workspace.d.ts +2 -4
- package/dist/workspace/workspace.d.ts.map +1 -1
- package/dist/workspace/workspace.js +7 -16
- package/dist/workspace/workspace.js.map +1 -1
- package/package.json +1 -1
- package/public/index.html +231 -0
- package/skills/docx/SKILL.md +468 -0
- package/skills/docx/scripts/__init__.py +1 -0
- package/skills/docx/scripts/accept_changes.py +181 -0
- package/skills/docx/scripts/comment.py +347 -0
- package/skills/docx/scripts/helpers/__init__.py +0 -0
- package/skills/docx/scripts/helpers/merge_runs.py +231 -0
- package/skills/docx/scripts/helpers/simplify_redlines.py +240 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- package/skills/docx/scripts/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- package/skills/docx/scripts/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- package/skills/docx/scripts/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- package/skills/docx/scripts/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- package/skills/docx/scripts/ooxml/schemas/mce/mc.xsd +75 -0
- package/skills/docx/scripts/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
- package/skills/docx/scripts/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
- package/skills/docx/scripts/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
- package/skills/docx/scripts/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
- package/skills/docx/scripts/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
- package/skills/docx/scripts/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- package/skills/docx/scripts/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
- package/skills/docx/scripts/ooxml/scripts/pack.py +159 -0
- package/skills/docx/scripts/ooxml/scripts/unpack.py +29 -0
- package/skills/docx/scripts/ooxml/scripts/validate.py +106 -0
- package/skills/docx/scripts/ooxml/scripts/validation/__init__.py +15 -0
- package/skills/docx/scripts/ooxml/scripts/validation/base.py +1023 -0
- package/skills/docx/scripts/ooxml/scripts/validation/docx.py +519 -0
- package/skills/docx/scripts/ooxml/scripts/validation/pptx.py +315 -0
- package/skills/docx/scripts/ooxml/scripts/validation/redlining.py +284 -0
- package/skills/docx/scripts/pack.py +166 -0
- package/skills/docx/scripts/templates/comments.xml +3 -0
- package/skills/docx/scripts/templates/commentsExtended.xml +3 -0
- package/skills/docx/scripts/templates/commentsExtensible.xml +3 -0
- package/skills/docx/scripts/templates/commentsIds.xml +3 -0
- package/skills/docx/scripts/templates/people.xml +3 -0
- package/skills/docx/scripts/unpack.py +134 -0
- package/skills/longform-video-generation/SKILL.md +298 -0
- package/skills/longform-video-generation/references/advanced_techniques.md +474 -0
- package/skills/longform-video-generation/references/google_api_guide.md +288 -0
- package/skills/longform-video-generation/scripts/video_generator.py +579 -0
- package/skills/pdf/FORMS.md +305 -0
- package/skills/pdf/REFERENCE.md +612 -0
- package/skills/pdf/SKILL.md +293 -0
- package/skills/pdf/scripts/check_bounding_boxes.py +70 -0
- package/skills/pdf/scripts/check_fillable_fields.py +12 -0
- package/skills/pdf/scripts/convert_pdf_to_images.py +35 -0
- package/skills/pdf/scripts/create_validation_image.py +41 -0
- package/skills/pdf/scripts/extract_form_field_info.py +152 -0
- package/skills/pdf/scripts/extract_form_structure.py +124 -0
- package/skills/pdf/scripts/fill_fillable_fields.py +116 -0
- package/skills/pdf/scripts/fill_pdf_form_with_annotations.py +136 -0
- package/skills/pptx/SKILL.md +171 -0
- package/skills/pptx/editing.md +205 -0
- package/skills/pptx/pptxgenjs.md +377 -0
- package/skills/pptx/scripts/add_slide.py +225 -0
- package/skills/pptx/scripts/clean.py +309 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- package/skills/pptx/scripts/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- package/skills/pptx/scripts/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- package/skills/pptx/scripts/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- package/skills/pptx/scripts/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- package/skills/pptx/scripts/ooxml/schemas/mce/mc.xsd +75 -0
- package/skills/pptx/scripts/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
- package/skills/pptx/scripts/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
- package/skills/pptx/scripts/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
- package/skills/pptx/scripts/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
- package/skills/pptx/scripts/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
- package/skills/pptx/scripts/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- package/skills/pptx/scripts/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
- package/skills/pptx/scripts/ooxml/scripts/pack.py +159 -0
- package/skills/pptx/scripts/ooxml/scripts/unpack.py +29 -0
- package/skills/pptx/scripts/ooxml/scripts/validate.py +106 -0
- package/skills/pptx/scripts/ooxml/scripts/validation/__init__.py +15 -0
- package/skills/pptx/scripts/ooxml/scripts/validation/base.py +1023 -0
- package/skills/pptx/scripts/ooxml/scripts/validation/docx.py +519 -0
- package/skills/pptx/scripts/ooxml/scripts/validation/pptx.py +315 -0
- package/skills/pptx/scripts/ooxml/scripts/validation/redlining.py +284 -0
- package/skills/pptx/scripts/pack.py +168 -0
- package/skills/pptx/scripts/thumbnail.py +318 -0
- package/skills/pptx/scripts/unpack.py +86 -0
- package/skills/xlsx/SKILL.md +291 -0
- package/skills/xlsx/recalc.py +247 -0
|
@@ -0,0 +1,579 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Long-Form Video Generator with Veo 3.1
|
|
4
|
+
Generate videos longer than 8 seconds using Google's Veo 3.1 API.
|
|
5
|
+
|
|
6
|
+
Outputs videos to /home/user/task directory (sandbox working directory).
|
|
7
|
+
Progress is logged to /home/user/task/video_gen_progress_{timestamp}.log
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
python video_generator.py "scene 1" "scene 2" "scene 3"
|
|
11
|
+
python video_generator.py --enhanced --resolution 2K "scene 1" "scene 2"
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import os
|
|
15
|
+
import sys
|
|
16
|
+
import time
|
|
17
|
+
import subprocess
|
|
18
|
+
import argparse
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import List, Optional
|
|
21
|
+
from datetime import datetime
|
|
22
|
+
from google import genai
|
|
23
|
+
from google.genai import types
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class LongFormVideoGenerator:
|
|
27
|
+
"""Generate long-form videos with Veo 3.1"""
|
|
28
|
+
|
|
29
|
+
def __init__(self, api_key: str, output_dir: str = "/home/user/task", temp_dir: str = "/home/user/task", progress_file: str = None):
|
|
30
|
+
self.api_key = api_key
|
|
31
|
+
self.output_dir = Path(output_dir)
|
|
32
|
+
self.temp_dir = Path(temp_dir)
|
|
33
|
+
self.client = genai.Client(api_key=api_key)
|
|
34
|
+
|
|
35
|
+
# Ensure directories exist (task directory should already exist from sandbox setup)
|
|
36
|
+
self.output_dir.mkdir(parents=True, exist_ok=True)
|
|
37
|
+
self.temp_dir.mkdir(parents=True, exist_ok=True)
|
|
38
|
+
|
|
39
|
+
# Setup progress logging
|
|
40
|
+
if progress_file is None:
|
|
41
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
42
|
+
progress_file = f"/home/user/task/video_gen_progress_{timestamp}.log"
|
|
43
|
+
self.progress_file = progress_file
|
|
44
|
+
self.log_progress("STARTED", "Video generation started")
|
|
45
|
+
|
|
46
|
+
def log_progress(self, status: str, message: str):
|
|
47
|
+
"""Log progress to file"""
|
|
48
|
+
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
49
|
+
log_entry = f"[{timestamp}] {status}: {message}\n"
|
|
50
|
+
with open(self.progress_file, 'a') as f:
|
|
51
|
+
f.write(log_entry)
|
|
52
|
+
f.flush() # Force write to disk immediately so clients can see updates
|
|
53
|
+
print(log_entry.strip())
|
|
54
|
+
|
|
55
|
+
def generate_reference_image(
|
|
56
|
+
self,
|
|
57
|
+
prompt: str,
|
|
58
|
+
output_path: str,
|
|
59
|
+
model: str = "gemini-3-pro-image-preview",
|
|
60
|
+
resolution: str = "2K",
|
|
61
|
+
aspect_ratio: str = "16:9"
|
|
62
|
+
) -> str:
|
|
63
|
+
"""Generate reference image using Nano Banana Pro"""
|
|
64
|
+
try:
|
|
65
|
+
self.log_progress("IMAGE_GEN", f"Generating reference image with {model}")
|
|
66
|
+
print(f"\nGenerating reference image with {model}")
|
|
67
|
+
print(f"Resolution: {resolution}, Aspect Ratio: {aspect_ratio}")
|
|
68
|
+
|
|
69
|
+
config = types.GenerateContentConfig(
|
|
70
|
+
response_modalities=['IMAGE'],
|
|
71
|
+
image_config=types.ImageConfig(aspect_ratio=aspect_ratio)
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
response = self.client.models.generate_content(
|
|
75
|
+
model=model,
|
|
76
|
+
contents=prompt,
|
|
77
|
+
config=config
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
for part in response.parts:
|
|
81
|
+
if image := part.as_image():
|
|
82
|
+
image.save(output_path)
|
|
83
|
+
self.log_progress("IMAGE_SAVED", f"Reference image saved: {output_path}")
|
|
84
|
+
print(f"Reference image saved: {output_path}")
|
|
85
|
+
return output_path
|
|
86
|
+
|
|
87
|
+
raise Exception("No image generated in response")
|
|
88
|
+
except Exception as e:
|
|
89
|
+
self.log_progress("FAILURE", f"Reference image generation failed: {str(e)}")
|
|
90
|
+
import traceback
|
|
91
|
+
self.log_progress("FAILURE", f"Traceback: {traceback.format_exc()}")
|
|
92
|
+
raise
|
|
93
|
+
|
|
94
|
+
raise ValueError("No image generated")
|
|
95
|
+
|
|
96
|
+
def generate_video_clip(
|
|
97
|
+
self,
|
|
98
|
+
prompt: str,
|
|
99
|
+
scene_number: int,
|
|
100
|
+
reference_image: Optional[str] = None
|
|
101
|
+
) -> str:
|
|
102
|
+
"""Generate single 8-second video clip"""
|
|
103
|
+
|
|
104
|
+
self.log_progress("SCENE_START", f"Starting scene {scene_number}")
|
|
105
|
+
print(f"\n{'='*70}")
|
|
106
|
+
print(f"Scene {scene_number}")
|
|
107
|
+
print(f"{'='*70}")
|
|
108
|
+
print(f"Prompt: {prompt[:80]}...")
|
|
109
|
+
|
|
110
|
+
if reference_image:
|
|
111
|
+
print(f"Using reference image: {reference_image}")
|
|
112
|
+
|
|
113
|
+
try:
|
|
114
|
+
if reference_image and os.path.exists(reference_image):
|
|
115
|
+
print(f"Loading reference image...")
|
|
116
|
+
reference_img = types.Image.from_file(location=reference_image)
|
|
117
|
+
|
|
118
|
+
operation = self.client.models.generate_videos(
|
|
119
|
+
model="veo-3.1-generate-preview",
|
|
120
|
+
prompt=prompt,
|
|
121
|
+
image=reference_img,
|
|
122
|
+
config=types.GenerateVideosConfig(
|
|
123
|
+
duration_seconds=8,
|
|
124
|
+
aspect_ratio="16:9",
|
|
125
|
+
resolution="720p"
|
|
126
|
+
)
|
|
127
|
+
)
|
|
128
|
+
else:
|
|
129
|
+
operation = self.client.models.generate_videos(
|
|
130
|
+
model="veo-3.1-generate-preview",
|
|
131
|
+
prompt=prompt,
|
|
132
|
+
config=types.GenerateVideosConfig(
|
|
133
|
+
duration_seconds=8,
|
|
134
|
+
aspect_ratio="16:9",
|
|
135
|
+
resolution="720p"
|
|
136
|
+
)
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
# Poll until ready
|
|
140
|
+
self.log_progress("SCENE_GENERATING", f"Scene {scene_number}: Generating video (2-3 minutes)...")
|
|
141
|
+
print(f"Generating video (2-3 minutes)...")
|
|
142
|
+
poll_count = 0
|
|
143
|
+
start_time = time.time()
|
|
144
|
+
try:
|
|
145
|
+
while not operation.done:
|
|
146
|
+
elapsed = int(time.time() - start_time)
|
|
147
|
+
# Log progress on every poll (every 10 seconds) so clients see updates
|
|
148
|
+
self.log_progress("SCENE_PROGRESS", f"Scene {scene_number}: Still generating... ({elapsed}s elapsed)")
|
|
149
|
+
print(f"Still generating... ({elapsed}s elapsed)")
|
|
150
|
+
time.sleep(10)
|
|
151
|
+
try:
|
|
152
|
+
operation = self.client.operations.get(operation)
|
|
153
|
+
except Exception as poll_error:
|
|
154
|
+
self.log_progress("FAILURE", f"Scene {scene_number}: Error polling operation: {str(poll_error)}")
|
|
155
|
+
raise
|
|
156
|
+
poll_count += 1
|
|
157
|
+
|
|
158
|
+
# Safety timeout: if polling for more than 10 minutes, something is wrong
|
|
159
|
+
if elapsed > 600:
|
|
160
|
+
raise Exception(f"Video generation timeout after {elapsed}s")
|
|
161
|
+
|
|
162
|
+
if not operation.response or not operation.response.generated_videos:
|
|
163
|
+
raise Exception("Video generation failed - no video in response")
|
|
164
|
+
except Exception as poll_exception:
|
|
165
|
+
self.log_progress("FAILURE", f"Scene {scene_number}: Polling failed: {str(poll_exception)}")
|
|
166
|
+
import traceback
|
|
167
|
+
self.log_progress("FAILURE", f"Traceback: {traceback.format_exc()}")
|
|
168
|
+
raise
|
|
169
|
+
|
|
170
|
+
# Download video
|
|
171
|
+
try:
|
|
172
|
+
self.log_progress("SCENE_DOWNLOADING", f"Scene {scene_number}: Downloading video...")
|
|
173
|
+
print(f"Downloading video...")
|
|
174
|
+
generated_video = operation.response.generated_videos[0]
|
|
175
|
+
|
|
176
|
+
video_path = str(self.temp_dir / f"scene_{scene_number:03d}.mp4")
|
|
177
|
+
self.client.files.download(file=generated_video.video)
|
|
178
|
+
generated_video.video.save(video_path)
|
|
179
|
+
|
|
180
|
+
self.log_progress("SCENE_COMPLETE", f"Scene {scene_number}: Video saved: {video_path}")
|
|
181
|
+
print(f"Video saved: {video_path}")
|
|
182
|
+
return video_path
|
|
183
|
+
except Exception as download_error:
|
|
184
|
+
self.log_progress("FAILURE", f"Scene {scene_number}: Download failed: {str(download_error)}")
|
|
185
|
+
import traceback
|
|
186
|
+
self.log_progress("FAILURE", f"Traceback: {traceback.format_exc()}")
|
|
187
|
+
raise
|
|
188
|
+
|
|
189
|
+
except Exception as e:
|
|
190
|
+
error_msg = f"Scene {scene_number}: API Error - {str(e)}"
|
|
191
|
+
self.log_progress("FAILURE", error_msg)
|
|
192
|
+
print(f"API Error: {e}")
|
|
193
|
+
import traceback
|
|
194
|
+
self.log_progress("FAILURE", f"Traceback: {traceback.format_exc()}")
|
|
195
|
+
raise
|
|
196
|
+
|
|
197
|
+
def extract_last_frame(self, video_path: str, output_path: str) -> str:
|
|
198
|
+
"""Extract last frame from video"""
|
|
199
|
+
try:
|
|
200
|
+
print(f"Extracting last frame...")
|
|
201
|
+
|
|
202
|
+
result = subprocess.run(
|
|
203
|
+
['ffprobe', '-v', 'error', '-show_entries', 'format=duration',
|
|
204
|
+
'-of', 'default=noprint_wrappers=1:nokey=1', video_path],
|
|
205
|
+
capture_output=True,
|
|
206
|
+
text=True
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
if result.returncode != 0:
|
|
210
|
+
raise Exception(f"ffprobe failed: {result.stderr}")
|
|
211
|
+
|
|
212
|
+
duration = float(result.stdout.strip())
|
|
213
|
+
|
|
214
|
+
result = subprocess.run([
|
|
215
|
+
'ffmpeg', '-ss', str(duration - 0.1), '-i', video_path,
|
|
216
|
+
'-vframes', '1', '-q:v', '2', output_path, '-y'
|
|
217
|
+
], capture_output=True, check=True, text=True)
|
|
218
|
+
|
|
219
|
+
print(f"Frame extracted: {output_path}")
|
|
220
|
+
return output_path
|
|
221
|
+
except Exception as e:
|
|
222
|
+
self.log_progress("FAILURE", f"Frame extraction failed: {str(e)}")
|
|
223
|
+
import traceback
|
|
224
|
+
self.log_progress("FAILURE", f"Traceback: {traceback.format_exc()}")
|
|
225
|
+
raise
|
|
226
|
+
|
|
227
|
+
def concatenate_videos_smooth(
|
|
228
|
+
self,
|
|
229
|
+
video_paths: List[str],
|
|
230
|
+
output_path: str,
|
|
231
|
+
transition_duration: float = 0.5
|
|
232
|
+
) -> str:
|
|
233
|
+
"""Concatenate videos with smooth crossfade transitions"""
|
|
234
|
+
print(f"\n{'='*70}")
|
|
235
|
+
print(f"Concatenating {len(video_paths)} videos with smooth transitions")
|
|
236
|
+
print(f"Transition duration: {transition_duration}s")
|
|
237
|
+
print(f"{'='*70}\n")
|
|
238
|
+
|
|
239
|
+
if len(video_paths) == 1:
|
|
240
|
+
subprocess.run(['cp', video_paths[0], output_path], check=True)
|
|
241
|
+
print(f"Single video copied")
|
|
242
|
+
return output_path
|
|
243
|
+
|
|
244
|
+
# For 2 videos
|
|
245
|
+
if len(video_paths) == 2:
|
|
246
|
+
offset = 8.0 - transition_duration
|
|
247
|
+
cmd = [
|
|
248
|
+
'ffmpeg',
|
|
249
|
+
'-i', video_paths[0],
|
|
250
|
+
'-i', video_paths[1],
|
|
251
|
+
'-filter_complex',
|
|
252
|
+
f'[0:v][1:v]xfade=transition=fade:duration={transition_duration}:offset={offset}[outv];'
|
|
253
|
+
f'[0:a][1:a]acrossfade=d={transition_duration}[outa]',
|
|
254
|
+
'-map', '[outv]',
|
|
255
|
+
'-map', '[outa]',
|
|
256
|
+
'-c:v', 'libx264', # H.264 codec for web compatibility
|
|
257
|
+
'-preset', 'medium', # Encoding speed/quality balance
|
|
258
|
+
'-crf', '23', # Quality (18-28, lower = better quality)
|
|
259
|
+
'-pix_fmt', 'yuv420p', # Ensure compatibility
|
|
260
|
+
'-c:a', 'aac', # AAC audio codec
|
|
261
|
+
'-b:a', '128k', # Audio bitrate
|
|
262
|
+
'-movflags', '+faststart', # Enable web streaming (play before full download)
|
|
263
|
+
'-y', output_path
|
|
264
|
+
]
|
|
265
|
+
subprocess.run(cmd, capture_output=True, check=True)
|
|
266
|
+
print(f"Videos concatenated with smooth transition")
|
|
267
|
+
return output_path
|
|
268
|
+
|
|
269
|
+
# For 3+ videos, chain xfades
|
|
270
|
+
filter_parts = []
|
|
271
|
+
audio_parts = []
|
|
272
|
+
|
|
273
|
+
for i in range(len(video_paths) - 1):
|
|
274
|
+
offset = (8.0 - transition_duration) * (i + 1)
|
|
275
|
+
|
|
276
|
+
if i == 0:
|
|
277
|
+
filter_parts.append(
|
|
278
|
+
f'[0:v][1:v]xfade=transition=fade:duration={transition_duration}:offset={8.0-transition_duration}[v01]'
|
|
279
|
+
)
|
|
280
|
+
audio_parts.append(f'[0:a][1:a]acrossfade=d={transition_duration}[a01]')
|
|
281
|
+
else:
|
|
282
|
+
prev_label = f'v0{i}' if i == 1 else f'v{i-1}{i}'
|
|
283
|
+
curr_label = f'v{i}{i+1}'
|
|
284
|
+
filter_parts.append(
|
|
285
|
+
f'[{prev_label}][{i+1}:v]xfade=transition=fade:duration={transition_duration}:offset={offset}[{curr_label}]'
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
prev_audio = f'a0{i}' if i == 1 else f'a{i-1}{i}'
|
|
289
|
+
curr_audio = f'a{i}{i+1}'
|
|
290
|
+
audio_parts.append(f'[{prev_audio}][{i+1}:a]acrossfade=d={transition_duration}[{curr_audio}]')
|
|
291
|
+
|
|
292
|
+
last_v = f'v{len(video_paths)-2}{len(video_paths)-1}'
|
|
293
|
+
last_a = f'a{len(video_paths)-2}{len(video_paths)-1}'
|
|
294
|
+
|
|
295
|
+
filter_complex = ';'.join(filter_parts + audio_parts)
|
|
296
|
+
|
|
297
|
+
input_args = []
|
|
298
|
+
for video in video_paths:
|
|
299
|
+
input_args.extend(['-i', video])
|
|
300
|
+
|
|
301
|
+
cmd = ['ffmpeg'] + input_args + [
|
|
302
|
+
'-filter_complex', filter_complex,
|
|
303
|
+
'-map', f'[{last_v}]',
|
|
304
|
+
'-map', f'[{last_a}]',
|
|
305
|
+
'-c:v', 'libx264', # H.264 codec for web compatibility
|
|
306
|
+
'-preset', 'medium', # Encoding speed/quality balance
|
|
307
|
+
'-crf', '23', # Quality (18-28, lower = better quality)
|
|
308
|
+
'-pix_fmt', 'yuv420p', # Ensure compatibility
|
|
309
|
+
'-c:a', 'aac', # AAC audio codec
|
|
310
|
+
'-b:a', '128k', # Audio bitrate
|
|
311
|
+
'-movflags', '+faststart', # Enable web streaming (play before full download)
|
|
312
|
+
'-y', output_path
|
|
313
|
+
]
|
|
314
|
+
|
|
315
|
+
try:
|
|
316
|
+
result = subprocess.run(cmd, capture_output=True, check=True, text=True)
|
|
317
|
+
print(f"Videos concatenated with smooth transitions")
|
|
318
|
+
return output_path
|
|
319
|
+
except subprocess.CalledProcessError as e:
|
|
320
|
+
error_msg = f"Video concatenation failed: {e.stderr if e.stderr else str(e)}"
|
|
321
|
+
self.log_progress("FAILURE", error_msg)
|
|
322
|
+
import traceback
|
|
323
|
+
self.log_progress("FAILURE", f"Traceback: {traceback.format_exc()}")
|
|
324
|
+
raise Exception(error_msg) from e
|
|
325
|
+
except Exception as e:
|
|
326
|
+
self.log_progress("FAILURE", f"Video concatenation failed: {str(e)}")
|
|
327
|
+
import traceback
|
|
328
|
+
self.log_progress("FAILURE", f"Traceback: {traceback.format_exc()}")
|
|
329
|
+
raise
|
|
330
|
+
|
|
331
|
+
def generate_longform_video(
|
|
332
|
+
self,
|
|
333
|
+
scenes: List[str],
|
|
334
|
+
output_filename: str = "output.mp4",
|
|
335
|
+
use_enhanced_first_frame: bool = False,
|
|
336
|
+
enhanced_resolution: str = "2K",
|
|
337
|
+
use_frame_chaining: bool = True,
|
|
338
|
+
smooth_transitions: bool = True,
|
|
339
|
+
transition_duration: float = 0.5
|
|
340
|
+
) -> str:
|
|
341
|
+
"""Generate long-form video"""
|
|
342
|
+
|
|
343
|
+
if not scenes:
|
|
344
|
+
raise ValueError("scenes parameter is required")
|
|
345
|
+
|
|
346
|
+
self.log_progress("INIT", f"Starting long-form video generation - {len(scenes)} scenes")
|
|
347
|
+
print("\n" + "="*70)
|
|
348
|
+
print(f"LONG-FORM VIDEO GENERATION - {len(scenes)} SCENES")
|
|
349
|
+
if use_enhanced_first_frame:
|
|
350
|
+
print(f"Enhanced mode: First frame with Nano Banana Pro ({enhanced_resolution})")
|
|
351
|
+
self.log_progress("CONFIG", f"Enhanced mode enabled: {enhanced_resolution}")
|
|
352
|
+
if smooth_transitions:
|
|
353
|
+
print(f"Smooth transitions enabled ({transition_duration}s crossfade)")
|
|
354
|
+
self.log_progress("CONFIG", f"Smooth transitions: {transition_duration}s crossfade")
|
|
355
|
+
print("="*70 + "\n")
|
|
356
|
+
|
|
357
|
+
video_paths = []
|
|
358
|
+
last_frame_path = None
|
|
359
|
+
|
|
360
|
+
for i, prompt in enumerate(scenes):
|
|
361
|
+
scene_num = i + 1
|
|
362
|
+
reference = last_frame_path
|
|
363
|
+
|
|
364
|
+
# Use Nano Banana Pro for first scene
|
|
365
|
+
if scene_num == 1 and use_enhanced_first_frame:
|
|
366
|
+
print(f"\nGENERATING HIGH-QUALITY FIRST FRAME")
|
|
367
|
+
print(f"Using Nano Banana Pro ({enhanced_resolution})")
|
|
368
|
+
|
|
369
|
+
image_prompt = f"""
|
|
370
|
+
Cinematic still frame for video scene.
|
|
371
|
+
{prompt}
|
|
372
|
+
High-resolution professional photography, dramatic lighting.
|
|
373
|
+
16:9 aspect ratio, photorealistic, ultra detailed.
|
|
374
|
+
"""
|
|
375
|
+
|
|
376
|
+
reference = str(self.temp_dir / "pro_first_frame.png")
|
|
377
|
+
self.generate_reference_image(
|
|
378
|
+
prompt=image_prompt,
|
|
379
|
+
output_path=reference,
|
|
380
|
+
model="gemini-3-pro-image-preview",
|
|
381
|
+
resolution=enhanced_resolution,
|
|
382
|
+
aspect_ratio="16:9"
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
# Generate video
|
|
386
|
+
video_path = self.generate_video_clip(prompt, scene_num, reference)
|
|
387
|
+
video_paths.append(video_path)
|
|
388
|
+
|
|
389
|
+
# Extract last frame for chaining
|
|
390
|
+
if use_frame_chaining and i < len(scenes) - 1:
|
|
391
|
+
frame_path = str(self.temp_dir / f"scene_{scene_num:03d}_last_frame.png")
|
|
392
|
+
last_frame_path = self.extract_last_frame(video_path, frame_path)
|
|
393
|
+
|
|
394
|
+
print(f"Scene {scene_num} complete")
|
|
395
|
+
|
|
396
|
+
if i < len(scenes) - 1:
|
|
397
|
+
print(f"Pausing 5 seconds...")
|
|
398
|
+
time.sleep(5)
|
|
399
|
+
|
|
400
|
+
# Concatenate
|
|
401
|
+
self.log_progress("CONCAT_START", "Concatenating video scenes...")
|
|
402
|
+
output_path = str(self.output_dir / output_filename)
|
|
403
|
+
if smooth_transitions and len(video_paths) > 1:
|
|
404
|
+
self.concatenate_videos_smooth(video_paths, output_path, transition_duration)
|
|
405
|
+
else:
|
|
406
|
+
# Simple concatenation
|
|
407
|
+
try:
|
|
408
|
+
list_file = str(self.temp_dir / 'concat_list.txt')
|
|
409
|
+
with open(list_file, 'w') as f:
|
|
410
|
+
for video_path in video_paths:
|
|
411
|
+
f.write(f"file '{os.path.abspath(video_path)}'\n")
|
|
412
|
+
|
|
413
|
+
result = subprocess.run([
|
|
414
|
+
'ffmpeg', '-f', 'concat', '-safe', '0',
|
|
415
|
+
'-i', list_file,
|
|
416
|
+
'-c:v', 'libx264', # Re-encode for optimization
|
|
417
|
+
'-preset', 'medium',
|
|
418
|
+
'-crf', '23',
|
|
419
|
+
'-pix_fmt', 'yuv420p',
|
|
420
|
+
'-c:a', 'aac',
|
|
421
|
+
'-b:a', '128k',
|
|
422
|
+
'-movflags', '+faststart', # Enable web streaming
|
|
423
|
+
'-y', output_path
|
|
424
|
+
], capture_output=True, check=True, text=True)
|
|
425
|
+
except subprocess.CalledProcessError as e:
|
|
426
|
+
error_msg = f"Simple concatenation failed: {e.stderr if e.stderr else str(e)}"
|
|
427
|
+
self.log_progress("FAILURE", error_msg)
|
|
428
|
+
import traceback
|
|
429
|
+
self.log_progress("FAILURE", f"Traceback: {traceback.format_exc()}")
|
|
430
|
+
raise Exception(error_msg) from e
|
|
431
|
+
except Exception as e:
|
|
432
|
+
self.log_progress("FAILURE", f"Simple concatenation failed: {str(e)}")
|
|
433
|
+
import traceback
|
|
434
|
+
self.log_progress("FAILURE", f"Traceback: {traceback.format_exc()}")
|
|
435
|
+
raise
|
|
436
|
+
|
|
437
|
+
# Get final info
|
|
438
|
+
try:
|
|
439
|
+
result = subprocess.run(
|
|
440
|
+
['ffprobe', '-v', 'error', '-show_entries', 'format=duration',
|
|
441
|
+
'-of', 'default=noprint_wrappers=1:nokey=1', output_path],
|
|
442
|
+
capture_output=True,
|
|
443
|
+
text=True
|
|
444
|
+
)
|
|
445
|
+
if result.returncode != 0:
|
|
446
|
+
raise Exception(f"ffprobe failed: {result.stderr}")
|
|
447
|
+
duration = float(result.stdout.strip())
|
|
448
|
+
except Exception as e:
|
|
449
|
+
self.log_progress("WARNING", f"Could not get video duration: {str(e)} (continuing anyway)")
|
|
450
|
+
duration = 0.0 # Continue even if duration check fails
|
|
451
|
+
|
|
452
|
+
self.log_progress("COMPLETE", f"Video generation complete: {output_path} ({duration:.2f}s, {len(video_paths)} scenes)")
|
|
453
|
+
self.log_progress("SUCCESS", f"Video saved to: {output_path}")
|
|
454
|
+
print(f"\n{'='*70}")
|
|
455
|
+
print("GENERATION COMPLETE")
|
|
456
|
+
print(f"{'='*70}")
|
|
457
|
+
print(f"Location: {output_path}")
|
|
458
|
+
print(f"Duration: {duration:.2f} seconds")
|
|
459
|
+
print(f"Scenes: {len(video_paths)}")
|
|
460
|
+
if use_enhanced_first_frame:
|
|
461
|
+
print(f"Enhanced with Nano Banana Pro ({enhanced_resolution})")
|
|
462
|
+
if smooth_transitions:
|
|
463
|
+
print(f"Smooth transitions: {transition_duration}s crossfade")
|
|
464
|
+
print(f"{'='*70}\n")
|
|
465
|
+
|
|
466
|
+
return output_path
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
def main():
|
|
470
|
+
"""CLI entry point"""
|
|
471
|
+
|
|
472
|
+
parser = argparse.ArgumentParser(
|
|
473
|
+
description='Generate long-form videos using Veo 3.1',
|
|
474
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
475
|
+
epilog="""
|
|
476
|
+
Examples:
|
|
477
|
+
python video_generator.py "Scene 1" "Scene 2" "Scene 3"
|
|
478
|
+
python video_generator.py --enhanced --resolution 2K "Scene 1" "Scene 2"
|
|
479
|
+
python video_generator.py -o my_video.mp4 "Scene 1" "Scene 2"
|
|
480
|
+
"""
|
|
481
|
+
)
|
|
482
|
+
|
|
483
|
+
parser.add_argument(
|
|
484
|
+
'scenes',
|
|
485
|
+
nargs='+',
|
|
486
|
+
help='Scene prompts (at least one required)'
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
parser.add_argument(
|
|
490
|
+
'-o', '--output',
|
|
491
|
+
default='longform_video.mp4',
|
|
492
|
+
help='Output filename (default: longform_video.mp4)'
|
|
493
|
+
)
|
|
494
|
+
|
|
495
|
+
parser.add_argument(
|
|
496
|
+
'--enhanced',
|
|
497
|
+
action='store_true',
|
|
498
|
+
help='Use Nano Banana Pro for first frame'
|
|
499
|
+
)
|
|
500
|
+
|
|
501
|
+
parser.add_argument(
|
|
502
|
+
'--resolution',
|
|
503
|
+
choices=['1K', '2K', '4K'],
|
|
504
|
+
default='2K',
|
|
505
|
+
help='Enhanced frame resolution (default: 2K)'
|
|
506
|
+
)
|
|
507
|
+
|
|
508
|
+
parser.add_argument(
|
|
509
|
+
'--no-smooth',
|
|
510
|
+
action='store_true',
|
|
511
|
+
help='Disable smooth transitions'
|
|
512
|
+
)
|
|
513
|
+
|
|
514
|
+
parser.add_argument(
|
|
515
|
+
'--transition-duration',
|
|
516
|
+
type=float,
|
|
517
|
+
default=0.5,
|
|
518
|
+
help='Transition duration in seconds (default: 0.5)'
|
|
519
|
+
)
|
|
520
|
+
|
|
521
|
+
parser.add_argument(
|
|
522
|
+
'--no-frame-chaining',
|
|
523
|
+
action='store_true',
|
|
524
|
+
help='Disable frame chaining'
|
|
525
|
+
)
|
|
526
|
+
|
|
527
|
+
args = parser.parse_args()
|
|
528
|
+
|
|
529
|
+
# Get API key from environment
|
|
530
|
+
api_key = os.getenv("GEMINI_API_KEY") or os.getenv("VEO3_API_KEY")
|
|
531
|
+
|
|
532
|
+
print("="*70)
|
|
533
|
+
print("LONG-FORM VIDEO GENERATOR")
|
|
534
|
+
print("Generate videos longer than 8 seconds with Veo 3.1")
|
|
535
|
+
print("="*70)
|
|
536
|
+
print()
|
|
537
|
+
|
|
538
|
+
scenes = args.scenes
|
|
539
|
+
print(f"Generating video with {len(scenes)} scenes")
|
|
540
|
+
print()
|
|
541
|
+
for i, scene in enumerate(scenes, 1):
|
|
542
|
+
preview = scene[:80].replace('\n', ' ')
|
|
543
|
+
print(f"Scene {i}: {preview}...")
|
|
544
|
+
print()
|
|
545
|
+
|
|
546
|
+
# Create generator (uses /home/user/task by default in sandbox)
|
|
547
|
+
generator = LongFormVideoGenerator(api_key=api_key)
|
|
548
|
+
|
|
549
|
+
# Print progress file location
|
|
550
|
+
print(f"Progress log: {generator.progress_file}")
|
|
551
|
+
print()
|
|
552
|
+
|
|
553
|
+
try:
|
|
554
|
+
# Generate video
|
|
555
|
+
video_path = generator.generate_longform_video(
|
|
556
|
+
scenes=scenes,
|
|
557
|
+
output_filename=args.output,
|
|
558
|
+
use_enhanced_first_frame=args.enhanced,
|
|
559
|
+
enhanced_resolution=args.resolution,
|
|
560
|
+
use_frame_chaining=not args.no_frame_chaining,
|
|
561
|
+
smooth_transitions=not args.no_smooth,
|
|
562
|
+
transition_duration=args.transition_duration
|
|
563
|
+
)
|
|
564
|
+
|
|
565
|
+
print(f"\nSUCCESS! Your video is ready: {video_path}")
|
|
566
|
+
print(f"Progress log: {generator.progress_file}")
|
|
567
|
+
|
|
568
|
+
except Exception as e:
|
|
569
|
+
generator.log_progress("FAILURE", f"Video generation failed: {str(e)}")
|
|
570
|
+
print(f"\nERROR: {e}")
|
|
571
|
+
import traceback
|
|
572
|
+
full_traceback = traceback.format_exc()
|
|
573
|
+
generator.log_progress("FAILURE", f"Full traceback: {full_traceback}")
|
|
574
|
+
traceback.print_exc()
|
|
575
|
+
sys.exit(1)
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
if __name__ == "__main__":
|
|
579
|
+
main()
|