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,83 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useCurrentFrame, useVideoConfig } from "remotion";
3
+ import { loadFont } from "@remotion/google-fonts/Anton";
4
+ import { ALIVE_LINE_HEIGHT, activeCue, cueFade, fitPunch, lineGap, punchWord, wordLook, } from "./alive-layout.js";
5
+ const { fontFamily } = loadFont();
6
+ function justifyFor(side) {
7
+ if (side === "left")
8
+ return "flex-start";
9
+ if (side === "right")
10
+ return "flex-end";
11
+ return "center";
12
+ }
13
+ function originFor(side) {
14
+ if (side === "left")
15
+ return "left bottom";
16
+ if (side === "right")
17
+ return "right bottom";
18
+ return "center bottom";
19
+ }
20
+ function textShadow(finish, fontSize) {
21
+ const glow = `0 ${Math.round(fontSize * 0.04)}px ${Math.round(fontSize * 0.3)}px rgba(0,0,0,0.5)`;
22
+ if (finish.shadowBlur <= 0)
23
+ return glow;
24
+ const offset = Math.round(finish.shadowBlur / 2);
25
+ return `${offset}px ${offset}px ${finish.shadowBlur}px rgba(0,0,0,${finish.shadowOpacity / 100}), ${glow}`;
26
+ }
27
+ const AliveLine = ({ words, fontSize, time, fade, settings, side, finish, last }) => {
28
+ const stroke = Math.min(finish.strokeWidth, fontSize * 0.035);
29
+ return (_jsx("div", { style: {
30
+ display: "flex",
31
+ justifyContent: justifyFor(side),
32
+ gap: Math.round(fontSize * 0.2),
33
+ fontSize,
34
+ lineHeight: ALIVE_LINE_HEIGHT,
35
+ height: fontSize * ALIVE_LINE_HEIGHT,
36
+ marginBottom: last ? 0 : lineGap(fontSize),
37
+ whiteSpace: "nowrap",
38
+ }, children: words.map((word, index) => {
39
+ const look = wordLook(word, time, settings, fontSize, fade);
40
+ return (_jsx("span", { style: {
41
+ display: "inline-block",
42
+ opacity: look.opacity,
43
+ color: look.color,
44
+ transform: `translateY(${look.translateY}px) scale(${look.scale})`,
45
+ transformOrigin: originFor(side),
46
+ WebkitTextStroke: stroke > 0 ? `${stroke}px ${finish.strokeColor}` : undefined,
47
+ paintOrder: stroke > 0 ? "stroke fill" : undefined,
48
+ textShadow: textShadow(finish, fontSize),
49
+ }, children: word.text.toUpperCase() }, `${word.start}-${index}`));
50
+ }) }));
51
+ };
52
+ function blockStyle(column, top) {
53
+ return {
54
+ position: "absolute",
55
+ left: column.x,
56
+ top,
57
+ width: column.width,
58
+ display: "flex",
59
+ flexDirection: "column",
60
+ fontFamily,
61
+ fontWeight: 400,
62
+ userSelect: "none",
63
+ pointerEvents: "none",
64
+ };
65
+ }
66
+ export const AliveCaptions = ({ cues, settings, ...finish }) => {
67
+ const frame = useCurrentFrame();
68
+ const { fps } = useVideoConfig();
69
+ const time = frame / fps;
70
+ const cue = activeCue(cues, time);
71
+ if (!cue)
72
+ return null;
73
+ const fade = cueFade(cue, time);
74
+ if (settings.style === "punch") {
75
+ const word = punchWord(cue, time);
76
+ if (!word)
77
+ return null;
78
+ const fontSize = fitPunch(word, cue.column, settings.energySizing);
79
+ const top = cue.column.y + (cue.column.height - fontSize * ALIVE_LINE_HEIGHT) / 2;
80
+ return (_jsx("div", { style: blockStyle(cue.column, top), children: _jsx(AliveLine, { words: [word], fontSize: fontSize, time: time, fade: fade, settings: settings, side: cue.column.side, finish: finish, last: true }) }));
81
+ }
82
+ return (_jsx("div", { style: blockStyle(cue.column, cue.blockTop), children: cue.lines.map((line, index) => (_jsx(AliveLine, { words: line.words, fontSize: line.fontSize, time: time, fade: fade, settings: settings, side: cue.column.side, finish: finish, last: index === cue.lines.length - 1 }, `${cue.id}-${index}`))) }));
83
+ };
@@ -0,0 +1,58 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useCurrentFrame, useVideoConfig } from "remotion";
3
+ import { getCurrentWordIndex } from "../util.js";
4
+ import { getHighlightFontSize, getYPosition } from "../constants.js";
5
+ export const Highlight = ({ chunks, position, fontSize, textColor, highlightColor, strokeWidth, strokeColor, shadowBlur, shadowOpacity, }) => {
6
+ const frame = useCurrentFrame();
7
+ const { fps, width, height } = useVideoConfig();
8
+ const currentTime = frame / fps;
9
+ const orientation = height > width ? "portrait" : width === height ? "square" : "landscape";
10
+ const fontPx = getHighlightFontSize(fontSize, orientation);
11
+ const yFraction = getYPosition(position, orientation);
12
+ const activeChunk = chunks.find((c) => currentTime >= c.start && currentTime <= c.end);
13
+ if (!activeChunk || activeChunk.words.length === 0)
14
+ return null;
15
+ const wordIdx = getCurrentWordIndex(activeChunk.words, currentTime, 0.05);
16
+ const boxPadding = 8;
17
+ const boxRadius = 8;
18
+ return (_jsx("div", { style: {
19
+ position: "absolute",
20
+ top: 0,
21
+ left: 0,
22
+ width: "100%",
23
+ height: "100%",
24
+ display: "flex",
25
+ alignItems: yFraction <= 0.3 ? "flex-start" : yFraction >= 0.6 ? "flex-end" : "center",
26
+ justifyContent: "center",
27
+ paddingTop: yFraction <= 0.3 ? `${Math.round(height * yFraction)}px` : undefined,
28
+ paddingBottom: yFraction >= 0.6 ? `${Math.round(height * (1 - yFraction))}px` : undefined,
29
+ pointerEvents: "none",
30
+ }, children: _jsx("div", { style: {
31
+ display: "flex",
32
+ flexWrap: "wrap",
33
+ justifyContent: "center",
34
+ alignItems: "center",
35
+ gap: `${Math.round(fontPx * 0.3)}px`,
36
+ maxWidth: `${width - 80}px`,
37
+ padding: "0 40px",
38
+ }, children: activeChunk.words.map((word, i) => {
39
+ const isActive = i === wordIdx;
40
+ const text = word.word.trim().toUpperCase();
41
+ if (!text)
42
+ return null;
43
+ return (_jsx("span", { style: {
44
+ fontFamily: "'Montserrat', 'Arial Black', Arial, sans-serif",
45
+ fontWeight: 800,
46
+ fontSize: fontPx,
47
+ lineHeight: 1.2,
48
+ color: textColor,
49
+ WebkitTextStroke: strokeWidth > 0 ? `${strokeWidth}px ${strokeColor}` : undefined,
50
+ paintOrder: strokeWidth > 0 ? "stroke fill" : undefined,
51
+ textShadow: shadowBlur > 0 ? `${Math.round(shadowBlur / 2)}px ${Math.round(shadowBlur / 2)}px ${shadowBlur}px rgba(0,0,0,${shadowOpacity / 100})` : undefined,
52
+ whiteSpace: "nowrap",
53
+ padding: `${boxPadding}px`,
54
+ borderRadius: isActive ? boxRadius : 0,
55
+ backgroundColor: isActive ? highlightColor : "transparent",
56
+ }, children: text }, i));
57
+ }) }) }));
58
+ };
@@ -0,0 +1,67 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useCurrentFrame, useVideoConfig, interpolate, Easing } from "remotion";
3
+ import { loadFont } from "@remotion/google-fonts/Anton";
4
+ import { getCurrentWordIndex } from "../util.js";
5
+ import { getImpactFontSize, getYPosition, POP_DURATION_FRAMES, POP_SCALE_START, POP_SCALE_END } from "../constants.js";
6
+ const { fontFamily } = loadFont();
7
+ export const Impact = ({ chunks, position, fontSize, textColor, highlightColor, strokeWidth, strokeColor, shadowBlur, shadowOpacity, }) => {
8
+ const frame = useCurrentFrame();
9
+ const { fps, width, height } = useVideoConfig();
10
+ const currentTime = frame / fps;
11
+ const orientation = height > width ? "portrait" : width === height ? "square" : "landscape";
12
+ const fontPx = getImpactFontSize(fontSize, orientation);
13
+ const yFraction = getYPosition(position, orientation);
14
+ const activeChunk = chunks.find((c) => currentTime >= c.start && currentTime <= c.end);
15
+ if (!activeChunk || activeChunk.words.length === 0)
16
+ return null;
17
+ const wordIdx = getCurrentWordIndex(activeChunk.words, currentTime);
18
+ return (_jsx("div", { style: {
19
+ position: "absolute",
20
+ top: 0,
21
+ left: 0,
22
+ width: "100%",
23
+ height: "100%",
24
+ display: "flex",
25
+ alignItems: yFraction <= 0.3 ? "flex-start" : yFraction >= 0.6 ? "flex-end" : "center",
26
+ justifyContent: "center",
27
+ paddingTop: yFraction <= 0.3 ? `${Math.round(height * yFraction)}px` : undefined,
28
+ paddingBottom: yFraction >= 0.6 ? `${Math.round(height * (1 - yFraction))}px` : undefined,
29
+ pointerEvents: "none",
30
+ }, children: _jsx("div", { style: {
31
+ display: "flex",
32
+ justifyContent: "center",
33
+ alignItems: "center",
34
+ gap: `${Math.round(fontPx * 0.25)}px`,
35
+ maxWidth: `${width - 80}px`,
36
+ padding: "0 40px",
37
+ }, children: activeChunk.words.map((word, i) => {
38
+ const isActive = i === wordIdx;
39
+ const text = word.word.trim();
40
+ if (!text)
41
+ return null;
42
+ let scale = 1.0;
43
+ if (isActive) {
44
+ const wordStartFrame = Math.round(word.start * fps);
45
+ const framesSinceWordStart = frame - wordStartFrame;
46
+ scale = interpolate(framesSinceWordStart, [0, POP_DURATION_FRAMES], [POP_SCALE_START, POP_SCALE_END], {
47
+ extrapolateLeft: "clamp",
48
+ extrapolateRight: "clamp",
49
+ easing: Easing.out(Easing.cubic),
50
+ });
51
+ }
52
+ return (_jsx("span", { style: {
53
+ fontFamily,
54
+ fontWeight: 400,
55
+ fontSize: fontPx,
56
+ lineHeight: 1.1,
57
+ color: isActive ? highlightColor : textColor,
58
+ WebkitTextStroke: strokeWidth > 0 ? `${strokeWidth}px ${strokeColor}` : undefined,
59
+ paintOrder: strokeWidth > 0 ? "stroke fill" : undefined,
60
+ textShadow: shadowBlur > 0 ? `${Math.round(shadowBlur / 2)}px ${Math.round(shadowBlur / 2)}px ${shadowBlur}px rgba(0,0,0,${shadowOpacity / 100})` : undefined,
61
+ whiteSpace: "nowrap",
62
+ transform: `scale(${scale})`,
63
+ display: "inline-block",
64
+ userSelect: "none",
65
+ }, children: text.toUpperCase() }, i));
66
+ }) }) }));
67
+ };
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Impact } from "./Impact.js";
3
+ import { Highlight } from "./Highlight.js";
4
+ export const SubtitleOverlay = ({ chunks, style, position, fontSize, textColor, highlightColor, strokeWidth, strokeColor, shadowBlur, shadowOpacity, }) => {
5
+ if (style === "highlight") {
6
+ return (_jsx(Highlight, { chunks: chunks, position: position, fontSize: fontSize, textColor: textColor, highlightColor: highlightColor, strokeWidth: strokeWidth, strokeColor: strokeColor, shadowBlur: shadowBlur, shadowOpacity: shadowOpacity }));
7
+ }
8
+ return (_jsx(Impact, { chunks: chunks, position: position, fontSize: fontSize, textColor: textColor, highlightColor: highlightColor, strokeWidth: strokeWidth, strokeColor: strokeColor, shadowBlur: shadowBlur, shadowOpacity: shadowOpacity }));
9
+ };
@@ -0,0 +1,321 @@
1
+ /**
2
+ * Pure layout for alive captions: phrase/line grouping, frame-aware column
3
+ * placement, stack fitting and per-frame word entrance. Shared by the
4
+ * Remotion renderer and the client studio so the preview matches the render.
5
+ */
6
+ const PHRASE_GAP_SECONDS = 0.45;
7
+ const PHRASE_MAX_WORDS = 8;
8
+ const PHRASE_MAX_SECONDS = 3.4;
9
+ const PHRASE_MAX_LINES = 4;
10
+ const LINE_MAX_WORDS = 2;
11
+ const LINE_MAX_CHARS = 11;
12
+ const HERO_ENERGY = 0.8;
13
+ const HERO_MIN_CHARS = 5;
14
+ /** Average advance of the condensed display face per character, in em. */
15
+ export const ALIVE_GLYPH_EM = 0.5;
16
+ export const ALIVE_LINE_HEIGHT = 0.92;
17
+ const LINE_GAP_EM = 0.06;
18
+ const LEAD_SECONDS = 0.06;
19
+ const HOLD_SECONDS = 0.5;
20
+ const FADE_SECONDS = 0.18;
21
+ const MIN_WORD_SECONDS = 0.05;
22
+ const REGION_PAD = 0.02;
23
+ const MIN_FREE_SPAN = 0.3;
24
+ const SENTENCE_END = /[.!?]["')\]]*$/;
25
+ const EDGE_PUNCTUATION = /^[^\p{L}\p{N}'’]+|[^\p{L}\p{N}'’]+$/gu;
26
+ export function displayWord(raw) {
27
+ return raw.trim().replace(EDGE_PUNCTUATION, "");
28
+ }
29
+ function breaksBefore(current, word) {
30
+ if (current.length === 0)
31
+ return false;
32
+ const previous = current[current.length - 1];
33
+ return word.start - previous.end >= PHRASE_GAP_SECONDS
34
+ || current.length >= PHRASE_MAX_WORDS
35
+ || word.end - current[0].start > PHRASE_MAX_SECONDS;
36
+ }
37
+ /** Group one transcript segment's words into sung/spoken phrases. */
38
+ export function splitPhrases(words) {
39
+ const phrases = [];
40
+ let current = [];
41
+ for (const word of words) {
42
+ if (breaksBefore(current, word)) {
43
+ phrases.push(current);
44
+ current = [];
45
+ }
46
+ current.push(word);
47
+ if (SENTENCE_END.test(word.raw.trim())) {
48
+ phrases.push(current);
49
+ current = [];
50
+ }
51
+ }
52
+ if (current.length > 0)
53
+ phrases.push(current);
54
+ return phrases;
55
+ }
56
+ function isHero(word) {
57
+ return word.energy >= HERO_ENERGY && word.text.length >= HERO_MIN_CHARS;
58
+ }
59
+ function lineChars(line) {
60
+ return line.reduce((sum, word) => sum + word.text.length, 0) + Math.max(0, line.length - 1);
61
+ }
62
+ function startsNewLine(line, word) {
63
+ if (line.length === 0)
64
+ return false;
65
+ return line.length >= LINE_MAX_WORDS
66
+ || lineChars(line) + 1 + word.text.length > LINE_MAX_CHARS
67
+ || isHero(word)
68
+ || line.some(isHero);
69
+ }
70
+ /** Stack a phrase into short lines; loud long words get a line of their own. */
71
+ export function splitLines(words) {
72
+ const lines = [];
73
+ let line = [];
74
+ for (const word of words) {
75
+ if (startsNewLine(line, word)) {
76
+ lines.push(line);
77
+ line = [];
78
+ }
79
+ line.push(word);
80
+ }
81
+ if (line.length > 0)
82
+ lines.push(line);
83
+ return lines;
84
+ }
85
+ function toAliveWord(word) {
86
+ return {
87
+ text: displayWord(word.raw),
88
+ transcriptStart: word.start,
89
+ transcriptEnd: word.end,
90
+ onsetStart: word.onsetStart,
91
+ energy: word.energy,
92
+ };
93
+ }
94
+ function chunkLines(lines) {
95
+ const groups = [];
96
+ for (let index = 0; index < lines.length; index += PHRASE_MAX_LINES) {
97
+ groups.push(lines.slice(index, index + PHRASE_MAX_LINES));
98
+ }
99
+ return groups;
100
+ }
101
+ /** Phrase line stacks for a list of transcript segments, in time order. */
102
+ export function buildPhraseLines(segments) {
103
+ return segments
104
+ .flatMap(splitPhrases)
105
+ .map((phrase) => phrase.map(toAliveWord).filter((word) => word.text.length > 0))
106
+ .filter((phrase) => phrase.length > 0)
107
+ .flatMap((phrase) => chunkLines(splitLines(phrase)));
108
+ }
109
+ export function phraseFrameTime(lines) {
110
+ const words = lines.flat();
111
+ const first = words[0];
112
+ const last = words[words.length - 1];
113
+ return (first.transcriptStart + last.transcriptEnd) / 2;
114
+ }
115
+ export function wordTiming(word, snapToOnsets) {
116
+ const shift = snapToOnsets ? word.onsetStart - word.transcriptStart : 0;
117
+ const start = word.transcriptStart + shift;
118
+ const end = Math.max(start + MIN_WORD_SECONDS, word.transcriptEnd + shift);
119
+ return { text: word.text, start, end, energy: word.energy };
120
+ }
121
+ /** Pixel rect of the contain-fitted source inside the target frame. */
122
+ export function contentRect(geometry) {
123
+ const { sourceWidth, sourceHeight, targetWidth, targetHeight } = geometry;
124
+ const scale = Math.min(targetWidth / sourceWidth, targetHeight / sourceHeight);
125
+ const width = sourceWidth * scale;
126
+ const height = sourceHeight * scale;
127
+ return { x: (targetWidth - width) / 2, y: (targetHeight - height) / 2, width, height };
128
+ }
129
+ /** Map a source-normalized box into target-normalized coordinates. */
130
+ export function regionToTarget(region, geometry) {
131
+ const content = contentRect(geometry);
132
+ return {
133
+ x: (content.x + region.x * content.width) / geometry.targetWidth,
134
+ y: (content.y + region.y * content.height) / geometry.targetHeight,
135
+ width: region.width * content.width / geometry.targetWidth,
136
+ height: region.height * content.height / geometry.targetHeight,
137
+ };
138
+ }
139
+ function columnCandidates(geometry) {
140
+ if (geometry.targetHeight > geometry.targetWidth)
141
+ return [{ side: "center", x0: 0.07, x1: 0.93 }];
142
+ return [
143
+ { side: "left", x0: 0.06, x1: 0.47 },
144
+ { side: "right", x0: 0.53, x1: 0.94 },
145
+ ];
146
+ }
147
+ function verticalBounds(geometry) {
148
+ const portrait = geometry.targetHeight > geometry.targetWidth;
149
+ return { y0: 0.08, y1: portrait ? 0.86 : 0.92 };
150
+ }
151
+ function blockersFor(candidate, boxes) {
152
+ return boxes
153
+ .filter((box) => box.x - REGION_PAD < candidate.x1 && box.x + box.width + REGION_PAD > candidate.x0)
154
+ .map((box) => ({ y0: box.y - REGION_PAD, y1: box.y + box.height + REGION_PAD }))
155
+ .sort((a, b) => a.y0 - b.y0);
156
+ }
157
+ /** Free vertical spans of `bounds` once the blocker spans are removed. */
158
+ export function freeSpans(bounds, blockers) {
159
+ const spans = [];
160
+ let cursor = bounds.y0;
161
+ for (const blocker of blockers) {
162
+ if (blocker.y0 > cursor)
163
+ spans.push({ y0: cursor, y1: Math.min(blocker.y0, bounds.y1) });
164
+ cursor = Math.max(cursor, blocker.y1);
165
+ if (cursor >= bounds.y1)
166
+ break;
167
+ }
168
+ if (cursor < bounds.y1)
169
+ spans.push({ y0: cursor, y1: bounds.y1 });
170
+ return spans.filter((span) => span.y1 - span.y0 > 0);
171
+ }
172
+ function longestSpan(spans) {
173
+ return spans.reduce((best, span) => best === null || span.y1 - span.y0 > best.y1 - best.y0 ? span : best, null);
174
+ }
175
+ function blockedHeight(blockers) {
176
+ return blockers.reduce((sum, span) => sum + (span.y1 - span.y0), 0);
177
+ }
178
+ function scoreCandidate(candidate, bounds, boxes) {
179
+ const blockers = blockersFor(candidate, boxes);
180
+ return { candidate, span: longestSpan(freeSpans(bounds, blockers)), blocked: blockedHeight(blockers) };
181
+ }
182
+ function spanLength(scored) {
183
+ return scored.span ? scored.span.y1 - scored.span.y0 : 0;
184
+ }
185
+ function toColumn(candidate, span, geometry) {
186
+ return {
187
+ side: candidate.side,
188
+ x: candidate.x0 * geometry.targetWidth,
189
+ y: span.y0 * geometry.targetHeight,
190
+ width: (candidate.x1 - candidate.x0) * geometry.targetWidth,
191
+ height: (span.y1 - span.y0) * geometry.targetHeight,
192
+ };
193
+ }
194
+ /**
195
+ * Pick the stack column: the candidate side with the tallest stretch clear of
196
+ * avoided regions (target-normalized). Falls back to the least-covered
197
+ * full-height column when nothing leaves a usable free span.
198
+ */
199
+ export function placeColumn(avoid, geometry) {
200
+ const bounds = verticalBounds(geometry);
201
+ const scored = columnCandidates(geometry).map((candidate) => scoreCandidate(candidate, bounds, avoid));
202
+ const best = scored.reduce((a, b) => (spanLength(b) > spanLength(a) + 0.02 ? b : a));
203
+ if (best.span && spanLength(best) >= MIN_FREE_SPAN)
204
+ return toColumn(best.candidate, best.span, geometry);
205
+ const leastBlocked = scored.reduce((a, b) => (b.blocked < a.blocked ? b : a));
206
+ return toColumn(leastBlocked.candidate, bounds, geometry);
207
+ }
208
+ export function avoidedRegions(regions, settings) {
209
+ return regions.filter((region) => (region.kind === "face" ? settings.avoidFaces : settings.avoidText));
210
+ }
211
+ function lineText(words) {
212
+ return words.map((word) => word.text).join(" ");
213
+ }
214
+ function lineEnergy(words) {
215
+ return Math.max(...words.map((word) => word.energy));
216
+ }
217
+ function fitToWidth(text, width, cap) {
218
+ return Math.min(cap, width / (Math.max(text.length, 2) * ALIVE_GLYPH_EM));
219
+ }
220
+ export function stackHeight(fontSizes) {
221
+ const total = fontSizes.reduce((sum, size) => sum + size * (ALIVE_LINE_HEIGHT + LINE_GAP_EM), 0);
222
+ return total - (fontSizes.length > 0 ? fontSizes[fontSizes.length - 1] * LINE_GAP_EM : 0);
223
+ }
224
+ export function lineGap(fontSize) {
225
+ return fontSize * LINE_GAP_EM;
226
+ }
227
+ /** Fit each stacked line to the column width, then shrink the block to fit its height. */
228
+ export function fitStack(lines, column, energySizing) {
229
+ const cap = column.height * 0.36;
230
+ const fitted = lines.map((words) => fitToWidth(lineText(words), column.width, cap));
231
+ const sized = energySizing
232
+ ? fitted.map((size, index) => size * (0.7 + 0.3 * lineEnergy(lines[index])))
233
+ : fitted.map(() => Math.min(...fitted));
234
+ const scale = Math.min(1, column.height / stackHeight(sized));
235
+ return lines.map((words, index) => ({ words, fontSize: Math.round(sized[index] * scale * 10) / 10 }));
236
+ }
237
+ export function fitPunch(word, column, energySizing) {
238
+ const scale = energySizing ? 0.7 + 0.3 * word.energy : 0.85;
239
+ return Math.min(fitToWidth(word.text, column.width, column.height * 0.6), column.height * 0.6) * scale;
240
+ }
241
+ function cueWindow(lines, nextStart) {
242
+ const words = lines.flat();
243
+ const start = words[0].start - LEAD_SECONDS;
244
+ const hold = words[words.length - 1].end + HOLD_SECONDS;
245
+ const end = nextStart === null ? hold : Math.min(hold, nextStart - LEAD_SECONDS);
246
+ return { start, end: Math.max(end, start + 0.1) };
247
+ }
248
+ function firstStart(phrase, snap) {
249
+ return wordTiming(phrase.lines[0][0], snap).start;
250
+ }
251
+ /** Resolve analysed phrases into positioned, timed cues for one target frame. */
252
+ export function resolveAliveCues(phrases, settings, geometry) {
253
+ return phrases.map((phrase, index) => {
254
+ const lines = phrase.lines.map((line) => line.map((word) => wordTiming(word, settings.snapToOnsets)));
255
+ const next = phrases[index + 1];
256
+ const window = cueWindow(lines, next ? firstStart(next, settings.snapToOnsets) : null);
257
+ const avoid = avoidedRegions(phrase.regions, settings).map((region) => regionToTarget(region, geometry));
258
+ const column = placeColumn(avoid, geometry);
259
+ const fitted = fitStack(lines, column, settings.energySizing);
260
+ const blockHeight = stackHeight(fitted.map((line) => line.fontSize));
261
+ return {
262
+ id: phrase.id,
263
+ start: window.start,
264
+ end: window.end,
265
+ column,
266
+ lines: fitted,
267
+ blockTop: column.y + (column.height - blockHeight) / 2,
268
+ };
269
+ });
270
+ }
271
+ export function activeCue(cues, time) {
272
+ return cues.find((cue) => time >= cue.start && time < cue.end) ?? null;
273
+ }
274
+ export function cueFade(cue, time) {
275
+ return clamp01((cue.end - time) / FADE_SECONDS);
276
+ }
277
+ /** The most recently started word of a cue, for punch style. */
278
+ export function punchWord(cue, time) {
279
+ return cue.lines.flatMap((line) => line.words).filter((word) => word.start <= time).pop() ?? null;
280
+ }
281
+ const MOTIONS = {
282
+ pop: {
283
+ seconds: 0.14,
284
+ apply: (eased, _progress, energy, fontSize) => ({
285
+ opacity: eased,
286
+ scale: 1 + (1 - eased) * 0.3 * (0.6 + energy),
287
+ translateY: (1 - eased) * fontSize * 0.12,
288
+ }),
289
+ },
290
+ slam: {
291
+ seconds: 0.09,
292
+ apply: (eased, progress) => ({ opacity: clamp01(progress * 3), scale: 1 + (1 - eased) * 0.9, translateY: 0 }),
293
+ },
294
+ rise: {
295
+ seconds: 0.22,
296
+ apply: (eased, _progress, _energy, fontSize) => ({ opacity: eased, scale: 1, translateY: (1 - eased) * fontSize * 0.35 }),
297
+ },
298
+ };
299
+ function clamp01(value) {
300
+ return value < 0 ? 0 : value > 1 ? 1 : value;
301
+ }
302
+ function wordColor(word, time, settings) {
303
+ const started = time >= word.start;
304
+ const active = started && time < word.end;
305
+ return active || (started && word.energy >= HERO_ENERGY) ? settings.hitColor : settings.textColor;
306
+ }
307
+ export function wordLook(word, time, settings, fontSize, fade) {
308
+ const age = time - word.start;
309
+ const color = wordColor(word, time, settings);
310
+ if (settings.style === "karaoke") {
311
+ return { opacity: (age < 0 ? 0.3 : 1) * fade, scale: 1, translateY: 0, color };
312
+ }
313
+ if (age < 0)
314
+ return { opacity: 0, scale: 1, translateY: 0, color };
315
+ const curve = MOTIONS[settings.motion];
316
+ const progress = clamp01(age / curve.seconds);
317
+ const eased = 1 - Math.pow(1 - progress, 3);
318
+ const energy = settings.energySizing ? word.energy : 0.5;
319
+ const look = curve.apply(eased, progress, energy, fontSize);
320
+ return { ...look, opacity: look.opacity * fade, color };
321
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Alive captions: stacked, frame-aware, audio-timed music-video subtitles.
3
+ * These types are shared by the server (analysis, render) and the client
4
+ * studio preview, so this file must stay free of runtime imports.
5
+ */
6
+ export const ALIVE_STYLES = ["stack", "punch", "karaoke"];
7
+ export const ALIVE_MOTIONS = ["pop", "slam", "rise"];
8
+ export const DEFAULT_ALIVE_SETTINGS = {
9
+ style: "stack",
10
+ motion: "pop",
11
+ energySizing: true,
12
+ snapToOnsets: true,
13
+ avoidFaces: true,
14
+ avoidText: true,
15
+ textColor: "#F8FAFC",
16
+ hitColor: "#72E7AA",
17
+ };
@@ -0,0 +1,68 @@
1
+ const CHUNK_LIMITS = {
2
+ "highlight:portrait": [40, 8],
3
+ "highlight:landscape": [60, 12],
4
+ "highlight:square": [60, 12],
5
+ "impact:portrait": [12, 2],
6
+ "impact:landscape": [16, 2],
7
+ "impact:square": [16, 2],
8
+ };
9
+ const MAX_DURATION = 6.0;
10
+ function getChunkLimits(style, orientation) {
11
+ return CHUNK_LIMITS[`${style}:${orientation}`] ?? [42, 8];
12
+ }
13
+ export function chunkSegments(segments, orientation = "portrait", style = "impact") {
14
+ const [maxChars, maxWords] = getChunkLimits(style, orientation);
15
+ const chunked = [];
16
+ for (const segment of segments) {
17
+ const words = segment.words;
18
+ if (words.length === 0) {
19
+ const text = segment.text.trim();
20
+ if (text) {
21
+ chunked.push({ start: segment.start, end: segment.end, text, words: [] });
22
+ }
23
+ continue;
24
+ }
25
+ let currentWords = [];
26
+ let chunkStart = null;
27
+ for (let idx = 0; idx < words.length; idx++) {
28
+ const word = words[idx];
29
+ if (chunkStart === null) {
30
+ chunkStart = word.start;
31
+ }
32
+ const potentialText = [...currentWords, word].map((w) => w.word.trim()).join(" ");
33
+ const potentialDuration = word.end - chunkStart;
34
+ const wouldExceed = currentWords.length >= maxWords ||
35
+ potentialText.length > maxChars ||
36
+ potentialDuration > MAX_DURATION;
37
+ if (wouldExceed && currentWords.length > 0) {
38
+ const chunkText = currentWords.map((w) => w.word.trim()).join(" ");
39
+ const chunkEnd = currentWords[currentWords.length - 1].end;
40
+ chunked.push({
41
+ start: chunkStart,
42
+ end: chunkEnd,
43
+ text: chunkText.trim(),
44
+ words: [...currentWords],
45
+ });
46
+ currentWords = [];
47
+ chunkStart = word.start;
48
+ }
49
+ currentWords.push(word);
50
+ const wordText = word.word.trim();
51
+ const isSentenceEnd = wordText.endsWith(".") || wordText.endsWith("!") || wordText.endsWith("?");
52
+ const isLast = idx === words.length - 1;
53
+ if ((isSentenceEnd || isLast) && currentWords.length > 0) {
54
+ const chunkText = currentWords.map((w) => w.word.trim()).join(" ");
55
+ const chunkEnd = currentWords[currentWords.length - 1].end;
56
+ chunked.push({
57
+ start: chunkStart,
58
+ end: chunkEnd,
59
+ text: chunkText.trim(),
60
+ words: [...currentWords],
61
+ });
62
+ currentWords = [];
63
+ chunkStart = null;
64
+ }
65
+ }
66
+ }
67
+ return chunked;
68
+ }
@@ -0,0 +1,42 @@
1
+ export function parseSegments(data) {
2
+ if (data.segments && data.segments.length > 0) {
3
+ return data.segments.map(parseOneSegment);
4
+ }
5
+ if (data.words && data.words.length > 0) {
6
+ const words = data.words
7
+ .filter((w) => (w.type ?? "word") === "word")
8
+ .map(parseOneWord)
9
+ .filter((w) => w !== null);
10
+ if (words.length === 0)
11
+ return [];
12
+ return [{
13
+ start: words[0].start,
14
+ end: words[words.length - 1].end,
15
+ text: words.map((w) => w.word).join(" "),
16
+ words,
17
+ }];
18
+ }
19
+ return [];
20
+ }
21
+ function parseOneSegment(seg) {
22
+ const words = (seg.words ?? [])
23
+ .filter((w) => (w.type ?? "word") === "word")
24
+ .map(parseOneWord)
25
+ .filter((w) => w !== null);
26
+ return {
27
+ start: seg.start ?? 0,
28
+ end: seg.end ?? 0,
29
+ text: (seg.text ?? "").trim(),
30
+ words,
31
+ };
32
+ }
33
+ function parseOneWord(w) {
34
+ const text = (w.text ?? w.word ?? "").trim();
35
+ if (!text)
36
+ return null;
37
+ return {
38
+ word: text,
39
+ start: w.start ?? 0,
40
+ end: w.end ?? 0,
41
+ };
42
+ }
@@ -0,0 +1,4 @@
1
+ const SUBTITLE_STYLES = ["impact", "highlight", "alive"];
2
+ export function isSubtitleStyle(value) {
3
+ return typeof value === "string" && SUBTITLE_STYLES.includes(value);
4
+ }
@@ -0,0 +1 @@
1
+ export {};