videobook 0.0.0-stage → 0.2.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.
Files changed (565) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/LICENSE +21 -0
  3. package/README.md +195 -2
  4. package/bin/videobook.js +7 -0
  5. package/dist/acestep/client.js +203 -0
  6. package/dist/acestep/config.js +62 -0
  7. package/dist/acestep/errors.js +8 -0
  8. package/dist/acestep/process.js +175 -0
  9. package/dist/alive-captions/analyze.js +117 -0
  10. package/dist/alive-captions/audio.js +179 -0
  11. package/dist/alive-captions/frames.js +59 -0
  12. package/dist/alive-captions/model.js +37 -0
  13. package/dist/alive-captions/schema.js +49 -0
  14. package/dist/alive-captions/track.js +9 -0
  15. package/dist/api-key.js +33 -0
  16. package/dist/app-state.js +56 -0
  17. package/dist/app.js +257 -0
  18. package/dist/archive-b2.js +332 -0
  19. package/dist/archive-store.js +169 -0
  20. package/dist/artifact-label.js +36 -0
  21. package/dist/artifact-refs.js +49 -0
  22. package/dist/asset-tag-extraction.js +169 -0
  23. package/dist/asset-tags.js +228 -0
  24. package/dist/asset-usage.js +23 -0
  25. package/dist/audio-cue.js +71 -0
  26. package/dist/audio-encoding.js +32 -0
  27. package/dist/audio-sync-request.js +1 -0
  28. package/dist/audio-sync.js +223 -0
  29. package/dist/audio-trim-range.js +31 -0
  30. package/dist/book-context.js +31 -0
  31. package/dist/book-manager.js +374 -0
  32. package/dist/catalog-gc.js +26 -0
  33. package/dist/chain-executor.js +815 -0
  34. package/dist/chain-generation.js +115 -0
  35. package/dist/chain-plan.js +746 -0
  36. package/dist/chain-step-review.js +60 -0
  37. package/dist/character-name.js +25 -0
  38. package/dist/chat-generation-routing.js +73 -0
  39. package/dist/chat-generation-settings.js +78 -0
  40. package/dist/chat-models.js +44 -0
  41. package/dist/chat-prompt-rewrite.js +67 -0
  42. package/dist/chat-router.js +628 -0
  43. package/dist/chat-view-actions.js +49 -0
  44. package/dist/cli/args.js +242 -0
  45. package/dist/cli/config-command.js +103 -0
  46. package/dist/cli/doctor-command.js +289 -0
  47. package/dist/cli/entry.js +2 -0
  48. package/dist/cli/key-report.js +16 -0
  49. package/dist/cli/main.js +104 -0
  50. package/dist/cli/mcp-command.js +88 -0
  51. package/dist/cli/mcp-relay.js +51 -0
  52. package/dist/cli/open-browser.js +14 -0
  53. package/dist/cli/run-command.js +231 -0
  54. package/dist/cli/serve-command.js +62 -0
  55. package/dist/cli/setup-command.js +60 -0
  56. package/dist/cli/stdio-guard.js +8 -0
  57. package/dist/cold-open-model.js +7 -0
  58. package/dist/cold-open.js +79 -0
  59. package/dist/command-reference.js +95 -0
  60. package/dist/content-store.js +74 -0
  61. package/dist/cost.js +8 -0
  62. package/dist/crop-geometry.js +23 -0
  63. package/dist/dancing-workflow.js +447 -0
  64. package/dist/data-home.js +44 -0
  65. package/dist/diagnostics.js +109 -0
  66. package/dist/dialog-quality.js +34 -0
  67. package/dist/downloads-dest.js +56 -0
  68. package/dist/editing-moments.js +194 -0
  69. package/dist/elevenlabs-audio-extension.js +66 -0
  70. package/dist/engine.js +54 -0
  71. package/dist/env-file.js +91 -0
  72. package/dist/env-secrets.js +148 -0
  73. package/dist/ffmpeg-binary.js +12 -0
  74. package/dist/ffmpeg.js +425 -0
  75. package/dist/ffprobe-binary.js +77 -0
  76. package/dist/forget-asset.js +216 -0
  77. package/dist/frame-utils.js +27 -0
  78. package/dist/generation-credit-error.js +11 -0
  79. package/dist/generation-input-mentions.js +117 -0
  80. package/dist/generation-input-policy.js +68 -0
  81. package/dist/generation-reference-mentions.js +97 -0
  82. package/dist/goal-characters.js +49 -0
  83. package/dist/goal-plan-v3.js +273 -0
  84. package/dist/goal-plan.js +220 -0
  85. package/dist/goal-planner-prompt.js +28 -0
  86. package/dist/goal-prompt-budget.js +4 -0
  87. package/dist/goal-reference-images.js +25 -0
  88. package/dist/goal-state.js +261 -0
  89. package/dist/goal-stills.js +153 -0
  90. package/dist/goal-store.js +144 -0
  91. package/dist/goal-styles.js +144 -0
  92. package/dist/goal-v3-runtime.js +157 -0
  93. package/dist/guides/character_schema_guide.md +70 -0
  94. package/dist/guides/gpt_image_2_5_prompting_guide.md +21 -0
  95. package/dist/guides/grok_imagine_prompting_guide.md +72 -0
  96. package/dist/guides/image_reproduction_guide.md +84 -0
  97. package/dist/guides/kling_3_0_prompting_guide.md +136 -0
  98. package/dist/guides/nano_banana_pro_prompting_guide.md +28 -0
  99. package/dist/guides/seedance_2_prompting_guide.md +172 -0
  100. package/dist/http-access.js +49 -0
  101. package/dist/http-byte-range.js +31 -0
  102. package/dist/hydration.js +56 -0
  103. package/dist/image-compress.js +77 -0
  104. package/dist/image-edit-default.js +1 -0
  105. package/dist/image-edit-models.js +41 -0
  106. package/dist/image-edit-settings.js +11 -0
  107. package/dist/image-region-edit.js +32 -0
  108. package/dist/image-subject-map.js +523 -0
  109. package/dist/image.js +46 -0
  110. package/dist/index.js +12 -0
  111. package/dist/job-deadline.js +60 -0
  112. package/dist/jobs/_alibaba-upload.js +30 -0
  113. package/dist/jobs/_audio-chunks.js +31 -0
  114. package/dist/jobs/_download-into-asset.js +15 -0
  115. package/dist/jobs/_fal-shared.js +22 -0
  116. package/dist/jobs/_fal-upload.js +35 -0
  117. package/dist/jobs/_image-edit-adapter.js +213 -0
  118. package/dist/jobs/_kie-poll.js +119 -0
  119. package/dist/jobs/_kie-upload.js +32 -0
  120. package/dist/jobs/_lease.js +163 -0
  121. package/dist/jobs/_persist-output.js +19 -0
  122. package/dist/jobs/_storage-retry.js +21 -0
  123. package/dist/jobs/_transcribe.js +245 -0
  124. package/dist/jobs/_translate-transcript.js +270 -0
  125. package/dist/jobs/analyze-alive-captions.js +10 -0
  126. package/dist/jobs/analyze-editing-moments.js +104 -0
  127. package/dist/jobs/analyze-jokes.js +143 -0
  128. package/dist/jobs/analyze-keyword-placement.js +155 -0
  129. package/dist/jobs/analyze-music.js +14 -0
  130. package/dist/jobs/analyze-video.js +282 -0
  131. package/dist/jobs/apply-cuts.js +83 -0
  132. package/dist/jobs/apply-sfx.js +107 -0
  133. package/dist/jobs/archive-clip.js +458 -0
  134. package/dist/jobs/assemble-goal.js +98 -0
  135. package/dist/jobs/audio-boost.js +71 -0
  136. package/dist/jobs/audio-mix.js +161 -0
  137. package/dist/jobs/backfill-asset-tags.js +144 -0
  138. package/dist/jobs/burn-watermark.js +316 -0
  139. package/dist/jobs/change-video-speed.js +176 -0
  140. package/dist/jobs/clean-video-audio.js +207 -0
  141. package/dist/jobs/cold-open.js +166 -0
  142. package/dist/jobs/combine-goal.js +108 -0
  143. package/dist/jobs/complete-provider-task.js +185 -0
  144. package/dist/jobs/copy-library-asset.js +33 -0
  145. package/dist/jobs/cost-estimate-assessment.js +113 -0
  146. package/dist/jobs/crop-image.js +47 -0
  147. package/dist/jobs/crop-video.js +71 -0
  148. package/dist/jobs/describe-image.js +181 -0
  149. package/dist/jobs/detect-scenes.js +33 -0
  150. package/dist/jobs/doctor-fix.js +100 -0
  151. package/dist/jobs/download-video.js +427 -0
  152. package/dist/jobs/enqueue-search-index.js +24 -0
  153. package/dist/jobs/even-split.js +122 -0
  154. package/dist/jobs/export-sequence.js +365 -0
  155. package/dist/jobs/extend-audio.js +229 -0
  156. package/dist/jobs/extract-audio.js +72 -0
  157. package/dist/jobs/extract-frame.js +58 -0
  158. package/dist/jobs/extract-notebook-audio.js +82 -0
  159. package/dist/jobs/extract-notebook-frame.js +93 -0
  160. package/dist/jobs/extract-video-clip.js +8 -0
  161. package/dist/jobs/extract-video-poster.js +55 -0
  162. package/dist/jobs/extract-waveform.js +25 -0
  163. package/dist/jobs/finish-goal.js +255 -0
  164. package/dist/jobs/forget-asset.js +11 -0
  165. package/dist/jobs/generate-acestep-cover.js +219 -0
  166. package/dist/jobs/generate-media.js +70 -0
  167. package/dist/jobs/image-subjects.js +1059 -0
  168. package/dist/jobs/index.js +18 -0
  169. package/dist/jobs/inject-first-frame.js +175 -0
  170. package/dist/jobs/isolate-vocals.js +69 -0
  171. package/dist/jobs/kie-paygate.js +177 -0
  172. package/dist/jobs/label-music-sections.js +5 -0
  173. package/dist/jobs/osg-placement.js +135 -0
  174. package/dist/jobs/pad-video.js +72 -0
  175. package/dist/jobs/plan-goal.js +97 -0
  176. package/dist/jobs/post-record.js +21 -0
  177. package/dist/jobs/post-to-x.js +21 -0
  178. package/dist/jobs/prepare-search-reference.js +190 -0
  179. package/dist/jobs/prepare-temporal-search.js +16 -0
  180. package/dist/jobs/process-audio.js +67 -0
  181. package/dist/jobs/process-image.js +58 -0
  182. package/dist/jobs/process-upload.js +166 -0
  183. package/dist/jobs/process-video.js +438 -0
  184. package/dist/jobs/register-all.js +161 -0
  185. package/dist/jobs/relayout-notebook.js +78 -0
  186. package/dist/jobs/remove-sound.js +299 -0
  187. package/dist/jobs/render-project.js +380 -0
  188. package/dist/jobs/render-qr-outro.js +138 -0
  189. package/dist/jobs/render-timeline.js +283 -0
  190. package/dist/jobs/repair-search-indexes.js +42 -0
  191. package/dist/jobs/replace-video-audio.js +64 -0
  192. package/dist/jobs/restore-archive-clip.js +53 -0
  193. package/dist/jobs/reverse-video.js +53 -0
  194. package/dist/jobs/rewrite-script.js +173 -0
  195. package/dist/jobs/run-notebook.js +3335 -0
  196. package/dist/jobs/semantic-index.js +797 -0
  197. package/dist/jobs/smooth-scene-concat.js +257 -0
  198. package/dist/jobs/splice-video.js +280 -0
  199. package/dist/jobs/split-video.js +127 -0
  200. package/dist/jobs/suggest-cuts.js +149 -0
  201. package/dist/jobs/suggest-sfx.js +135 -0
  202. package/dist/jobs/sync-video-to-audio.js +329 -0
  203. package/dist/jobs/tag-asset.js +266 -0
  204. package/dist/jobs/task-failure.js +16 -0
  205. package/dist/jobs/timewarp-video.js +152 -0
  206. package/dist/jobs/transcribe-video-audio.js +117 -0
  207. package/dist/jobs/transcribe.js +35 -0
  208. package/dist/jobs/translate-transcript.js +55 -0
  209. package/dist/jobs/trim-audio.js +133 -0
  210. package/dist/jobs/trim-video.js +291 -0
  211. package/dist/jobs/xai-paygate.js +26 -0
  212. package/dist/keyword-placement-cache.js +74 -0
  213. package/dist/lazy-render.js +142 -0
  214. package/dist/library-browse.js +78 -0
  215. package/dist/lightning-defaults.js +206 -0
  216. package/dist/load-env.js +12 -0
  217. package/dist/local-search-models.js +206 -0
  218. package/dist/local-upload.js +318 -0
  219. package/dist/logger.js +69 -0
  220. package/dist/media/adapters/alibaba.js +20 -0
  221. package/dist/media/adapters/elevenlabs.js +58 -0
  222. package/dist/media/adapters/fal.js +45 -0
  223. package/dist/media/adapters/index.js +26 -0
  224. package/dist/media/adapters/kie.js +120 -0
  225. package/dist/media/adapters/pending.js +21 -0
  226. package/dist/media/adapters/types.js +5 -0
  227. package/dist/media/adapters/xai.js +18 -0
  228. package/dist/media/catalog.js +236 -0
  229. package/dist/media/complete.js +191 -0
  230. package/dist/media/cost.js +45 -0
  231. package/dist/media/descriptor.js +157 -0
  232. package/dist/media/endpoints.js +11 -0
  233. package/dist/media/frame-options.js +18 -0
  234. package/dist/media/insert-video-timing.js +17 -0
  235. package/dist/media/json-schema.js +75 -0
  236. package/dist/media/legacy-task-types.js +129 -0
  237. package/dist/media/listing.js +24 -0
  238. package/dist/media/model-id.js +14 -0
  239. package/dist/media/preview.js +86 -0
  240. package/dist/media/prompt-dialects.js +65 -0
  241. package/dist/media/ref-fields.js +106 -0
  242. package/dist/media/refs.js +131 -0
  243. package/dist/media/request.js +188 -0
  244. package/dist/media/types.js +6 -0
  245. package/dist/media/wire.js +161 -0
  246. package/dist/media-library.js +535 -0
  247. package/dist/media-output-path.js +33 -0
  248. package/dist/multyculty-workflow.js +757 -0
  249. package/dist/notebook-action-catalog.js +608 -0
  250. package/dist/notebook-cell-outputs.js +726 -0
  251. package/dist/notebook-credit-retry.js +45 -0
  252. package/dist/notebook-fixtures.js +8 -0
  253. package/dist/notebook-generation-contract.js +77 -0
  254. package/dist/notebook-generation-controls.js +145 -0
  255. package/dist/notebook-goal-layout.js +40 -0
  256. package/dist/notebook-graph.js +37 -0
  257. package/dist/notebook-image-edit.js +84 -0
  258. package/dist/notebook-initial-generation-prompt.js +222 -0
  259. package/dist/notebook-live-analysis.js +941 -0
  260. package/dist/notebook-media-ops.js +579 -0
  261. package/dist/notebook-mutations.js +20 -0
  262. package/dist/notebook-placement.js +41 -0
  263. package/dist/notebook-provider-failure.js +87 -0
  264. package/dist/notebook-published-post.js +26 -0
  265. package/dist/notebook-reference-resolver.js +227 -0
  266. package/dist/notebook-relayout.js +270 -0
  267. package/dist/notebook-run-plan.js +519 -0
  268. package/dist/notebook-shots.js +162 -0
  269. package/dist/notebook-workflow.js +856 -0
  270. package/dist/one-password.js +200 -0
  271. package/dist/openai-intelligence.js +99 -0
  272. package/dist/original-media-file.js +12 -0
  273. package/dist/osg-assets.js +36 -0
  274. package/dist/osg-placement-cache.js +64 -0
  275. package/dist/osg-settings.js +27 -0
  276. package/dist/paid-tools.js +22 -0
  277. package/dist/platform-guidance.js +26 -0
  278. package/dist/posts/dropbox-auth.js +91 -0
  279. package/dist/posts/dropbox-upload.js +132 -0
  280. package/dist/posts/icloud-drive.js +134 -0
  281. package/dist/posts/icloud-status.js +64 -0
  282. package/dist/posts/post-contract.js +2 -0
  283. package/dist/posts/post-copy-format.js +43 -0
  284. package/dist/posts/post-copy.js +193 -0
  285. package/dist/posts/post-folder.js +18 -0
  286. package/dist/posts/post-metrics.js +119 -0
  287. package/dist/posts/post-store.js +340 -0
  288. package/dist/posts/x-post-target.js +43 -0
  289. package/dist/preview-payload.js +90 -0
  290. package/dist/prompt-surfaces.js +22 -0
  291. package/dist/provider-prompt-transform.js +77 -0
  292. package/dist/provider-status-link.js +21 -0
  293. package/dist/provider-task-record.js +78 -0
  294. package/dist/provider-task-status.js +47 -0
  295. package/dist/provider-usage.js +294 -0
  296. package/dist/queue.js +810 -0
  297. package/dist/reference-video-duration.js +17 -0
  298. package/dist/remotion/BlurFillVideo.js +51 -0
  299. package/dist/remotion/KeywordGraphicsOverlay.js +68 -0
  300. package/dist/remotion/OsgOverlay.js +30 -0
  301. package/dist/remotion/QrOutroComposition.js +47 -0
  302. package/dist/remotion/RenderComposition.js +11 -0
  303. package/dist/remotion/browser.js +59 -0
  304. package/dist/remotion/constants.js +35 -0
  305. package/dist/remotion/index.js +66 -0
  306. package/dist/remotion/keyword-graphics.js +151 -0
  307. package/dist/remotion/keyword-placement.js +182 -0
  308. package/dist/remotion/osg-spec.js +198 -0
  309. package/dist/remotion/public/qr-outro-impact.wav +0 -0
  310. package/dist/remotion/qr-outro-audio.js +1 -0
  311. package/dist/remotion/qr-outro-spec.js +211 -0
  312. package/dist/remotion/shared-bundle.js +37 -0
  313. package/dist/remotion/subtitles/AliveCaptions.js +83 -0
  314. package/dist/remotion/subtitles/Highlight.js +58 -0
  315. package/dist/remotion/subtitles/Impact.js +67 -0
  316. package/dist/remotion/subtitles/SubtitleOverlay.js +9 -0
  317. package/dist/remotion/subtitles/alive-layout.js +321 -0
  318. package/dist/remotion/subtitles/alive-types.js +17 -0
  319. package/dist/remotion/subtitles/chunker.js +68 -0
  320. package/dist/remotion/subtitles/segments.js +42 -0
  321. package/dist/remotion/subtitles/styles.js +4 -0
  322. package/dist/remotion/subtitles/types.js +1 -0
  323. package/dist/remotion/util.js +18 -0
  324. package/dist/remotion-cancellation.js +15 -0
  325. package/dist/rename-helpers.js +204 -0
  326. package/dist/rhythm/analyze.js +128 -0
  327. package/dist/rhythm/contract.js +14 -0
  328. package/dist/rhythm/draft-model.js +19 -0
  329. package/dist/rhythm/energy.js +133 -0
  330. package/dist/rhythm/fft.js +44 -0
  331. package/dist/rhythm/model.js +71 -0
  332. package/dist/rhythm/music-analysis.js +124 -0
  333. package/dist/rhythm/music-draft.js +261 -0
  334. package/dist/rhythm/onset.js +101 -0
  335. package/dist/rhythm/run-worker.js +51 -0
  336. package/dist/rhythm/score.js +47 -0
  337. package/dist/rhythm/section-labels.js +168 -0
  338. package/dist/rhythm/sequence-rhythm.js +67 -0
  339. package/dist/rhythm/synthesize.js +103 -0
  340. package/dist/rhythm/timing.js +98 -0
  341. package/dist/rhythm/track.js +320 -0
  342. package/dist/rhythm/visual-fit.js +4 -0
  343. package/dist/rhythm/worker.js +25 -0
  344. package/dist/scene-detect.js +200 -0
  345. package/dist/scene-transition.js +12 -0
  346. package/dist/scheduled-posts.js +200 -0
  347. package/dist/search-index-coverage.js +13 -0
  348. package/dist/search-index-media.js +71 -0
  349. package/dist/search-reference-store.js +60 -0
  350. package/dist/search-scoring.js +17 -0
  351. package/dist/serve.js +125 -0
  352. package/dist/server-lock.js +143 -0
  353. package/dist/server.js +42 -0
  354. package/dist/srt.js +16 -0
  355. package/dist/tag-backfill-state.js +121 -0
  356. package/dist/tag-settings.js +30 -0
  357. package/dist/tag-vocabulary.js +237 -0
  358. package/dist/test-queue-cleanup.js +1 -0
  359. package/dist/time-parse.js +50 -0
  360. package/dist/timeline-helpers.js +554 -0
  361. package/dist/timewarp.js +87 -0
  362. package/dist/tools/_image-inputs.js +9 -0
  363. package/dist/tools/_media-refs.js +84 -0
  364. package/dist/tools/_shared.js +59 -0
  365. package/dist/tools/_suggest-shared.js +279 -0
  366. package/dist/tools/add-timeline-audio.js +73 -0
  367. package/dist/tools/alive-captions.js +157 -0
  368. package/dist/tools/analyze-jokes.js +56 -0
  369. package/dist/tools/analyze-video.js +66 -0
  370. package/dist/tools/animate-keywords.js +217 -0
  371. package/dist/tools/api-status.js +42 -0
  372. package/dist/tools/application-commands.js +34 -0
  373. package/dist/tools/apply-cuts.js +112 -0
  374. package/dist/tools/apply-sfx.js +152 -0
  375. package/dist/tools/apply-watermark.js +670 -0
  376. package/dist/tools/archive-clip.js +74 -0
  377. package/dist/tools/asset-history.js +61 -0
  378. package/dist/tools/asset-manifest.js +24 -0
  379. package/dist/tools/asset-tags.js +151 -0
  380. package/dist/tools/assign-slot.js +97 -0
  381. package/dist/tools/audio-boost.js +107 -0
  382. package/dist/tools/audio-mix.js +121 -0
  383. package/dist/tools/book-usage.js +16 -0
  384. package/dist/tools/chain.js +136 -0
  385. package/dist/tools/change-video-speed.js +105 -0
  386. package/dist/tools/chat-history.js +5 -0
  387. package/dist/tools/chat.js +848 -0
  388. package/dist/tools/clean-video-audio.js +97 -0
  389. package/dist/tools/clear-failed-tasks.js +66 -0
  390. package/dist/tools/cold-open.js +114 -0
  391. package/dist/tools/command-plan.js +1680 -0
  392. package/dist/tools/commands.js +1666 -0
  393. package/dist/tools/concat-images.js +236 -0
  394. package/dist/tools/connect-service.js +55 -0
  395. package/dist/tools/copy-file-to-clipboard.js +58 -0
  396. package/dist/tools/create-character.js +114 -0
  397. package/dist/tools/create-qart.js +253 -0
  398. package/dist/tools/create-qr-outro.js +149 -0
  399. package/dist/tools/create-qrbtf.js +215 -0
  400. package/dist/tools/create-watermark.js +97 -0
  401. package/dist/tools/crop-asset.js +18 -0
  402. package/dist/tools/crop-image.js +120 -0
  403. package/dist/tools/crop-video.js +97 -0
  404. package/dist/tools/delete-asset.js +104 -0
  405. package/dist/tools/describe-image.js +69 -0
  406. package/dist/tools/detect-scenes.js +44 -0
  407. package/dist/tools/doctor.js +126 -0
  408. package/dist/tools/download-video.js +114 -0
  409. package/dist/tools/duplicate-asset.js +184 -0
  410. package/dist/tools/edit-timeline.js +59 -0
  411. package/dist/tools/editing-moments.js +105 -0
  412. package/dist/tools/estimate-cost.js +171 -0
  413. package/dist/tools/even-split.js +295 -0
  414. package/dist/tools/export-asset-file.js +22 -0
  415. package/dist/tools/export-asset.js +140 -0
  416. package/dist/tools/export-sequence.js +250 -0
  417. package/dist/tools/export-to-downloads.js +54 -0
  418. package/dist/tools/export-transcript-srt.js +79 -0
  419. package/dist/tools/extend-audio.js +100 -0
  420. package/dist/tools/extract-audio.js +174 -0
  421. package/dist/tools/extract-frame.js +108 -0
  422. package/dist/tools/extract-notebook-audio.js +173 -0
  423. package/dist/tools/extract-notebook-frame.js +160 -0
  424. package/dist/tools/extract-video-clip.js +101 -0
  425. package/dist/tools/generate-media.js +157 -0
  426. package/dist/tools/generation-payload.js +48 -0
  427. package/dist/tools/get-timeline.js +12 -0
  428. package/dist/tools/get-watermark-config.js +38 -0
  429. package/dist/tools/goal.js +644 -0
  430. package/dist/tools/image-subjects.js +711 -0
  431. package/dist/tools/index.js +323 -0
  432. package/dist/tools/inject-first-frame.js +154 -0
  433. package/dist/tools/invert-image.js +70 -0
  434. package/dist/tools/isolate-vocals.js +66 -0
  435. package/dist/tools/list-assets.js +98 -0
  436. package/dist/tools/list-cell-generations.js +225 -0
  437. package/dist/tools/list-elevenlabs-voices.js +163 -0
  438. package/dist/tools/log-wrapper.js +29 -0
  439. package/dist/tools/make-cover-song.js +95 -0
  440. package/dist/tools/mark-asset-seen.js +23 -0
  441. package/dist/tools/media-library.js +245 -0
  442. package/dist/tools/move-timeline-audio.js +35 -0
  443. package/dist/tools/music-analysis.js +60 -0
  444. package/dist/tools/music-draft.js +29 -0
  445. package/dist/tools/pad-video.js +87 -0
  446. package/dist/tools/post-record.js +83 -0
  447. package/dist/tools/post.js +168 -0
  448. package/dist/tools/prompt-history.js +164 -0
  449. package/dist/tools/provider-key-status.js +248 -0
  450. package/dist/tools/qrbtf-render.js +849 -0
  451. package/dist/tools/read-asset-metadata.js +26 -0
  452. package/dist/tools/read-file-at-commit.js +29 -0
  453. package/dist/tools/read-transcript.js +41 -0
  454. package/dist/tools/reconcile-project-work.js +1452 -0
  455. package/dist/tools/recover-asset.js +28 -0
  456. package/dist/tools/remove-sound.js +72 -0
  457. package/dist/tools/remove-timeline-audio.js +34 -0
  458. package/dist/tools/remove-timeline-slot.js +40 -0
  459. package/dist/tools/rename-all.js +434 -0
  460. package/dist/tools/rename-asset.js +34 -0
  461. package/dist/tools/render-settings.js +47 -0
  462. package/dist/tools/render-timeline.js +120 -0
  463. package/dist/tools/render.js +384 -0
  464. package/dist/tools/reorder-character-images.js +45 -0
  465. package/dist/tools/replace-video-audio.js +133 -0
  466. package/dist/tools/restore-archive-clips.js +209 -0
  467. package/dist/tools/restore-asset-version.js +37 -0
  468. package/dist/tools/retry-generation.js +54 -0
  469. package/dist/tools/reverse-video.js +74 -0
  470. package/dist/tools/rewrite-prompt.js +388 -0
  471. package/dist/tools/rewrite-script.js +67 -0
  472. package/dist/tools/schema-from-apicity.js +66 -0
  473. package/dist/tools/search-archive.js +150 -0
  474. package/dist/tools/semantic-search.js +1214 -0
  475. package/dist/tools/sequence-rhythm.js +43 -0
  476. package/dist/tools/set-character-metadata.js +84 -0
  477. package/dist/tools/set-env-secret.js +40 -0
  478. package/dist/tools/set-osg.js +143 -0
  479. package/dist/tools/show-folder.js +37 -0
  480. package/dist/tools/smooth-scene-concat.js +122 -0
  481. package/dist/tools/splice-video.js +149 -0
  482. package/dist/tools/split-video.js +202 -0
  483. package/dist/tools/suggest-cuts.js +45 -0
  484. package/dist/tools/suggest-sfx.js +43 -0
  485. package/dist/tools/sync-video-to-audio.js +115 -0
  486. package/dist/tools/tag-backfill.js +177 -0
  487. package/dist/tools/timewarp-video.js +85 -0
  488. package/dist/tools/tool-def.js +1 -0
  489. package/dist/tools/tool-json-schema.js +116 -0
  490. package/dist/tools/transcribe.js +90 -0
  491. package/dist/tools/translate-subtitles.js +59 -0
  492. package/dist/tools/trim-audio.js +139 -0
  493. package/dist/tools/trim-video.js +168 -0
  494. package/dist/tools/update-script.js +25 -0
  495. package/dist/tools/update-timeline-audio.js +40 -0
  496. package/dist/tools/upload-file.js +41 -0
  497. package/dist/tools/verify-x-mentions.js +141 -0
  498. package/dist/tools/video-editing-command-contract.js +291 -0
  499. package/dist/tools/video-source-reference.js +30 -0
  500. package/dist/tools/video-time-input.js +18 -0
  501. package/dist/tools/videobook-domain.js +2021 -0
  502. package/dist/tools/wait-for-asset-ready.js +143 -0
  503. package/dist/tools/workspace-actions.js +54 -0
  504. package/dist/transcript-projection.js +202 -0
  505. package/dist/transcript-srt.js +33 -0
  506. package/dist/upload.js +76 -0
  507. package/dist/usage-providers.js +25 -0
  508. package/dist/video-analysis-projection.js +46 -0
  509. package/dist/video-poster.js +33 -0
  510. package/dist/video-proxy.js +99 -0
  511. package/dist/vision-regions.js +111 -0
  512. package/dist/voice-actions.js +90 -0
  513. package/dist/voice-audio.js +55 -0
  514. package/dist/voice-config.js +122 -0
  515. package/dist/voice-continuation.js +54 -0
  516. package/dist/voice-contract.js +1 -0
  517. package/dist/voice-http.js +145 -0
  518. package/dist/voice-reply-progress.js +55 -0
  519. package/dist/voice-session.js +992 -0
  520. package/dist/watermark-badge.js +259 -0
  521. package/dist/watermark-config.js +111 -0
  522. package/dist/watermark-handle-position.js +55 -0
  523. package/dist/watermark-platform.js +15 -0
  524. package/dist/watermark-timing.js +16 -0
  525. package/dist/workspace-action-catalog.js +315 -0
  526. package/dist/workspace-action-contract.js +21 -0
  527. package/dist/workspace-action-groups.js +98 -0
  528. package/dist/workspace-action-surfaces.js +7 -0
  529. package/dist/x/account.js +88 -0
  530. package/dist/x/authenticated-handle.js +30 -0
  531. package/dist/x/oauth-flow.js +191 -0
  532. package/dist/x/post-contract.js +1 -0
  533. package/dist/x/post-metadata.js +44 -0
  534. package/dist/x/post-to-x.js +456 -0
  535. package/dist/x/published-post-cell.js +90 -0
  536. package/dist/x/x-tokens.js +69 -0
  537. package/dist/ytdlp-binary.js +65 -0
  538. package/dist/zod-slots.js +334 -0
  539. package/package.json +84 -4
  540. package/ui/assets/alive-captions-BWb5nLKj.css +1 -0
  541. package/ui/assets/alive-captions-DPEr1nKM.js +1 -0
  542. package/ui/assets/confirm-bodies-D8ouHXKy.js +1 -0
  543. package/ui/assets/confirm-bodies-DrMhaXC6.css +1 -0
  544. package/ui/assets/fullscreen-B7nw4YbE.js +1 -0
  545. package/ui/assets/goal-ui-DK-xTCK0.js +2 -0
  546. package/ui/assets/goal-ui-DXdUM2KF.css +1 -0
  547. package/ui/assets/index-DzPAnCUj.css +1 -0
  548. package/ui/assets/index-kS1905s5.js +37 -0
  549. package/ui/assets/inspector-MlzQbTVo.js +1 -0
  550. package/ui/assets/jobs-ui-CoinOWx0.js +1 -0
  551. package/ui/assets/music-video-Cbe80kmQ.js +1 -0
  552. package/ui/assets/music-video-DPaiKKEq.css +1 -0
  553. package/ui/assets/ui-Oirr9VOf.css +1 -0
  554. package/ui/assets/ui-xo_Y1yQg.js +2 -0
  555. package/ui/assets/vendor-D2rfiZ5j.css +1 -0
  556. package/ui/assets/vendor-DfTCWgKm.js +19 -0
  557. package/ui/assets/vendor-flow-BXbeWxW7.js +7 -0
  558. package/ui/assets/vendor-flow-BnuhLJ6X.css +1 -0
  559. package/ui/assets/vendor-icons-CGqhCIXV.js +1 -0
  560. package/ui/assets/vendor-mcp-BTlEJcHV.js +67 -0
  561. package/ui/assets/vendor-react-Dj7D1fD4.js +9 -0
  562. package/ui/assets/vendor-remotion-CH1HXrlc.js +95 -0
  563. package/ui/assets/videobook-Qlp9HQFu.js +20 -0
  564. package/ui/favicon.svg +8 -0
  565. package/ui/index.html +126 -0
@@ -0,0 +1,143 @@
1
+ import { z } from "zod";
2
+ import { reconcileProjectWorkDef } from "./reconcile-project-work.js";
3
+ function delay(ms) {
4
+ return new Promise((resolve) => setTimeout(resolve, ms));
5
+ }
6
+ export function waitForAssetReadyDef(engine) {
7
+ return {
8
+ name: "wait_for_asset_ready",
9
+ description: "Wait until an asset's asynchronous generation, reconciliation, and file writes finish or the timeout expires.",
10
+ schema: {
11
+ asset_id: z.string().describe("Asset ID to wait on"),
12
+ book_id: z.string().describe("Book id"),
13
+ timeout_seconds: z
14
+ .number()
15
+ .default(300)
16
+ .describe("Max seconds to wait before returning a timeout error"),
17
+ },
18
+ handler: async (args) => {
19
+ const { asset_id, book_id, timeout_seconds, } = args;
20
+ // Verify asset exists
21
+ const manifestResult = await engine.files.manifest(asset_id);
22
+ if (!manifestResult.ok) {
23
+ return {
24
+ isError: true,
25
+ content: [
26
+ {
27
+ type: "text",
28
+ text: JSON.stringify({
29
+ asset_id,
30
+ status: "not_found",
31
+ error: "Asset not found",
32
+ }),
33
+ },
34
+ ],
35
+ };
36
+ }
37
+ const assetDir = manifestResult.value.path;
38
+ const reconcileHandler = reconcileProjectWorkDef(engine).handler;
39
+ const deadline = Date.now() + timeout_seconds * 1000;
40
+ const startTime = Date.now();
41
+ let iterations = 0;
42
+ let lastReconciliation = null;
43
+ while (Date.now() < deadline) {
44
+ iterations++;
45
+ // Drive state machine forward
46
+ const reconcileResult = await reconcileHandler({ book_id });
47
+ const reconcileData = JSON.parse(reconcileResult.content[0].text);
48
+ lastReconciliation = reconcileData;
49
+ // Check for failure
50
+ const failure = reconcileData.failed.find((f) => f.assetId === asset_id);
51
+ if (failure) {
52
+ return {
53
+ isError: true,
54
+ content: [
55
+ {
56
+ type: "text",
57
+ text: JSON.stringify({
58
+ asset_id,
59
+ status: "failed",
60
+ elapsed_seconds: Math.round((Date.now() - startTime) / 1000),
61
+ iterations,
62
+ error: failure.failMsg,
63
+ last_sync: lastReconciliation,
64
+ }),
65
+ },
66
+ ],
67
+ };
68
+ }
69
+ const jobs = engine.jobs.queue.list({
70
+ artifactId: asset_id,
71
+ limit: 20,
72
+ });
73
+ const failedJob = jobs.find((job) => job.state === "failed");
74
+ if (failedJob) {
75
+ return {
76
+ isError: true,
77
+ content: [
78
+ {
79
+ type: "text",
80
+ text: JSON.stringify({
81
+ asset_id,
82
+ status: "failed",
83
+ elapsed_seconds: Math.round((Date.now() - startTime) / 1000),
84
+ iterations,
85
+ error: failedJob.error?.message ?? "Asset job failed",
86
+ last_sync: lastReconciliation,
87
+ }),
88
+ },
89
+ ],
90
+ };
91
+ }
92
+ const activeJob = jobs.some((job) => job.state === "queued" ||
93
+ job.state === "running" ||
94
+ job.state === "completing");
95
+ const ptRead = await engine.jobs.pending.read(asset_id);
96
+ const pendingTask = ptRead.ok ? ptRead.value : null;
97
+ const locked = await engine.jobs.locks.isLocked(assetDir);
98
+ const workRead = await engine.jobs.artifactWork.read(asset_id);
99
+ const activeWork = workRead.ok &&
100
+ workRead.value !== null &&
101
+ (workRead.value.status === "working" ||
102
+ (workRead.value.status === "pending" &&
103
+ typeof workRead.value.meta.kind === "string"));
104
+ if (pendingTask === null &&
105
+ !locked &&
106
+ !activeJob &&
107
+ !activeWork) {
108
+ return {
109
+ content: [
110
+ {
111
+ type: "text",
112
+ text: JSON.stringify({
113
+ asset_id,
114
+ status: "ready",
115
+ elapsed_seconds: Math.round((Date.now() - startTime) / 1000),
116
+ iterations,
117
+ last_sync: lastReconciliation,
118
+ }),
119
+ },
120
+ ],
121
+ };
122
+ }
123
+ await delay(Math.min(5000, Math.max(1, deadline - Date.now())));
124
+ }
125
+ // Timeout
126
+ return {
127
+ isError: true,
128
+ content: [
129
+ {
130
+ type: "text",
131
+ text: JSON.stringify({
132
+ asset_id,
133
+ status: "timeout",
134
+ elapsed_seconds: Math.round((Date.now() - startTime) / 1000),
135
+ iterations,
136
+ last_sync: lastReconciliation,
137
+ }),
138
+ },
139
+ ],
140
+ };
141
+ },
142
+ };
143
+ }
@@ -0,0 +1,54 @@
1
+ import { z } from "zod";
2
+ import { buildWorkspaceActionCatalog } from "../workspace-action-catalog.js";
3
+ import { WORKSPACE_TOOL_GROUPS } from "../workspace-action-groups.js";
4
+ import { getCanonicalCommands } from "./commands.js";
5
+ const ACTION_GROUPS = Object.keys(WORKSPACE_TOOL_GROUPS);
6
+ function handlerName(action) {
7
+ switch (action.handler.kind) {
8
+ case "tool": return `tool:${action.handler.tool}`;
9
+ case "ui": return `ui:${action.handler.action}`;
10
+ case "command": return `command:${action.handler.command}`;
11
+ }
12
+ }
13
+ function actionSummary(action) {
14
+ return {
15
+ id: action.id,
16
+ command: action.command,
17
+ title: action.title,
18
+ group: action.group,
19
+ runs: handlerName(action),
20
+ };
21
+ }
22
+ function matchesQuery(action, terms) {
23
+ const text = [action.title, action.command, action.description, action.group, ...action.aliases, ...action.keywords]
24
+ .join(" ").toLowerCase();
25
+ return terms.every((term) => text.includes(term));
26
+ }
27
+ function selectActions(actions, selection) {
28
+ const terms = (selection.query ?? "").trim().toLowerCase().replace(/^\//u, "").split(/\s+/u).filter(Boolean);
29
+ const ids = selection.ids ? new Set(selection.ids) : null;
30
+ return actions.filter((action) => (!ids || ids.has(action.id))
31
+ && (!selection.group || action.group === selection.group)
32
+ && matchesQuery(action, terms));
33
+ }
34
+ export function workspaceActionsDef(getTools) {
35
+ return {
36
+ name: "list_workspace_actions",
37
+ description: "List workspace actions (tools, app screens and composite commands) as id, command, title, group and what each runs. Filter with query, group or ids; detail full adds typed inputs, surfaces, confirmation policy and tool coverage.",
38
+ scope: "global",
39
+ schema: {
40
+ query: z.string().optional().describe("Words that must all appear in an action's title, command, description or keywords"),
41
+ group: z.enum(ACTION_GROUPS).optional(),
42
+ ids: z.array(z.string()).optional().describe("Action ids such as tool.trim_video or command.goal"),
43
+ detail: z.enum(["summary", "full"]).optional(),
44
+ },
45
+ handler: async (args) => {
46
+ const catalog = buildWorkspaceActionCatalog(getTools(), getCanonicalCommands());
47
+ const actions = selectActions(catalog.actions, args);
48
+ const payload = args.detail === "full"
49
+ ? { actions, tools: catalog.tools }
50
+ : { actions: actions.map(actionSummary) };
51
+ return { content: [{ type: "text", text: JSON.stringify(payload) }] };
52
+ },
53
+ };
54
+ }
@@ -0,0 +1,202 @@
1
+ import { assessDialogQuality } from "./dialog-quality.js";
2
+ /** Artifact kinds whose media can carry speech worth transcribing. */
3
+ export function hasSpokenAudio(kind) {
4
+ return kind === "video" || kind === "audio" || kind === "final";
5
+ }
6
+ /** Word-level ElevenLabs speech-to-text output written by the transcribe job. */
7
+ export const TIMED_TRANSCRIPT_FILE = "elevenlabs.json";
8
+ /** Plain-text transcription sidecar written during ingest. */
9
+ export const PLAIN_TRANSCRIPT_FILE = ".analysis.gpt4otrans.json";
10
+ function parseJson(value) {
11
+ try {
12
+ return JSON.parse(value.toString());
13
+ }
14
+ catch {
15
+ return null;
16
+ }
17
+ }
18
+ function firstChannelTranscript(raw) {
19
+ if (typeof raw !== "object" || raw === null)
20
+ return null;
21
+ const obj = raw;
22
+ if (typeof obj.words !== "undefined")
23
+ return obj;
24
+ const channels = obj.transcripts;
25
+ if (Array.isArray(channels) && channels.length > 0)
26
+ return channels[0];
27
+ return null;
28
+ }
29
+ function isTimedWord(value) {
30
+ if (typeof value !== "object" || value === null)
31
+ return false;
32
+ const word = value;
33
+ return typeof word.text === "string"
34
+ && typeof word.start === "number"
35
+ && typeof word.end === "number";
36
+ }
37
+ /** Audio events ("[laughter]") and spacing entries are not spoken words. */
38
+ function isSpeech(word) {
39
+ return word.type !== "audio_event" && word.type !== "spacing";
40
+ }
41
+ export function timedTranscriptFrom(raw) {
42
+ const transcript = firstChannelTranscript(raw);
43
+ if (!transcript || !Array.isArray(transcript.words))
44
+ return null;
45
+ const speech = transcript.words.filter(isTimedWord).filter(isSpeech);
46
+ if (speech.length === 0)
47
+ return { kind: "silence" };
48
+ const textEntries = transcript.words.filter(isTimedWord).filter((word) => word.type !== "audio_event");
49
+ const reconstructedText = textEntries.some((word) => word.type === "spacing")
50
+ ? textEntries.map((word) => word.text).join("").replace(/\s+/gu, " ").trim()
51
+ : speech.map((word) => word.text.trim()).join(" ");
52
+ return {
53
+ kind: "timed",
54
+ languageCode: transcript.language_code ?? null,
55
+ text: transcript.text ?? reconstructedText,
56
+ words: speech.map((word) => ({
57
+ text: word.text,
58
+ start: word.start,
59
+ end: word.end,
60
+ speakerId: word.speaker_id ?? null,
61
+ })),
62
+ };
63
+ }
64
+ function plainTranscriptFrom(raw) {
65
+ if (typeof raw !== "object" || raw === null)
66
+ return null;
67
+ const quality = assessDialogQuality(raw);
68
+ if (!quality.hasDialog && (quality.reason === "empty" || quality.reason === "noise_only")) {
69
+ return { kind: "silence" };
70
+ }
71
+ return { kind: "plain", text: quality.text };
72
+ }
73
+ function sidecarFile(source) {
74
+ return source === "elevenlabs" ? TIMED_TRANSCRIPT_FILE : PLAIN_TRANSCRIPT_FILE;
75
+ }
76
+ async function readTimedTranscript(engine, assetId) {
77
+ const timedRead = await engine.files.read(assetId, TIMED_TRANSCRIPT_FILE);
78
+ if (!timedRead.ok)
79
+ return null;
80
+ return timedTranscriptFrom(parseJson(timedRead.value));
81
+ }
82
+ async function readPlainTranscript(engine, assetId) {
83
+ const plainRead = await engine.files.read(assetId, PLAIN_TRANSCRIPT_FILE);
84
+ if (!plainRead.ok)
85
+ return null;
86
+ return plainTranscriptFrom(parseJson(plainRead.value));
87
+ }
88
+ async function readAssetTranscriptWithSource(engine, assetId) {
89
+ const timed = await readTimedTranscript(engine, assetId);
90
+ if (timed)
91
+ return { transcript: timed, source: "timed" };
92
+ const plain = await readPlainTranscript(engine, assetId);
93
+ if (plain)
94
+ return { transcript: plain, source: "plain" };
95
+ return { transcript: { kind: "missing" }, source: "none" };
96
+ }
97
+ /**
98
+ * Resolve the best transcript for an artifact: word-timed ElevenLabs cues when
99
+ * present, otherwise the plain ingest transcription.
100
+ */
101
+ export async function readAssetTranscript(engine, assetId) {
102
+ return (await readAssetTranscriptWithSource(engine, assetId)).transcript;
103
+ }
104
+ function countWords(text) {
105
+ const stripped = text.replace(/\[[^\]]*\]/g, "").trim();
106
+ return stripped.length === 0 ? 0 : stripped.split(/\s+/).length;
107
+ }
108
+ function transcriptSummary(transcript) {
109
+ if (transcript.kind === "missing")
110
+ return null;
111
+ if (transcript.kind === "silence") {
112
+ return { kind: "silence", language_code: null, text: "", word_count: 0 };
113
+ }
114
+ if (transcript.kind === "plain") {
115
+ return {
116
+ kind: "plain",
117
+ language_code: null,
118
+ text: transcript.text,
119
+ word_count: countWords(transcript.text),
120
+ };
121
+ }
122
+ return {
123
+ kind: "timed",
124
+ language_code: transcript.languageCode,
125
+ text: transcript.text,
126
+ word_count: transcript.words.length,
127
+ };
128
+ }
129
+ function transcriptSourceSummary(transcript) {
130
+ const summary = transcriptSummary(transcript);
131
+ if (!summary)
132
+ return null;
133
+ if (transcript.kind !== "timed")
134
+ return summary;
135
+ return {
136
+ ...summary,
137
+ words: transcript.words.map((word) => ({
138
+ text: word.text,
139
+ start: word.start,
140
+ end: word.end,
141
+ })),
142
+ };
143
+ }
144
+ /** Word-timed transcripts are large; asset listings re-read them constantly.
145
+ * Their content-addressed hash makes an in-process memo safe and exact. */
146
+ const SUMMARY_CACHE_LIMIT = 512;
147
+ const summaryByObjectHash = new Map();
148
+ function rememberSummary(objectHash, summary) {
149
+ summaryByObjectHash.delete(objectHash);
150
+ summaryByObjectHash.set(objectHash, summary);
151
+ while (summaryByObjectHash.size > SUMMARY_CACHE_LIMIT) {
152
+ const oldest = summaryByObjectHash.keys().next().value;
153
+ if (typeof oldest !== "string")
154
+ return;
155
+ summaryByObjectHash.delete(oldest);
156
+ }
157
+ }
158
+ async function readTimedSource(engine, assetId, timedHash) {
159
+ if (timedHash) {
160
+ const cached = summaryByObjectHash.get(timedHash);
161
+ if (cached)
162
+ return cached;
163
+ }
164
+ const timed = await readTimedTranscript(engine, assetId);
165
+ if (!timed)
166
+ return null;
167
+ const summary = transcriptSourceSummary(timed);
168
+ if (timedHash && summary)
169
+ rememberSummary(timedHash, summary);
170
+ return summary;
171
+ }
172
+ /** Both transcript sidecars, independently, or null when neither exists.
173
+ * Pass the artifact's manifest files to skip missing sidecars and reuse the
174
+ * cached projection of an unchanged word-timed transcript. */
175
+ export async function readAssetTranscriptSources(engine, assetId, files) {
176
+ const timedFile = files?.find((file) => file.name === TIMED_TRANSCRIPT_FILE);
177
+ const plainFile = files?.find((file) => file.name === PLAIN_TRANSCRIPT_FILE);
178
+ if (files && !timedFile && !plainFile)
179
+ return null;
180
+ const elevenlabs = !files || timedFile
181
+ ? await readTimedSource(engine, assetId, timedFile?.objectHash ?? null)
182
+ : null;
183
+ const plain = !files || plainFile
184
+ ? transcriptSourceSummary(await readPlainTranscript(engine, assetId) ?? { kind: "missing" })
185
+ : null;
186
+ if (!elevenlabs && !plain)
187
+ return null;
188
+ return { elevenlabs, plain };
189
+ }
190
+ export async function readTranscriptSidecarText(engine, assetId, source) {
191
+ const order = source
192
+ ? [source]
193
+ : ["elevenlabs", "plain"];
194
+ for (const kind of order) {
195
+ const file = sidecarFile(kind);
196
+ const read = await engine.files.read(assetId, file);
197
+ if (!read.ok)
198
+ continue;
199
+ return { source: kind, file, text: read.value.toString() };
200
+ }
201
+ return null;
202
+ }
@@ -0,0 +1,33 @@
1
+ import { chunkSegments } from "./remotion/subtitles/chunker.js";
2
+ import { parseSegments } from "./remotion/subtitles/segments.js";
3
+ import { srtContent } from "./srt.js";
4
+ function asElevenLabsJson(raw) {
5
+ if (typeof raw !== "object" || raw === null) {
6
+ throw new Error("ElevenLabs transcript is not valid JSON");
7
+ }
8
+ return raw;
9
+ }
10
+ function monotonicCues(chunks) {
11
+ const cues = [];
12
+ let lastEnd = 0;
13
+ for (const chunk of chunks) {
14
+ const text = chunk.text.trim();
15
+ if (!text)
16
+ continue;
17
+ const start = Math.max(chunk.start, lastEnd);
18
+ const end = Math.max(chunk.end, start);
19
+ const safeEnd = end === start ? start + 0.001 : end;
20
+ cues.push({ startSeconds: start, endSeconds: safeEnd, text });
21
+ lastEnd = safeEnd;
22
+ }
23
+ return cues;
24
+ }
25
+ export function transcriptToSrt(raw, options = {}) {
26
+ const segments = parseSegments(asElevenLabsJson(raw));
27
+ const chunks = chunkSegments(segments, options.orientation ?? "landscape", options.style ?? "impact");
28
+ const cues = monotonicCues(chunks);
29
+ if (cues.length === 0) {
30
+ throw new Error("No word-timed cues in the ElevenLabs transcript");
31
+ }
32
+ return srtContent(cues);
33
+ }
package/dist/upload.js ADDED
@@ -0,0 +1,76 @@
1
+ import { createFreeMediaUpload, uploadToAnyHost } from "@apicity/free-media-upload";
2
+ import { createHash } from "node:crypto";
3
+ import { maybeReencodeImage, maybeUpscaleSmallImage } from "./image-compress.js";
4
+ export function contentHash(data) {
5
+ return createHash("sha256").update(data).digest("hex");
6
+ }
7
+ const MIME_TYPES = {
8
+ jpg: "image/jpeg",
9
+ jpeg: "image/jpeg",
10
+ png: "image/png",
11
+ gif: "image/gif",
12
+ webp: "image/webp",
13
+ bmp: "image/bmp",
14
+ svg: "image/svg+xml",
15
+ mp4: "video/mp4",
16
+ webm: "video/webm",
17
+ mov: "video/quicktime",
18
+ avi: "video/x-msvideo",
19
+ mp3: "audio/mpeg",
20
+ wav: "audio/wav",
21
+ ogg: "audio/ogg",
22
+ flac: "audio/flac",
23
+ aac: "audio/aac",
24
+ m4a: "audio/mp4",
25
+ };
26
+ function inferMimeType(filename) {
27
+ const ext = filename.split(".").pop()?.toLowerCase();
28
+ return (ext && MIME_TYPES[ext]) ?? "application/octet-stream";
29
+ }
30
+ // Upload bytes to a free file host and return a publicly accessible download
31
+ // URL. Delegates rotation to @apicity/free-media-upload's `uploadToAnyHost` across
32
+ // litterbox/uguu/tflink.
33
+ //
34
+ // Host selection matters: Alibaba/Kie workers run in China and synchronously
35
+ // fetch the URL server-side during data inspection. Several otherwise-fine
36
+ // hosts (catbox.moe, filebin.net, temp.sh, tmpfiles.org) are unreachable or
37
+ // mis-typed from that side and cause InvalidParameter.DataInspection 400s
38
+ // even when the upload itself succeeded. The three hosts below were verified
39
+ // end-to-end (upload OK + Alibaba submit accepted).
40
+ export async function uploadBytesToFreeHost(data, filename) {
41
+ const mimeType = inferMimeType(filename);
42
+ const blob = new Blob([new Uint8Array(data)], { type: mimeType });
43
+ const file = new File([blob], filename, { type: mimeType });
44
+ return uploadToAnyHost(createFreeMediaUpload(), {
45
+ file,
46
+ filename,
47
+ hosts: ["litterbox", "uguu", "tflink"],
48
+ time: "1h",
49
+ });
50
+ }
51
+ async function prepareBytesForUpload(bytes, filename) {
52
+ const raw = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
53
+ const reencoded = await maybeReencodeImage(raw, filename);
54
+ const { data, filename: outName } = await maybeUpscaleSmallImage(reencoded.data, reencoded.filename);
55
+ const ext = outName.split(".").pop() ?? "bin";
56
+ const hashedFilename = `${contentHash(data)}.${ext}`;
57
+ const contentType = inferMimeType(outName);
58
+ return { data, hashedFilename, contentType };
59
+ }
60
+ // Read an asset, optionally re-encode it, and hash its final bytes for use as
61
+ // a collision-free remote filename. Shared preamble for every provider-native
62
+ // upload wrapper.
63
+ export async function prepareAssetForUpload(engine, assetId, filename) {
64
+ const readResult = await engine.files.read(assetId, filename);
65
+ if (!readResult.ok)
66
+ throw new Error(readResult.error.message);
67
+ return prepareBytesForUpload(new Uint8Array(readResult.value), filename);
68
+ }
69
+ // Upload an asset file via the free-host rotation. Kept for
70
+ // provider-agnostic callers (e.g. `transcribe.ts`) that don't target a
71
+ // specific AI provider. Provider-specific jobs should call the matching
72
+ // `uploadAssetTo{Fal,Kie,Alibaba}` wrapper instead.
73
+ export async function uploadAssetFile(engine, assetId, filename) {
74
+ const { data, hashedFilename } = await prepareAssetForUpload(engine, assetId, filename);
75
+ return uploadBytesToFreeHost(data, hashedFilename);
76
+ }
@@ -0,0 +1,25 @@
1
+ import { createOpenAi as openai } from "@apicity/openai";
2
+ import { createXai as xai } from "@apicity/xai";
3
+ import { createKie as kie } from "@apicity/kie";
4
+ import { createFal as fal } from "@apicity/fal";
5
+ import { createAlibaba as alibaba } from "@apicity/alibaba";
6
+ import { createElevenLabs as elevenlabs } from "@apicity/elevenlabs";
7
+ import { providerUsageFetch } from "./provider-usage.js";
8
+ export function createOpenAi(options) {
9
+ return openai({ ...options, fetch: providerUsageFetch("openai", options.fetch) });
10
+ }
11
+ export function createXai(options) {
12
+ return xai({ ...options, fetch: providerUsageFetch("xai", options.fetch) });
13
+ }
14
+ export function createKie(options) {
15
+ return kie({ ...options, fetch: providerUsageFetch("kie", options.fetch) });
16
+ }
17
+ export function createFal(options) {
18
+ return fal({ ...options, fetch: providerUsageFetch("fal", options.fetch) });
19
+ }
20
+ export function createAlibaba(options) {
21
+ return alibaba({ ...options, fetch: providerUsageFetch("alibaba", options.fetch) });
22
+ }
23
+ export function createElevenLabs(options) {
24
+ return elevenlabs({ ...options, fetch: providerUsageFetch("elevenlabs", options.fetch) });
25
+ }
@@ -0,0 +1,46 @@
1
+ /** Vision analysis sidecar written by the analyze_video job. */
2
+ export const VIDEO_ANALYSIS_FILE = ".original.analysis.json";
3
+ /** Artifact kinds the analyze_video job can describe from sampled frames. */
4
+ export function isAnalyzableVideo(kind) {
5
+ return kind === "video" || kind === "final";
6
+ }
7
+ function stringOrNull(value) {
8
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
9
+ }
10
+ function stringList(value) {
11
+ return Array.isArray(value)
12
+ ? value.filter((entry) => typeof entry === "string" && entry.trim().length > 0)
13
+ : [];
14
+ }
15
+ function videoAnalysisSummaryFrom(raw) {
16
+ if (typeof raw !== "object" || raw === null)
17
+ return null;
18
+ const analysis = raw;
19
+ const summary = stringOrNull(analysis.summary);
20
+ const tags = stringList(analysis.tags);
21
+ const subjects = stringList(analysis.subjects);
22
+ const sceneCount = Array.isArray(analysis.scenes) ? analysis.scenes.length : 0;
23
+ if (!summary && tags.length === 0 && subjects.length === 0 && sceneCount === 0) {
24
+ return null;
25
+ }
26
+ return {
27
+ summary: summary ?? "",
28
+ mood: stringOrNull(analysis.mood),
29
+ setting: stringOrNull(analysis.setting),
30
+ tags,
31
+ subjects,
32
+ scene_count: sceneCount,
33
+ };
34
+ }
35
+ /** Vision analysis projection for an artifact, or null when it has not been analyzed. */
36
+ export async function readVideoAnalysisSummary(engine, assetId) {
37
+ const read = await engine.files.read(assetId, VIDEO_ANALYSIS_FILE);
38
+ if (!read.ok)
39
+ return null;
40
+ try {
41
+ return videoAnalysisSummaryFrom(JSON.parse(read.value.toString()));
42
+ }
43
+ catch {
44
+ return null;
45
+ }
46
+ }
@@ -0,0 +1,33 @@
1
+ import { mkdtemp, readFile, rm } from "node:fs/promises";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { isThumbnailBlank, runFfmpeg } from "./ffmpeg.js";
5
+ export async function extractVideoPoster(videoPath, duration, signal) {
6
+ const scratch = await mkdtemp(join(tmpdir(), "videobook-poster-"));
7
+ const posterPath = join(scratch, "poster.jpg");
8
+ try {
9
+ const extract = async (seconds) => {
10
+ await runFfmpeg([
11
+ "-y", "-ss", String(seconds), "-i", videoPath,
12
+ "-map", "0:v:0", "-frames:v", "1", "-q:v", "2", posterPath,
13
+ ], { signal });
14
+ const bytes = await readFile(posterPath);
15
+ if (bytes.length === 0)
16
+ throw new Error("Video poster is empty");
17
+ return bytes;
18
+ };
19
+ const first = await extract(0);
20
+ if (duration > 0 && await isThumbnailBlank(posterPath)) {
21
+ try {
22
+ return await extract(duration / 2);
23
+ }
24
+ catch {
25
+ signal?.throwIfAborted();
26
+ }
27
+ }
28
+ return first;
29
+ }
30
+ finally {
31
+ await rm(scratch, { recursive: true, force: true });
32
+ }
33
+ }