@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.
Files changed (253) hide show
  1. package/dist/agent/agent.d.ts +90 -0
  2. package/dist/agent/agent.d.ts.map +1 -1
  3. package/dist/agent/agent.js +606 -0
  4. package/dist/agent/agent.js.map +1 -1
  5. package/dist/agent/pi-embedded-runner/run.d.ts.map +1 -1
  6. package/dist/agent/pi-embedded-runner/run.js +2 -1
  7. package/dist/agent/pi-embedded-runner/run.js.map +1 -1
  8. package/dist/agent/pi-embedded-runner/system-prompt.d.ts.map +1 -1
  9. package/dist/agent/pi-embedded-runner/system-prompt.js +16 -37
  10. package/dist/agent/pi-embedded-runner/system-prompt.js.map +1 -1
  11. package/dist/agent/pi-embedded-runner/tools.d.ts +4 -1
  12. package/dist/agent/pi-embedded-runner/tools.d.ts.map +1 -1
  13. package/dist/agent/pi-embedded-runner/tools.js +3 -1
  14. package/dist/agent/pi-embedded-runner/tools.js.map +1 -1
  15. package/dist/agent/pi-embedded-runner/types.d.ts +4 -0
  16. package/dist/agent/pi-embedded-runner/types.d.ts.map +1 -1
  17. package/dist/cli/env-loader.d.ts.map +1 -1
  18. package/dist/cli/env-loader.js +1 -0
  19. package/dist/cli/env-loader.js.map +1 -1
  20. package/dist/cli/setup.js +2 -2
  21. package/dist/cli/setup.js.map +1 -1
  22. package/dist/cron/normalize.d.ts +31 -0
  23. package/dist/cron/normalize.d.ts.map +1 -0
  24. package/dist/cron/normalize.js +211 -0
  25. package/dist/cron/normalize.js.map +1 -0
  26. package/dist/cron/scheduler.d.ts +33 -3
  27. package/dist/cron/scheduler.d.ts.map +1 -1
  28. package/dist/cron/scheduler.js +253 -48
  29. package/dist/cron/scheduler.js.map +1 -1
  30. package/dist/heartbeat/runner.d.ts +27 -12
  31. package/dist/heartbeat/runner.d.ts.map +1 -1
  32. package/dist/heartbeat/runner.js +82 -104
  33. package/dist/heartbeat/runner.js.map +1 -1
  34. package/dist/infra/heartbeat-events-filter.d.ts +29 -0
  35. package/dist/infra/heartbeat-events-filter.d.ts.map +1 -0
  36. package/dist/infra/heartbeat-events-filter.js +80 -0
  37. package/dist/infra/heartbeat-events-filter.js.map +1 -0
  38. package/dist/infra/index.d.ts +9 -0
  39. package/dist/infra/index.d.ts.map +1 -0
  40. package/dist/infra/index.js +9 -0
  41. package/dist/infra/index.js.map +1 -0
  42. package/dist/infra/system-events.d.ts +58 -2
  43. package/dist/infra/system-events.d.ts.map +1 -1
  44. package/dist/infra/system-events.js +80 -14
  45. package/dist/infra/system-events.js.map +1 -1
  46. package/dist/server.d.ts.map +1 -1
  47. package/dist/server.js +6 -1
  48. package/dist/server.js.map +1 -1
  49. package/dist/services/platform-keys.d.ts +19 -0
  50. package/dist/services/platform-keys.d.ts.map +1 -0
  51. package/dist/services/platform-keys.js +74 -0
  52. package/dist/services/platform-keys.js.map +1 -0
  53. package/dist/subagent/registry.d.ts +96 -0
  54. package/dist/subagent/registry.d.ts.map +1 -0
  55. package/dist/subagent/registry.js +180 -0
  56. package/dist/subagent/registry.js.map +1 -0
  57. package/dist/tools/complete-turn.d.ts +2 -2
  58. package/dist/tools/complete-turn.js +10 -10
  59. package/dist/tools/complete-turn.js.map +1 -1
  60. package/dist/tools/contacts.d.ts +13 -0
  61. package/dist/tools/contacts.d.ts.map +1 -0
  62. package/dist/tools/contacts.js +80 -0
  63. package/dist/tools/contacts.js.map +1 -0
  64. package/dist/tools/cron.d.ts +17 -2
  65. package/dist/tools/cron.d.ts.map +1 -1
  66. package/dist/tools/cron.js +117 -35
  67. package/dist/tools/cron.js.map +1 -1
  68. package/dist/tools/google-maps.d.ts +6 -6
  69. package/dist/tools/google-maps.d.ts.map +1 -1
  70. package/dist/tools/google-maps.js +207 -262
  71. package/dist/tools/google-maps.js.map +1 -1
  72. package/dist/tools/index.d.ts +17 -7
  73. package/dist/tools/index.d.ts.map +1 -1
  74. package/dist/tools/index.js +40 -9
  75. package/dist/tools/index.js.map +1 -1
  76. package/dist/tools/phone-call.d.ts +11 -0
  77. package/dist/tools/phone-call.d.ts.map +1 -0
  78. package/dist/tools/phone-call.js +151 -0
  79. package/dist/tools/phone-call.js.map +1 -0
  80. package/dist/tools/sessions-spawn.d.ts +33 -0
  81. package/dist/tools/sessions-spawn.d.ts.map +1 -0
  82. package/dist/tools/sessions-spawn.js +164 -0
  83. package/dist/tools/sessions-spawn.js.map +1 -0
  84. package/dist/tools/spotify.d.ts +12 -0
  85. package/dist/tools/spotify.d.ts.map +1 -0
  86. package/dist/tools/spotify.js +251 -0
  87. package/dist/tools/spotify.js.map +1 -0
  88. package/dist/tools/subagents.d.ts +23 -0
  89. package/dist/tools/subagents.d.ts.map +1 -0
  90. package/dist/tools/subagents.js +209 -0
  91. package/dist/tools/subagents.js.map +1 -0
  92. package/dist/tools/whatsapp.d.ts +13 -0
  93. package/dist/tools/whatsapp.d.ts.map +1 -0
  94. package/dist/tools/whatsapp.js +215 -0
  95. package/dist/tools/whatsapp.js.map +1 -0
  96. package/dist/tools/youtube.d.ts +12 -0
  97. package/dist/tools/youtube.d.ts.map +1 -0
  98. package/dist/tools/youtube.js +218 -0
  99. package/dist/tools/youtube.js.map +1 -0
  100. package/dist/utils/asterizk-auth.d.ts +43 -0
  101. package/dist/utils/asterizk-auth.d.ts.map +1 -0
  102. package/dist/utils/asterizk-auth.js +125 -0
  103. package/dist/utils/asterizk-auth.js.map +1 -0
  104. package/dist/web-server.d.ts.map +1 -1
  105. package/dist/web-server.js +132 -0
  106. package/dist/web-server.js.map +1 -1
  107. package/dist/workspace/index.d.ts +3 -4
  108. package/dist/workspace/index.d.ts.map +1 -1
  109. package/dist/workspace/index.js +3 -4
  110. package/dist/workspace/index.js.map +1 -1
  111. package/dist/workspace/templates.d.ts +8 -7
  112. package/dist/workspace/templates.d.ts.map +1 -1
  113. package/dist/workspace/templates.js +18 -127
  114. package/dist/workspace/templates.js.map +1 -1
  115. package/dist/workspace/workspace.d.ts +2 -4
  116. package/dist/workspace/workspace.d.ts.map +1 -1
  117. package/dist/workspace/workspace.js +7 -16
  118. package/dist/workspace/workspace.js.map +1 -1
  119. package/package.json +1 -1
  120. package/public/index.html +231 -0
  121. package/skills/docx/SKILL.md +468 -0
  122. package/skills/docx/scripts/__init__.py +1 -0
  123. package/skills/docx/scripts/accept_changes.py +181 -0
  124. package/skills/docx/scripts/comment.py +347 -0
  125. package/skills/docx/scripts/helpers/__init__.py +0 -0
  126. package/skills/docx/scripts/helpers/merge_runs.py +231 -0
  127. package/skills/docx/scripts/helpers/simplify_redlines.py +240 -0
  128. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  129. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  130. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  131. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  132. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  133. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  134. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  135. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  136. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  137. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  138. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  139. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  140. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  141. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  142. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  143. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  144. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  145. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  146. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  147. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  148. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  149. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  150. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  151. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  152. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  153. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  154. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  155. package/skills/docx/scripts/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  156. package/skills/docx/scripts/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  157. package/skills/docx/scripts/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  158. package/skills/docx/scripts/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  159. package/skills/docx/scripts/ooxml/schemas/mce/mc.xsd +75 -0
  160. package/skills/docx/scripts/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
  161. package/skills/docx/scripts/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
  162. package/skills/docx/scripts/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
  163. package/skills/docx/scripts/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
  164. package/skills/docx/scripts/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
  165. package/skills/docx/scripts/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  166. package/skills/docx/scripts/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
  167. package/skills/docx/scripts/ooxml/scripts/pack.py +159 -0
  168. package/skills/docx/scripts/ooxml/scripts/unpack.py +29 -0
  169. package/skills/docx/scripts/ooxml/scripts/validate.py +106 -0
  170. package/skills/docx/scripts/ooxml/scripts/validation/__init__.py +15 -0
  171. package/skills/docx/scripts/ooxml/scripts/validation/base.py +1023 -0
  172. package/skills/docx/scripts/ooxml/scripts/validation/docx.py +519 -0
  173. package/skills/docx/scripts/ooxml/scripts/validation/pptx.py +315 -0
  174. package/skills/docx/scripts/ooxml/scripts/validation/redlining.py +284 -0
  175. package/skills/docx/scripts/pack.py +166 -0
  176. package/skills/docx/scripts/templates/comments.xml +3 -0
  177. package/skills/docx/scripts/templates/commentsExtended.xml +3 -0
  178. package/skills/docx/scripts/templates/commentsExtensible.xml +3 -0
  179. package/skills/docx/scripts/templates/commentsIds.xml +3 -0
  180. package/skills/docx/scripts/templates/people.xml +3 -0
  181. package/skills/docx/scripts/unpack.py +134 -0
  182. package/skills/longform-video-generation/SKILL.md +298 -0
  183. package/skills/longform-video-generation/references/advanced_techniques.md +474 -0
  184. package/skills/longform-video-generation/references/google_api_guide.md +288 -0
  185. package/skills/longform-video-generation/scripts/video_generator.py +579 -0
  186. package/skills/pdf/FORMS.md +305 -0
  187. package/skills/pdf/REFERENCE.md +612 -0
  188. package/skills/pdf/SKILL.md +293 -0
  189. package/skills/pdf/scripts/check_bounding_boxes.py +70 -0
  190. package/skills/pdf/scripts/check_fillable_fields.py +12 -0
  191. package/skills/pdf/scripts/convert_pdf_to_images.py +35 -0
  192. package/skills/pdf/scripts/create_validation_image.py +41 -0
  193. package/skills/pdf/scripts/extract_form_field_info.py +152 -0
  194. package/skills/pdf/scripts/extract_form_structure.py +124 -0
  195. package/skills/pdf/scripts/fill_fillable_fields.py +116 -0
  196. package/skills/pdf/scripts/fill_pdf_form_with_annotations.py +136 -0
  197. package/skills/pptx/SKILL.md +171 -0
  198. package/skills/pptx/editing.md +205 -0
  199. package/skills/pptx/pptxgenjs.md +377 -0
  200. package/skills/pptx/scripts/add_slide.py +225 -0
  201. package/skills/pptx/scripts/clean.py +309 -0
  202. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  203. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  204. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  205. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  206. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  207. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  208. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  209. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  210. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  211. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  212. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  213. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  214. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  215. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  216. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  217. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  218. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  219. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  220. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  221. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  222. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  223. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  224. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  225. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  226. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  227. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  228. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  229. package/skills/pptx/scripts/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  230. package/skills/pptx/scripts/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  231. package/skills/pptx/scripts/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  232. package/skills/pptx/scripts/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  233. package/skills/pptx/scripts/ooxml/schemas/mce/mc.xsd +75 -0
  234. package/skills/pptx/scripts/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
  235. package/skills/pptx/scripts/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
  236. package/skills/pptx/scripts/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
  237. package/skills/pptx/scripts/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
  238. package/skills/pptx/scripts/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
  239. package/skills/pptx/scripts/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  240. package/skills/pptx/scripts/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
  241. package/skills/pptx/scripts/ooxml/scripts/pack.py +159 -0
  242. package/skills/pptx/scripts/ooxml/scripts/unpack.py +29 -0
  243. package/skills/pptx/scripts/ooxml/scripts/validate.py +106 -0
  244. package/skills/pptx/scripts/ooxml/scripts/validation/__init__.py +15 -0
  245. package/skills/pptx/scripts/ooxml/scripts/validation/base.py +1023 -0
  246. package/skills/pptx/scripts/ooxml/scripts/validation/docx.py +519 -0
  247. package/skills/pptx/scripts/ooxml/scripts/validation/pptx.py +315 -0
  248. package/skills/pptx/scripts/ooxml/scripts/validation/redlining.py +284 -0
  249. package/skills/pptx/scripts/pack.py +168 -0
  250. package/skills/pptx/scripts/thumbnail.py +318 -0
  251. package/skills/pptx/scripts/unpack.py +86 -0
  252. package/skills/xlsx/SKILL.md +291 -0
  253. 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()