@crievo/omni 0.0.0-stage → 0.1.1

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 (708) hide show
  1. package/.env.example +41 -0
  2. package/README.md +2 -2
  3. package/bin/omni.mjs +18 -0
  4. package/docs/onboarding/clone-voice.png +0 -0
  5. package/docs/onboarding/getting-started.png +0 -0
  6. package/docs/onboarding/new-video.png +0 -0
  7. package/docs/onboarding/providers.png +0 -0
  8. package/docs/onboarding/publish.png +0 -0
  9. package/docs/onboarding/references.png +0 -0
  10. package/docs/onboarding.md +212 -0
  11. package/omnivideo.config.example.json +437 -0
  12. package/package.json +40 -4
  13. package/reference/captions.md +156 -0
  14. package/reference/carousel-contract.md +91 -0
  15. package/reference/cloud.md +96 -0
  16. package/reference/learnings.md +792 -0
  17. package/reference/omnichat.md +305 -0
  18. package/reference/premiere-mcp.md +91 -0
  19. package/reference/publish.md +305 -0
  20. package/reference/scenes-contract.md +109 -0
  21. package/reference/studio.md +666 -0
  22. package/reference/voicestudio.md +81 -0
  23. package/reference/writing-for-voice.md +67 -0
  24. package/requirements.txt +3 -0
  25. package/studio/api/app.ts +87 -0
  26. package/studio/api/lib/errors.ts +33 -0
  27. package/studio/api/lib/files.ts +41 -0
  28. package/studio/api/lib/hono.ts +9 -0
  29. package/studio/api/lib/result.ts +14 -0
  30. package/studio/api/lib/validate.ts +31 -0
  31. package/studio/api/lib/video.ts +10 -0
  32. package/studio/api/middleware/json-charset.ts +11 -0
  33. package/studio/api/middleware/local-only.ts +32 -0
  34. package/studio/api/middleware/no-timeout.ts +10 -0
  35. package/studio/api/routes/admin.ts +18 -0
  36. package/studio/api/routes/agent-cascade.ts +100 -0
  37. package/studio/api/routes/agent-history.ts +23 -0
  38. package/studio/api/routes/agent.ts +162 -0
  39. package/studio/api/routes/analytics.ts +31 -0
  40. package/studio/api/routes/approval.ts +60 -0
  41. package/studio/api/routes/archive.ts +12 -0
  42. package/studio/api/routes/captions.ts +28 -0
  43. package/studio/api/routes/carousel.ts +38 -0
  44. package/studio/api/routes/clone.ts +27 -0
  45. package/studio/api/routes/create.ts +19 -0
  46. package/studio/api/routes/events.ts +11 -0
  47. package/studio/api/routes/export.ts +14 -0
  48. package/studio/api/routes/media.ts +30 -0
  49. package/studio/api/routes/omnichat.ts +113 -0
  50. package/studio/api/routes/onboarding.ts +14 -0
  51. package/studio/api/routes/publish.ts +40 -0
  52. package/studio/api/routes/queue.ts +14 -0
  53. package/studio/api/routes/references.ts +40 -0
  54. package/studio/api/routes/runner.ts +17 -0
  55. package/studio/api/routes/schedule.ts +47 -0
  56. package/studio/api/routes/services.ts +11 -0
  57. package/studio/api/routes/sessions.ts +27 -0
  58. package/studio/api/routes/settings.ts +102 -0
  59. package/studio/api/routes/sound.ts +45 -0
  60. package/studio/api/routes/thumbnail.ts +18 -0
  61. package/studio/api/routes/usage.ts +13 -0
  62. package/studio/api/routes/videos.ts +41 -0
  63. package/studio/api/routes/voices.ts +71 -0
  64. package/studio/api/services/admin.ts +113 -0
  65. package/studio/api/services/agent-cascade.ts +975 -0
  66. package/studio/api/services/agent-claude.ts +47 -0
  67. package/studio/api/services/agent-history.ts +103 -0
  68. package/studio/api/services/agent-persona.ts +77 -0
  69. package/studio/api/services/agent-tools.ts +2237 -0
  70. package/studio/api/services/agent-ui-tools.ts +259 -0
  71. package/studio/api/services/agent-wait.ts +32 -0
  72. package/studio/api/services/agent.ts +553 -0
  73. package/studio/api/services/analytics.ts +92 -0
  74. package/studio/api/services/approval.ts +134 -0
  75. package/studio/api/services/archive.ts +38 -0
  76. package/studio/api/services/captions.ts +268 -0
  77. package/studio/api/services/carousel.ts +206 -0
  78. package/studio/api/services/classify.ts +196 -0
  79. package/studio/api/services/clone-phrases.ts +75 -0
  80. package/studio/api/services/clone.ts +722 -0
  81. package/studio/api/services/create.ts +421 -0
  82. package/studio/api/services/events.ts +215 -0
  83. package/studio/api/services/media.ts +32 -0
  84. package/studio/api/services/omnichat.ts +624 -0
  85. package/studio/api/services/onboarding.ts +77 -0
  86. package/studio/api/services/publish.ts +287 -0
  87. package/studio/api/services/queue.ts +150 -0
  88. package/studio/api/services/references.ts +269 -0
  89. package/studio/api/services/root.ts +60 -0
  90. package/studio/api/services/services.ts +114 -0
  91. package/studio/api/services/sessions.ts +1007 -0
  92. package/studio/api/services/settings.ts +983 -0
  93. package/studio/api/services/sound.ts +311 -0
  94. package/studio/api/services/thumbnail.ts +133 -0
  95. package/studio/api/services/usage.ts +27 -0
  96. package/studio/api/services/videos.ts +418 -0
  97. package/studio/api/services/voice-pause.ts +98 -0
  98. package/studio/api/services/voices.ts +324 -0
  99. package/studio/runner/runner.ts +244 -0
  100. package/studio/shared/omni-actions.ts +68 -0
  101. package/studio/shared/omni-admin.ts +52 -0
  102. package/studio/shared/omni-farewell.ts +48 -0
  103. package/studio/shared/omni-hotword.ts +20 -0
  104. package/studio/shared/omni-screens.ts +228 -0
  105. package/studio/shared/schemas/admin.ts +15 -0
  106. package/studio/shared/schemas/agent-cascade.ts +58 -0
  107. package/studio/shared/schemas/agent-history.ts +73 -0
  108. package/studio/shared/schemas/agent.ts +50 -0
  109. package/studio/shared/schemas/analytics.ts +6 -0
  110. package/studio/shared/schemas/approval.ts +22 -0
  111. package/studio/shared/schemas/captions.ts +19 -0
  112. package/studio/shared/schemas/carousel.ts +26 -0
  113. package/studio/shared/schemas/clone.ts +40 -0
  114. package/studio/shared/schemas/common.ts +16 -0
  115. package/studio/shared/schemas/omnichat.ts +54 -0
  116. package/studio/shared/schemas/onboarding.ts +5 -0
  117. package/studio/shared/schemas/publish.ts +45 -0
  118. package/studio/shared/schemas/references.ts +20 -0
  119. package/studio/shared/schemas/settings.ts +82 -0
  120. package/studio/shared/schemas/sound.ts +22 -0
  121. package/studio/shared/schemas/videos.ts +38 -0
  122. package/studio/shared/schemas/voices.ts +23 -0
  123. package/studio/shared/types/api.ts +1290 -0
  124. package/studio/studio.ts +148 -0
  125. package/studio/tsconfig.json +15 -0
  126. package/studio/web/assets/logos/cartesia.svg +4 -0
  127. package/studio/web/assets/logos/elevenlabs.svg +12 -0
  128. package/studio/web/assets/logos/fish.svg +24 -0
  129. package/studio/web/assets/logos/omni-black.png +0 -0
  130. package/studio/web/assets/logos/omni-white.png +0 -0
  131. package/studio/web/assets/logos/voicestudio.svg +21 -0
  132. package/studio/web/components/AgentMark.tsx +7 -0
  133. package/studio/web/components/AgentWalking.tsx +11 -0
  134. package/studio/web/components/ApiKeyField.tsx +104 -0
  135. package/studio/web/components/AppShell.tsx +64 -0
  136. package/studio/web/components/ApprovalNotice.tsx +65 -0
  137. package/studio/web/components/AvatarGroupIcon.tsx +22 -0
  138. package/studio/web/components/AvatarNameField.tsx +86 -0
  139. package/studio/web/components/AvatarPicker.tsx +84 -0
  140. package/studio/web/components/BrandLogo.tsx +16 -0
  141. package/studio/web/components/CartesiaPlanSetting.tsx +90 -0
  142. package/studio/web/components/ClaudeWalking.tsx +32 -0
  143. package/studio/web/components/CloneVoiceDialog/GuidedScript.tsx +39 -0
  144. package/studio/web/components/CloneVoiceDialog/LanguageBar.tsx +37 -0
  145. package/studio/web/components/CloneVoiceDialog/LevelMeter.tsx +31 -0
  146. package/studio/web/components/CloneVoiceDialog/ProviderChoice.tsx +81 -0
  147. package/studio/web/components/CloneVoiceDialog/QualityPanel.tsx +62 -0
  148. package/studio/web/components/CloneVoiceDialog/RecordStep.tsx +50 -0
  149. package/studio/web/components/CloneVoiceDialog/RecorderBar.tsx +79 -0
  150. package/studio/web/components/CloneVoiceDialog/RedoSummary.tsx +54 -0
  151. package/studio/web/components/CloneVoiceDialog/SaveStep.tsx +91 -0
  152. package/studio/web/components/CloneVoiceDialog/SourceBar.tsx +30 -0
  153. package/studio/web/components/CloneVoiceDialog/index.tsx +141 -0
  154. package/studio/web/components/CloneVoiceDialog/types.ts +28 -0
  155. package/studio/web/components/CloneVoiceDialog/useCloneVoice.ts +528 -0
  156. package/studio/web/components/ConnectionSignals.tsx +51 -0
  157. package/studio/web/components/EditVoiceDialog/EditVoiceFooter.tsx +72 -0
  158. package/studio/web/components/EditVoiceDialog/ProviderCards.tsx +81 -0
  159. package/studio/web/components/EditVoiceDialog/index.tsx +256 -0
  160. package/studio/web/components/ErrorBanner.tsx +115 -0
  161. package/studio/web/components/FinishedTask.tsx +41 -0
  162. package/studio/web/components/FishModelSetting.tsx +69 -0
  163. package/studio/web/components/FishPriceSetting.tsx +44 -0
  164. package/studio/web/components/HumanBanner.tsx +64 -0
  165. package/studio/web/components/Lightbox.tsx +41 -0
  166. package/studio/web/components/LogStream.tsx +42 -0
  167. package/studio/web/components/Markdown.tsx +21 -0
  168. package/studio/web/components/MissingDmsPanel/BatchSend.tsx +137 -0
  169. package/studio/web/components/MissingDmsPanel/CommentCard.tsx +168 -0
  170. package/studio/web/components/MissingDmsPanel/CommentFeed.tsx +45 -0
  171. package/studio/web/components/MissingDmsPanel/DmBubble.tsx +38 -0
  172. package/studio/web/components/MissingDmsPanel/ReplyComposer.tsx +63 -0
  173. package/studio/web/components/MissingDmsPanel/index.tsx +98 -0
  174. package/studio/web/components/NotFound.tsx +48 -0
  175. package/studio/web/components/Omni/OmniAvatarPicker.tsx +92 -0
  176. package/studio/web/components/Omni/OmniCards.tsx +202 -0
  177. package/studio/web/components/Omni/OmniGlow.tsx +183 -0
  178. package/studio/web/components/Omni/OmniHistory.tsx +119 -0
  179. package/studio/web/components/Omni/OmniHotword.tsx +40 -0
  180. package/studio/web/components/Omni/OmniMicSelect.tsx +92 -0
  181. package/studio/web/components/Omni/OmniVoiceSample.tsx +81 -0
  182. package/studio/web/components/Omni/OmniWidget.tsx +743 -0
  183. package/studio/web/components/Omni/PendingActions.tsx +200 -0
  184. package/studio/web/components/Omni/Transcript.tsx +127 -0
  185. package/studio/web/components/OmnichatAccountStatus.tsx +87 -0
  186. package/studio/web/components/OmnichatRulePanel/RuleConfig.tsx +150 -0
  187. package/studio/web/components/OmnichatRulePanel/RuleDryRun.tsx +15 -0
  188. package/studio/web/components/OmnichatRulePanel/RuleEnableConfirm.tsx +42 -0
  189. package/studio/web/components/OmnichatRulePanel/RuleSkeleton.tsx +18 -0
  190. package/studio/web/components/OmnichatRulePanel/RuleStatus.tsx +55 -0
  191. package/studio/web/components/OmnichatRulePanel/index.tsx +115 -0
  192. package/studio/web/components/OmnichatRulePanel/rule-steps.ts +67 -0
  193. package/studio/web/components/OmnichatRulePanel/steps/MessageStep.tsx +91 -0
  194. package/studio/web/components/OmnichatRulePanel/steps/RepliesStep.tsx +201 -0
  195. package/studio/web/components/OmnichatRulePanel/steps/TriggerStep.tsx +76 -0
  196. package/studio/web/components/OmnichatRulePanel/steps/types.ts +11 -0
  197. package/studio/web/components/Palette.tsx +17 -0
  198. package/studio/web/components/PlatformCard.tsx +109 -0
  199. package/studio/web/components/PlatformIcon.tsx +19 -0
  200. package/studio/web/components/ProviderCard.tsx +80 -0
  201. package/studio/web/components/ProviderDetail.tsx +367 -0
  202. package/studio/web/components/ProviderLogo.tsx +20 -0
  203. package/studio/web/components/QueuePanel.tsx +77 -0
  204. package/studio/web/components/ReferenceActions.tsx +89 -0
  205. package/studio/web/components/ReferencePicker.tsx +136 -0
  206. package/studio/web/components/ResultMessage.tsx +7 -0
  207. package/studio/web/components/RuleForm.tsx +138 -0
  208. package/studio/web/components/RuleLog.tsx +151 -0
  209. package/studio/web/components/RunningTask.tsx +23 -0
  210. package/studio/web/components/SampleList.tsx +55 -0
  211. package/studio/web/components/SessionPanel.tsx +352 -0
  212. package/studio/web/components/SettingRow.tsx +14 -0
  213. package/studio/web/components/SettingsCategorySkeleton.tsx +25 -0
  214. package/studio/web/components/SidePanel.tsx +77 -0
  215. package/studio/web/components/Sidebar.tsx +289 -0
  216. package/studio/web/components/StageStepper.tsx +87 -0
  217. package/studio/web/components/StudioOffline.tsx +80 -0
  218. package/studio/web/components/TaskName.tsx +17 -0
  219. package/studio/web/components/TopBanners.tsx +30 -0
  220. package/studio/web/components/VideoOmnichatPanel.tsx +287 -0
  221. package/studio/web/components/VoiceAvatar.tsx +33 -0
  222. package/studio/web/components/VoiceCarousel.tsx +196 -0
  223. package/studio/web/components/VoiceCategory.tsx +9 -0
  224. package/studio/web/components/VoiceModal.tsx +39 -0
  225. package/studio/web/components/VoiceProfileCard.tsx +75 -0
  226. package/studio/web/components/VoiceProfilePicker.tsx +107 -0
  227. package/studio/web/components/VoiceProfilesSettings.tsx +150 -0
  228. package/studio/web/components/VoiceProviderCard.tsx +118 -0
  229. package/studio/web/components/VoiceProvidersCard.tsx +26 -0
  230. package/studio/web/components/WaitList.tsx +21 -0
  231. package/studio/web/components/icons.tsx +247 -0
  232. package/studio/web/components/ui/accordion.tsx +47 -0
  233. package/studio/web/components/ui/badge.tsx +18 -0
  234. package/studio/web/components/ui/calendar.tsx +43 -0
  235. package/studio/web/components/ui/chart.tsx +229 -0
  236. package/studio/web/components/ui/checkbox.tsx +26 -0
  237. package/studio/web/components/ui/cx.ts +2 -0
  238. package/studio/web/components/ui/date-time-picker.tsx +84 -0
  239. package/studio/web/components/ui/panel.tsx +23 -0
  240. package/studio/web/components/ui/picker.tsx +58 -0
  241. package/studio/web/components/ui/popover.tsx +24 -0
  242. package/studio/web/components/ui/select.tsx +89 -0
  243. package/studio/web/components/ui/skeleton.tsx +10 -0
  244. package/studio/web/components/ui/slider.tsx +68 -0
  245. package/studio/web/components/ui/switch.tsx +13 -0
  246. package/studio/web/components/ui/tooltip.tsx +59 -0
  247. package/studio/web/hooks/useAccounts.ts +27 -0
  248. package/studio/web/hooks/useAdmin.ts +12 -0
  249. package/studio/web/hooks/useAnalytics.ts +21 -0
  250. package/studio/web/hooks/useAvatarAudioReactive.ts +87 -0
  251. package/studio/web/hooks/useBrowserConnect.ts +48 -0
  252. package/studio/web/hooks/useClonePlan.ts +14 -0
  253. package/studio/web/hooks/useConnected.ts +7 -0
  254. package/studio/web/hooks/useLiveQuery.ts +29 -0
  255. package/studio/web/hooks/useMenuCollapsed.ts +32 -0
  256. package/studio/web/hooks/useMicDevices.ts +53 -0
  257. package/studio/web/hooks/useMissingDms.ts +211 -0
  258. package/studio/web/hooks/useMissingSetupSteps.ts +26 -0
  259. package/studio/web/hooks/useNow.ts +11 -0
  260. package/studio/web/hooks/useOmniAgent.ts +278 -0
  261. package/studio/web/hooks/useOmniClaude.ts +124 -0
  262. package/studio/web/hooks/useOmniDock.ts +142 -0
  263. package/studio/web/hooks/useOmniGreeting.ts +60 -0
  264. package/studio/web/hooks/useOmniHotword.ts +128 -0
  265. package/studio/web/hooks/useOmnichat.ts +26 -0
  266. package/studio/web/hooks/useOnboarding.ts +27 -0
  267. package/studio/web/hooks/usePost.ts +23 -0
  268. package/studio/web/hooks/useQueue.ts +10 -0
  269. package/studio/web/hooks/useReferences.ts +20 -0
  270. package/studio/web/hooks/useRuleEditor.ts +101 -0
  271. package/studio/web/hooks/useRunner.ts +24 -0
  272. package/studio/web/hooks/useSaveAction.ts +23 -0
  273. package/studio/web/hooks/useSchedule.ts +11 -0
  274. package/studio/web/hooks/useServices.ts +10 -0
  275. package/studio/web/hooks/useSessions.ts +15 -0
  276. package/studio/web/hooks/useSettings.ts +11 -0
  277. package/studio/web/hooks/useSettingsSection.ts +30 -0
  278. package/studio/web/hooks/useSidePanel.ts +23 -0
  279. package/studio/web/hooks/useSound.ts +30 -0
  280. package/studio/web/hooks/useUsage.ts +11 -0
  281. package/studio/web/hooks/useVideoVoice.ts +36 -0
  282. package/studio/web/hooks/useVideos.ts +21 -0
  283. package/studio/web/hooks/useVoiceProfilePreview.ts +52 -0
  284. package/studio/web/hooks/useVoiceProfiles.ts +7 -0
  285. package/studio/web/hooks/useVoiceRunning.ts +10 -0
  286. package/studio/web/hooks/useVoiceStudioClosed.ts +8 -0
  287. package/studio/web/index.html +19 -0
  288. package/studio/web/lib/agent.ts +7 -0
  289. package/studio/web/lib/alerts.ts +210 -0
  290. package/studio/web/lib/analytics-metrics.ts +351 -0
  291. package/studio/web/lib/api.ts +70 -0
  292. package/studio/web/lib/audio.ts +39 -0
  293. package/studio/web/lib/avatars.ts +41 -0
  294. package/studio/web/lib/calendar.ts +218 -0
  295. package/studio/web/lib/caption-position.ts +249 -0
  296. package/studio/web/lib/carousel.ts +24 -0
  297. package/studio/web/lib/chime.ts +42 -0
  298. package/studio/web/lib/clone-voice.ts +55 -0
  299. package/studio/web/lib/export.ts +11 -0
  300. package/studio/web/lib/files.ts +8 -0
  301. package/studio/web/lib/format.ts +93 -0
  302. package/studio/web/lib/media.ts +29 -0
  303. package/studio/web/lib/mic-device.ts +72 -0
  304. package/studio/web/lib/missing-dms.ts +70 -0
  305. package/studio/web/lib/omni-cards.ts +42 -0
  306. package/studio/web/lib/omni-cascade.ts +1067 -0
  307. package/studio/web/lib/omni-greeting-audio.ts +24 -0
  308. package/studio/web/lib/omni-hotword.ts +231 -0
  309. package/studio/web/lib/omni-panel.ts +67 -0
  310. package/studio/web/lib/omni-realtime.ts +811 -0
  311. package/studio/web/lib/omni-ui.ts +402 -0
  312. package/studio/web/lib/omni-wait.ts +174 -0
  313. package/studio/web/lib/omnichat-api.ts +44 -0
  314. package/studio/web/lib/orb.ts +270 -0
  315. package/studio/web/lib/pcm-recorder.ts +115 -0
  316. package/studio/web/lib/platforms.ts +17 -0
  317. package/studio/web/lib/providers.ts +27 -0
  318. package/studio/web/lib/publish-draft.ts +52 -0
  319. package/studio/web/lib/publish-settings.ts +81 -0
  320. package/studio/web/lib/query-client.ts +122 -0
  321. package/studio/web/lib/references.ts +13 -0
  322. package/studio/web/lib/services.ts +40 -0
  323. package/studio/web/lib/sessions.ts +103 -0
  324. package/studio/web/lib/settings-categories.ts +58 -0
  325. package/studio/web/lib/sound-audio.ts +33 -0
  326. package/studio/web/lib/sound-timeline.ts +291 -0
  327. package/studio/web/lib/sse.ts +86 -0
  328. package/studio/web/lib/stage-history.ts +141 -0
  329. package/studio/web/lib/stages.ts +344 -0
  330. package/studio/web/lib/studio-offline.ts +60 -0
  331. package/studio/web/lib/upload.ts +24 -0
  332. package/studio/web/lib/video-tabs.ts +118 -0
  333. package/studio/web/lib/voice-profile-draft.ts +34 -0
  334. package/studio/web/lib/voice-profiles.ts +3 -0
  335. package/studio/web/main.tsx +14 -0
  336. package/studio/web/pages/AdminPage/index.tsx +123 -0
  337. package/studio/web/pages/AgentPage/components/Measurements.tsx +102 -0
  338. package/studio/web/pages/AgentPage/index.tsx +28 -0
  339. package/studio/web/pages/AnalyticsPage/components/About.tsx +52 -0
  340. package/studio/web/pages/AnalyticsPage/components/ChannelCards.tsx +106 -0
  341. package/studio/web/pages/AnalyticsPage/components/ChannelView.tsx +137 -0
  342. package/studio/web/pages/AnalyticsPage/components/CollectionStatus.tsx +46 -0
  343. package/studio/web/pages/AnalyticsPage/components/Comparison.tsx +68 -0
  344. package/studio/web/pages/AnalyticsPage/components/Diagnosis.tsx +50 -0
  345. package/studio/web/pages/AnalyticsPage/components/Highlights.tsx +49 -0
  346. package/studio/web/pages/AnalyticsPage/components/Hint.tsx +11 -0
  347. package/studio/web/pages/AnalyticsPage/components/HookVsBody.tsx +100 -0
  348. package/studio/web/pages/AnalyticsPage/components/HourlyWave.tsx +133 -0
  349. package/studio/web/pages/AnalyticsPage/components/Insights.tsx +70 -0
  350. package/studio/web/pages/AnalyticsPage/components/MetricCard.tsx +46 -0
  351. package/studio/web/pages/AnalyticsPage/components/PlanSection.tsx +40 -0
  352. package/studio/web/pages/AnalyticsPage/components/ProgressionChart.tsx +155 -0
  353. package/studio/web/pages/AnalyticsPage/components/ReelBarsChart.tsx +154 -0
  354. package/studio/web/pages/AnalyticsPage/components/ReelThumb.tsx +19 -0
  355. package/studio/web/pages/AnalyticsPage/components/ReelView.tsx +290 -0
  356. package/studio/web/pages/AnalyticsPage/components/ReelsTable.tsx +269 -0
  357. package/studio/web/pages/AnalyticsPage/components/RequestWithFocus.tsx +38 -0
  358. package/studio/web/pages/AnalyticsPage/components/SegmentedControl.tsx +35 -0
  359. package/studio/web/pages/AnalyticsPage/components/VsMedian.tsx +31 -0
  360. package/studio/web/pages/AnalyticsPage/index.tsx +17 -0
  361. package/studio/web/pages/GettingStartedPage/index.tsx +174 -0
  362. package/studio/web/pages/NewVideoPage/components/DeliverablesPicker.tsx +62 -0
  363. package/studio/web/pages/NewVideoPage/components/FormatPicker.tsx +33 -0
  364. package/studio/web/pages/NewVideoPage/index.tsx +133 -0
  365. package/studio/web/pages/NotFoundPage/index.tsx +9 -0
  366. package/studio/web/pages/OmnichatPage/components/NewFromLink.tsx +59 -0
  367. package/studio/web/pages/OmnichatPage/components/RuleListItem.tsx +64 -0
  368. package/studio/web/pages/OmnichatPage/components/RulePreview.tsx +14 -0
  369. package/studio/web/pages/OmnichatPage/components/RuleView.tsx +88 -0
  370. package/studio/web/pages/OmnichatPage/index.tsx +153 -0
  371. package/studio/web/pages/QueuePage/index.tsx +22 -0
  372. package/studio/web/pages/ReferencePage/components/ReferenceTexts.tsx +131 -0
  373. package/studio/web/pages/ReferencePage/index.tsx +216 -0
  374. package/studio/web/pages/ReferencesPage/components/NewReferenceForm.tsx +150 -0
  375. package/studio/web/pages/ReferencesPage/components/ReferenceCard.tsx +42 -0
  376. package/studio/web/pages/ReferencesPage/components/ReferenceGridSkeleton.tsx +31 -0
  377. package/studio/web/pages/ReferencesPage/index.tsx +67 -0
  378. package/studio/web/pages/SchedulePage/components/CalendarSkeleton.tsx +18 -0
  379. package/studio/web/pages/SchedulePage/components/DayView.tsx +177 -0
  380. package/studio/web/pages/SchedulePage/components/HourPicker.tsx +103 -0
  381. package/studio/web/pages/SchedulePage/components/MonthDayCell.tsx +111 -0
  382. package/studio/web/pages/SchedulePage/components/MonthView.tsx +47 -0
  383. package/studio/web/pages/SchedulePage/components/PlatformIcons.tsx +12 -0
  384. package/studio/web/pages/SchedulePage/components/PostThumb.tsx +15 -0
  385. package/studio/web/pages/SchedulePage/components/WeekColumn.tsx +75 -0
  386. package/studio/web/pages/SchedulePage/components/WeekView.tsx +35 -0
  387. package/studio/web/pages/SchedulePage/index.tsx +285 -0
  388. package/studio/web/pages/SettingsPage/components/AgentAccounts.tsx +263 -0
  389. package/studio/web/pages/SettingsPage/components/AgentCategory.tsx +20 -0
  390. package/studio/web/pages/SettingsPage/components/ModelPicker.tsx +49 -0
  391. package/studio/web/pages/SettingsPage/components/ModelsSettings.tsx +138 -0
  392. package/studio/web/pages/SettingsPage/components/PlatformsCategory.tsx +23 -0
  393. package/studio/web/pages/SettingsPage/components/PublishingCategory/AccountStatus.tsx +29 -0
  394. package/studio/web/pages/SettingsPage/components/PublishingCategory/AutoPublishPanel.tsx +90 -0
  395. package/studio/web/pages/SettingsPage/components/PublishingCategory/BrowserConnect.tsx +62 -0
  396. package/studio/web/pages/SettingsPage/components/PublishingCategory/CheckField.tsx +19 -0
  397. package/studio/web/pages/SettingsPage/components/PublishingCategory/InstagramPanel.tsx +113 -0
  398. package/studio/web/pages/SettingsPage/components/PublishingCategory/OmnichatSettingsPanel.tsx +146 -0
  399. package/studio/web/pages/SettingsPage/components/PublishingCategory/SaveBar.tsx +33 -0
  400. package/studio/web/pages/SettingsPage/components/PublishingCategory/SchedulePanel.tsx +132 -0
  401. package/studio/web/pages/SettingsPage/components/PublishingCategory/SettingsCard.tsx +47 -0
  402. package/studio/web/pages/SettingsPage/components/PublishingCategory/SettingsCardGroup.tsx +19 -0
  403. package/studio/web/pages/SettingsPage/components/PublishingCategory/TextPanel.tsx +55 -0
  404. package/studio/web/pages/SettingsPage/components/PublishingCategory/TiktokPanel.tsx +123 -0
  405. package/studio/web/pages/SettingsPage/components/PublishingCategory/YoutubePanel.tsx +100 -0
  406. package/studio/web/pages/SettingsPage/components/PublishingCategory/badges.tsx +15 -0
  407. package/studio/web/pages/SettingsPage/components/PublishingCategory/index.tsx +40 -0
  408. package/studio/web/pages/SettingsPage/components/PublishingCategory/types.ts +10 -0
  409. package/studio/web/pages/SettingsPage/components/UsageCategory.tsx +389 -0
  410. package/studio/web/pages/SettingsPage/components/VoiceProvidersCategory.tsx +97 -0
  411. package/studio/web/pages/SettingsPage/index.tsx +85 -0
  412. package/studio/web/pages/VideoPage/components/AudioPlayer.tsx +113 -0
  413. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/AxisField.tsx +50 -0
  414. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/CaptionList.tsx +56 -0
  415. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/CaptionListItem.tsx +92 -0
  416. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/CaptionMargins.tsx +57 -0
  417. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/CaptionStyleFields.tsx +70 -0
  418. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/CaptionTextForm.tsx +87 -0
  419. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/ColorField.tsx +37 -0
  420. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/MarginField.tsx +59 -0
  421. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/NumberField.tsx +57 -0
  422. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/PositionControls.tsx +140 -0
  423. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/captionEditor.ts +215 -0
  424. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/useCaptionPosition.ts +154 -0
  425. package/studio/web/pages/VideoPage/components/CarouselChangeRequest.tsx +72 -0
  426. package/studio/web/pages/VideoPage/components/CarouselCompositionStage.tsx +80 -0
  427. package/studio/web/pages/VideoPage/components/CarouselCreate.tsx +63 -0
  428. package/studio/web/pages/VideoPage/components/CarouselMissing.tsx +19 -0
  429. package/studio/web/pages/VideoPage/components/CarouselPublishStage.tsx +173 -0
  430. package/studio/web/pages/VideoPage/components/CarouselScriptStage.tsx +82 -0
  431. package/studio/web/pages/VideoPage/components/CarouselSlideCard.tsx +49 -0
  432. package/studio/web/pages/VideoPage/components/CarouselViewer.tsx +55 -0
  433. package/studio/web/pages/VideoPage/components/ChangedVoices.tsx +53 -0
  434. package/studio/web/pages/VideoPage/components/CompositionStage.tsx +78 -0
  435. package/studio/web/pages/VideoPage/components/CoverPicker.tsx +32 -0
  436. package/studio/web/pages/VideoPage/components/EditorStage.tsx +53 -0
  437. package/studio/web/pages/VideoPage/components/LineTakes.tsx +160 -0
  438. package/studio/web/pages/VideoPage/components/LineTextEditor.tsx +124 -0
  439. package/studio/web/pages/VideoPage/components/LineVoicePicker.tsx +37 -0
  440. package/studio/web/pages/VideoPage/components/MediaImage.tsx +25 -0
  441. package/studio/web/pages/VideoPage/components/MotionScriptStage.tsx +39 -0
  442. package/studio/web/pages/VideoPage/components/NarrationStage.tsx +70 -0
  443. package/studio/web/pages/VideoPage/components/PublishForm.tsx +387 -0
  444. package/studio/web/pages/VideoPage/components/PublishStage.tsx +76 -0
  445. package/studio/web/pages/VideoPage/components/PublishText.tsx +149 -0
  446. package/studio/web/pages/VideoPage/components/ScenesApprovalPanel.tsx +278 -0
  447. package/studio/web/pages/VideoPage/components/ScheduledInfo.tsx +72 -0
  448. package/studio/web/pages/VideoPage/components/ScriptApprovalPanel.tsx +189 -0
  449. package/studio/web/pages/VideoPage/components/ScriptStage.tsx +17 -0
  450. package/studio/web/pages/VideoPage/components/SequencePlayer.tsx +146 -0
  451. package/studio/web/pages/VideoPage/components/SoundTimeline/AddSound.tsx +117 -0
  452. package/studio/web/pages/VideoPage/components/SoundTimeline/AreaSelection.tsx +74 -0
  453. package/studio/web/pages/VideoPage/components/SoundTimeline/BedPanel.tsx +94 -0
  454. package/studio/web/pages/VideoPage/components/SoundTimeline/BedRow.tsx +45 -0
  455. package/studio/web/pages/VideoPage/components/SoundTimeline/CaptionOverlay.tsx +44 -0
  456. package/studio/web/pages/VideoPage/components/SoundTimeline/CaptionSaveBar.tsx +32 -0
  457. package/studio/web/pages/VideoPage/components/SoundTimeline/CaptionsPanel.tsx +22 -0
  458. package/studio/web/pages/VideoPage/components/SoundTimeline/ClipBlock.tsx +111 -0
  459. package/studio/web/pages/VideoPage/components/SoundTimeline/ClipPanel.tsx +76 -0
  460. package/studio/web/pages/VideoPage/components/SoundTimeline/CutsPanel.tsx +44 -0
  461. package/studio/web/pages/VideoPage/components/SoundTimeline/EditorTabBar.tsx +73 -0
  462. package/studio/web/pages/VideoPage/components/SoundTimeline/EffectChoice.tsx +46 -0
  463. package/studio/web/pages/VideoPage/components/SoundTimeline/EffectItem.tsx +89 -0
  464. package/studio/web/pages/VideoPage/components/SoundTimeline/EffectPanel.tsx +232 -0
  465. package/studio/web/pages/VideoPage/components/SoundTimeline/EffectsRow.tsx +21 -0
  466. package/studio/web/pages/VideoPage/components/SoundTimeline/Lane.tsx +39 -0
  467. package/studio/web/pages/VideoPage/components/SoundTimeline/MarkerRow.tsx +40 -0
  468. package/studio/web/pages/VideoPage/components/SoundTimeline/MuteButton.tsx +36 -0
  469. package/studio/web/pages/VideoPage/components/SoundTimeline/NumberInput.tsx +37 -0
  470. package/studio/web/pages/VideoPage/components/SoundTimeline/PlanWarnings.tsx +19 -0
  471. package/studio/web/pages/VideoPage/components/SoundTimeline/PlayerResizeHandle.tsx +50 -0
  472. package/studio/web/pages/VideoPage/components/SoundTimeline/PreviewControls.tsx +51 -0
  473. package/studio/web/pages/VideoPage/components/SoundTimeline/SceneRow.tsx +54 -0
  474. package/studio/web/pages/VideoPage/components/SoundTimeline/SettingsPanel.tsx +65 -0
  475. package/studio/web/pages/VideoPage/components/SoundTimeline/SoundLibrary.tsx +99 -0
  476. package/studio/web/pages/VideoPage/components/SoundTimeline/SoundPreview.tsx +82 -0
  477. package/studio/web/pages/VideoPage/components/SoundTimeline/SpeedPanel.tsx +128 -0
  478. package/studio/web/pages/VideoPage/components/SoundTimeline/TimelineDock.tsx +56 -0
  479. package/studio/web/pages/VideoPage/components/SoundTimeline/TimelineFooter.tsx +26 -0
  480. package/studio/web/pages/VideoPage/components/SoundTimeline/TimelineSkeleton.tsx +21 -0
  481. package/studio/web/pages/VideoPage/components/SoundTimeline/TimelineToolbar.tsx +160 -0
  482. package/studio/web/pages/VideoPage/components/SoundTimeline/TimelineTracks.tsx +121 -0
  483. package/studio/web/pages/VideoPage/components/SoundTimeline/TrackSidebar.tsx +30 -0
  484. package/studio/web/pages/VideoPage/components/SoundTimeline/VoiceRow.tsx +53 -0
  485. package/studio/web/pages/VideoPage/components/SoundTimeline/Waveform.tsx +97 -0
  486. package/studio/web/pages/VideoPage/components/SoundTimeline/clipActions.ts +50 -0
  487. package/studio/web/pages/VideoPage/components/SoundTimeline/effectActions.ts +161 -0
  488. package/studio/web/pages/VideoPage/components/SoundTimeline/index.tsx +161 -0
  489. package/studio/web/pages/VideoPage/components/SoundTimeline/timelineGeometry.ts +45 -0
  490. package/studio/web/pages/VideoPage/components/SoundTimeline/types.ts +43 -0
  491. package/studio/web/pages/VideoPage/components/SoundTimeline/useAutosave.ts +107 -0
  492. package/studio/web/pages/VideoPage/components/SoundTimeline/useEditorShortcuts.ts +168 -0
  493. package/studio/web/pages/VideoPage/components/SoundTimeline/useFitHeight.ts +40 -0
  494. package/studio/web/pages/VideoPage/components/SoundTimeline/useFollowPlayhead.ts +21 -0
  495. package/studio/web/pages/VideoPage/components/SoundTimeline/useListenMix.ts +43 -0
  496. package/studio/web/pages/VideoPage/components/SoundTimeline/usePlayback.ts +231 -0
  497. package/studio/web/pages/VideoPage/components/SoundTimeline/usePlayerWidth.ts +27 -0
  498. package/studio/web/pages/VideoPage/components/SoundTimeline/useSoundData.ts +124 -0
  499. package/studio/web/pages/VideoPage/components/SoundTimeline/useSoundTimeline.ts +511 -0
  500. package/studio/web/pages/VideoPage/components/StageDrawer/CompareVersions.tsx +111 -0
  501. package/studio/web/pages/VideoPage/components/StageDrawer/VersionCard.tsx +81 -0
  502. package/studio/web/pages/VideoPage/components/StageDrawer/VersionMedia.tsx +24 -0
  503. package/studio/web/pages/VideoPage/components/StageDrawer/VersionText.tsx +15 -0
  504. package/studio/web/pages/VideoPage/components/StageDrawer/index.tsx +109 -0
  505. package/studio/web/pages/VideoPage/components/StageProcess.tsx +126 -0
  506. package/studio/web/pages/VideoPage/components/StageResult.tsx +69 -0
  507. package/studio/web/pages/VideoPage/components/SwapReference.tsx +92 -0
  508. package/studio/web/pages/VideoPage/components/ThumbnailPanel.tsx +252 -0
  509. package/studio/web/pages/VideoPage/components/TimingPanel.tsx +89 -0
  510. package/studio/web/pages/VideoPage/components/VideoPlayer.tsx +18 -0
  511. package/studio/web/pages/VideoPage/components/VideoSkeleton.tsx +50 -0
  512. package/studio/web/pages/VideoPage/components/VideoVoiceInfo.tsx +33 -0
  513. package/studio/web/pages/VideoPage/components/VideoVoicePicker.tsx +165 -0
  514. package/studio/web/pages/VideoPage/components/VoicePauseBanner.tsx +56 -0
  515. package/studio/web/pages/VideoPage/components/WithLogo.tsx +10 -0
  516. package/studio/web/pages/VideoPage/index.tsx +295 -0
  517. package/studio/web/pages/VideosPage/components/PublishedOn.tsx +85 -0
  518. package/studio/web/pages/VideosPage/components/ServicesPanel.tsx +81 -0
  519. package/studio/web/pages/VideosPage/components/SessionsPanel.tsx +62 -0
  520. package/studio/web/pages/VideosPage/components/VideoListSkeleton.tsx +25 -0
  521. package/studio/web/pages/VideosPage/components/VideoRow.tsx +122 -0
  522. package/studio/web/pages/VideosPage/index.tsx +134 -0
  523. package/studio/web/pages/VoicePage/index.tsx +28 -0
  524. package/studio/web/router.tsx +201 -0
  525. package/studio/web/styles/base/elements.css +55 -0
  526. package/studio/web/styles/base/utilities.css +25 -0
  527. package/studio/web/styles/components/badge.css +33 -0
  528. package/studio/web/styles/components/claude-running.css +27 -0
  529. package/studio/web/styles/components/claude-session.css +212 -0
  530. package/studio/web/styles/components/claude-walking.css +92 -0
  531. package/studio/web/styles/components/clone-voice-save.css +136 -0
  532. package/studio/web/styles/components/clone-voice.css +379 -0
  533. package/studio/web/styles/components/controls.css +70 -0
  534. package/studio/web/styles/components/data-and-image.css +54 -0
  535. package/studio/web/styles/components/forms.css +132 -0
  536. package/studio/web/styles/components/human-banner.css +99 -0
  537. package/studio/web/styles/components/lightbox.css +50 -0
  538. package/studio/web/styles/components/markdown.css +68 -0
  539. package/studio/web/styles/components/modal.css +70 -0
  540. package/studio/web/styles/components/omni-cards.css +105 -0
  541. package/studio/web/styles/components/omni.css +859 -0
  542. package/studio/web/styles/components/omnichat.css +538 -0
  543. package/studio/web/styles/components/panel.css +115 -0
  544. package/studio/web/styles/components/platform-card.css +140 -0
  545. package/studio/web/styles/components/provider-card.css +132 -0
  546. package/studio/web/styles/components/queue.css +185 -0
  547. package/studio/web/styles/components/reference-actions.css +133 -0
  548. package/studio/web/styles/components/reference-picker.css +146 -0
  549. package/studio/web/styles/components/results-drawer.css +224 -0
  550. package/studio/web/styles/components/segmented.css +115 -0
  551. package/studio/web/styles/components/side-panel-tab.css +23 -0
  552. package/studio/web/styles/components/status-signals.css +66 -0
  553. package/studio/web/styles/components/stepper.css +208 -0
  554. package/studio/web/styles/components/table.css +19 -0
  555. package/studio/web/styles/components/tabs.css +24 -0
  556. package/studio/web/styles/components/ui/accordion.css +71 -0
  557. package/studio/web/styles/components/ui/chart.css +162 -0
  558. package/studio/web/styles/components/ui/checkbox.css +37 -0
  559. package/studio/web/styles/components/ui/date-picker.css +153 -0
  560. package/studio/web/styles/components/ui/select.css +110 -0
  561. package/studio/web/styles/components/ui/skeleton.css +25 -0
  562. package/studio/web/styles/components/ui/slider.css +81 -0
  563. package/studio/web/styles/components/ui/switch.css +41 -0
  564. package/studio/web/styles/components/ui/tooltip.css +24 -0
  565. package/studio/web/styles/components/voice-carousel.css +255 -0
  566. package/studio/web/styles/components/voice-editor.css +179 -0
  567. package/studio/web/styles/components/voice-grid.css +56 -0
  568. package/studio/web/styles/components/voice-profiles.css +192 -0
  569. package/studio/web/styles/design-system.md +136 -0
  570. package/studio/web/styles/index.css +116 -0
  571. package/studio/web/styles/layout/page.css +41 -0
  572. package/studio/web/styles/layout/sidebar-groups.css +39 -0
  573. package/studio/web/styles/layout/sidebar.css +111 -0
  574. package/studio/web/styles/pages/agent.css +26 -0
  575. package/studio/web/styles/pages/analytics/dashboard.css +614 -0
  576. package/studio/web/styles/pages/analytics/insights.css +48 -0
  577. package/studio/web/styles/pages/getting-started.css +166 -0
  578. package/studio/web/styles/pages/new-video/form.css +32 -0
  579. package/studio/web/styles/pages/not-found.css +59 -0
  580. package/studio/web/styles/pages/references.css +204 -0
  581. package/studio/web/styles/pages/schedule/calendar.css +541 -0
  582. package/studio/web/styles/pages/schedule/chip-states.css +60 -0
  583. package/studio/web/styles/pages/settings/models.css +72 -0
  584. package/studio/web/styles/pages/settings/shell.css +361 -0
  585. package/studio/web/styles/pages/settings/usage.css +63 -0
  586. package/studio/web/styles/pages/studio-offline.css +105 -0
  587. package/studio/web/styles/pages/video/caption-controls.css +194 -0
  588. package/studio/web/styles/pages/video/captions-grid.css +70 -0
  589. package/studio/web/styles/pages/video/captions.css +79 -0
  590. package/studio/web/styles/pages/video/editor-chat.css +103 -0
  591. package/studio/web/styles/pages/video/export.css +50 -0
  592. package/studio/web/styles/pages/video/frames.css +29 -0
  593. package/studio/web/styles/pages/video/fullscreen-stage.css +400 -0
  594. package/studio/web/styles/pages/video/instagram-carousel.css +309 -0
  595. package/studio/web/styles/pages/video/layout.css +285 -0
  596. package/studio/web/styles/pages/video/narration.css +138 -0
  597. package/studio/web/styles/pages/video/preview-overview.css +11 -0
  598. package/studio/web/styles/pages/video/processing-notice.css +34 -0
  599. package/studio/web/styles/pages/video/publish-when.css +76 -0
  600. package/studio/web/styles/pages/video/publish.css +97 -0
  601. package/studio/web/styles/pages/video/result.css +96 -0
  602. package/studio/web/styles/pages/video/scenes.css +167 -0
  603. package/studio/web/styles/pages/video/sound-timeline.css +672 -0
  604. package/studio/web/styles/pages/video/sound-toolbar.css +149 -0
  605. package/studio/web/styles/pages/video/take-player.css +100 -0
  606. package/studio/web/styles/pages/video/thumbnail.css +157 -0
  607. package/studio/web/styles/pages/video/timeline.css +117 -0
  608. package/studio/web/styles/pages/video/voice-picker.css +14 -0
  609. package/studio/web/styles/pages/video-list/active-row.css +27 -0
  610. package/studio/web/styles/pages/video-list/card-link.css +49 -0
  611. package/studio/web/styles/pages/video-list/list.css +86 -0
  612. package/studio/web/styles/theme/identity/details.css +204 -0
  613. package/studio/web/styles/theme/identity/shell.css +249 -0
  614. package/studio/web/styles/theme/identity/surfaces.css +183 -0
  615. package/studio/web/styles/theme/identity/titles-and-buttons.css +120 -0
  616. package/studio/web/styles/theme/no-borders/blocks.css +208 -0
  617. package/studio/web/styles/theme/no-borders/controls.css +256 -0
  618. package/studio/web/styles/tokens.css +245 -0
  619. package/studio/web/types/api.ts +2 -0
  620. package/studio/web/types/assets.d.ts +9 -0
  621. package/templates/brief.md +27 -0
  622. package/templates/captions.html +204 -0
  623. package/templates/carousel-composition.md +47 -0
  624. package/templates/carousel-script.md +60 -0
  625. package/templates/components/voiceavatar/README.md +87 -0
  626. package/templates/components/voiceavatar/demo.html +50 -0
  627. package/templates/components/voiceavatar/voiceavatar.js +415 -0
  628. package/templates/motion.md +80 -0
  629. package/templates/playbook.md +43 -0
  630. package/templates/reference.md +123 -0
  631. package/templates/scene.html +112 -0
  632. package/templates/scenes.example.json +96 -0
  633. package/templates/script.md +63 -0
  634. package/templates/slide.html +71 -0
  635. package/templates/wireframe.html +91 -0
  636. package/tools/analysis.mjs +300 -0
  637. package/tools/approval.mjs +442 -0
  638. package/tools/assemble.mjs +942 -0
  639. package/tools/build-motion.mjs +437 -0
  640. package/tools/captions.mjs +246 -0
  641. package/tools/carousel.mjs +558 -0
  642. package/tools/compose.mjs +307 -0
  643. package/tools/cuts.mjs +74 -0
  644. package/tools/editor.mjs +419 -0
  645. package/tools/epidemic-capture.js +54 -0
  646. package/tools/fanout.mjs +69 -0
  647. package/tools/history.mjs +70 -0
  648. package/tools/history.py +61 -0
  649. package/tools/job-queue.mjs +180 -0
  650. package/tools/job_queue.py +430 -0
  651. package/tools/lib/agents.mjs +412 -0
  652. package/tools/lib/channel.mjs +399 -0
  653. package/tools/lib/claude-settings.mjs +67 -0
  654. package/tools/lib/cli.mjs +9 -0
  655. package/tools/lib/fanout.mjs +234 -0
  656. package/tools/lib/http.mjs +94 -0
  657. package/tools/lib/index.js +14 -0
  658. package/tools/lib/mcp-server.mjs +69 -0
  659. package/tools/lib/metrics/metrics.mjs +204 -0
  660. package/tools/lib/narration.mjs +15 -0
  661. package/tools/lib/oauth-client.mjs +295 -0
  662. package/tools/lib/omnichat/config.mjs +139 -0
  663. package/tools/lib/omnichat/errors.mjs +42 -0
  664. package/tools/lib/omnichat/extract.mjs +82 -0
  665. package/tools/lib/omnichat/gate.mjs +151 -0
  666. package/tools/lib/omnichat/manual.mjs +166 -0
  667. package/tools/lib/omnichat/match.mjs +78 -0
  668. package/tools/lib/omnichat/pass.mjs +393 -0
  669. package/tools/lib/omnichat/record.mjs +208 -0
  670. package/tools/lib/omnichat/text.mjs +315 -0
  671. package/tools/lib/omnichat/webhook.mjs +166 -0
  672. package/tools/lib/pairing.mjs +99 -0
  673. package/tools/lib/paths.mjs +41 -0
  674. package/tools/lib/platforms/instagram.mjs +469 -0
  675. package/tools/lib/platforms/tiktok.mjs +343 -0
  676. package/tools/lib/platforms/youtube.mjs +228 -0
  677. package/tools/lib/publish/publish.mjs +244 -0
  678. package/tools/lib/schedule/schedule.mjs +191 -0
  679. package/tools/lib/service.mjs +134 -0
  680. package/tools/lib/skill-client.mjs +126 -0
  681. package/tools/lib/skills-release.mjs +196 -0
  682. package/tools/lib/testing.mjs +52 -0
  683. package/tools/lib/up.mjs +187 -0
  684. package/tools/metrics.mjs +415 -0
  685. package/tools/models.mjs +134 -0
  686. package/tools/narrate.mjs +257 -0
  687. package/tools/omni.mjs +298 -0
  688. package/tools/omni_paths.py +12 -0
  689. package/tools/omnichat.mjs +1577 -0
  690. package/tools/omnivideo.py +1122 -0
  691. package/tools/package.mjs +130 -0
  692. package/tools/platforms.mjs +236 -0
  693. package/tools/premiere.mjs +533 -0
  694. package/tools/publish.mjs +1496 -0
  695. package/tools/reference.mjs +1408 -0
  696. package/tools/render.mjs +426 -0
  697. package/tools/runner.mjs +156 -0
  698. package/tools/setup.mjs +466 -0
  699. package/tools/skills-release.mjs +93 -0
  700. package/tools/sound.mjs +1323 -0
  701. package/tools/soundtrack.mjs +217 -0
  702. package/tools/thumbnail.mjs +271 -0
  703. package/tools/timing.mjs +108 -0
  704. package/tools/usage.py +628 -0
  705. package/tools/voice-envelope.mjs +185 -0
  706. package/tools/voice.py +448 -0
  707. package/tools/voice_sample.py +75 -0
  708. package/tools/wireframe.mjs +230 -0
@@ -0,0 +1,185 @@
1
+ #!/usr/bin/env node
2
+ /* ============================================================================
3
+ Voice envelope for the VoiceAvatar component (templates/components/voiceavatar).
4
+
5
+ node tools/voice-envelope.mjs voice.wav [more.wav …] --out env.js
6
+ node tools/voice-envelope.mjs --scene videos/<slug> <sceneId> [--out env.js]
7
+
8
+ Writes { fps: 60, duration, low, mid, high, all }: four per-frame arrays, 0..1.
9
+ It emulates what the live orb reads from a browser AnalyserNode
10
+ (sampleRate 48 kHz, fftSize 256, Blackman window, smoothingTimeConstant 0.8,
11
+ -100…-30 dB mapped to a byte) and averages the same bands: low 0–200 Hz,
12
+ mid 200–2000 Hz, high 2–20 kHz, all = whole spectrum. Doing it here, once,
13
+ makes the render deterministic: the scene never decodes audio.
14
+
15
+ Several WAVs are laid end to end; in --scene mode each line of the scene
16
+ goes at its `start` (scenes.json), so the envelope is on the scene's clock.
17
+
18
+ --out file.json writes JSON; --out file.js writes `window.VOICE_ENVELOPE = …`
19
+ (what a scene loads with <script src>); no --out prints JSON to stdout.
20
+ Requires ffmpeg.
21
+ ============================================================================ */
22
+
23
+ import { spawnSync } from "node:child_process";
24
+ import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
25
+ import { dirname, join, resolve } from "node:path";
26
+
27
+ const SR = 48000;
28
+ const FPS = 60;
29
+ const FFT = 256;
30
+ const BINS = FFT / 2;
31
+ const SMOOTH = 0.8;
32
+ const MIN_DB = -100;
33
+ const MAX_DB = -30;
34
+ const STEP = SR / FPS; /* 800 samples per frame */
35
+
36
+ /* --- arguments ------------------------------------------------------------- */
37
+ const argv = process.argv.slice(2);
38
+ const flag = n => {
39
+ const i = argv.indexOf(`--${n}`);
40
+ return i < 0 ? null : argv[i + 1];
41
+ };
42
+ const sceneDir = flag("scene");
43
+ const out = flag("out");
44
+ const positional = [];
45
+ for (let i = 0; i < argv.length; i++) {
46
+ if (argv[i].startsWith("--")) {
47
+ i++;
48
+ continue;
49
+ }
50
+ positional.push(argv[i]);
51
+ }
52
+
53
+ const fail = msg => {
54
+ console.error(`erro: ${msg}`);
55
+ process.exit(1);
56
+ };
57
+
58
+ /* --- decode: any audio → mono float32 at 48 kHz ------------------------------ */
59
+ function decode(file) {
60
+ const r = spawnSync("ffmpeg", ["-v", "error", "-i", file, "-f", "f32le", "-ac", "1", "-ar", String(SR), "-"], {
61
+ maxBuffer: 1 << 30,
62
+ });
63
+ if (r.status !== 0) fail(`ffmpeg não leu ${file}: ${r.stderr}`);
64
+ const b = r.stdout;
65
+ return new Float32Array(b.buffer, b.byteOffset, Math.floor(b.byteLength / 4));
66
+ }
67
+
68
+ /* --- the signal: the files placed on one timeline --------------------------- */
69
+ const parts = []; /* { start (s), samples } */
70
+ if (sceneDir) {
71
+ const dir = resolve(sceneDir);
72
+ const video = JSON.parse(readFileSync(join(dir, "scenes.json"), "utf8"));
73
+ const scene = video.scenes.find(s => s.id === positional[0]);
74
+ if (!scene) fail(`cena "${positional[0]}" não existe em ${dir}/scenes.json`);
75
+ for (const l of scene.lines ?? []) {
76
+ if (!l.wav || l.start == null) continue;
77
+ parts.push({ start: l.start, samples: decode(join(dir, l.wav)) });
78
+ }
79
+ if (!parts.length) fail(`a cena ${scene.id} não tem fala com áudio e posição (rode a etapa de voz)`);
80
+ } else {
81
+ if (!positional.length) {
82
+ console.error("uso: node voice-envelope.mjs <audio…> [--out env.js]");
83
+ console.error(" node voice-envelope.mjs --scene videos/<slug> <sceneId> [--out env.js]");
84
+ process.exit(1);
85
+ }
86
+ let at = 0;
87
+ for (const f of positional) {
88
+ const samples = decode(f);
89
+ parts.push({ start: at, samples });
90
+ at += samples.length / SR;
91
+ }
92
+ }
93
+ const total = Math.max(...parts.map(p => Math.round(p.start * SR) + p.samples.length));
94
+ const signal = new Float32Array(total);
95
+ for (const p of parts) {
96
+ const o = Math.round(p.start * SR);
97
+ for (let i = 0; i < p.samples.length; i++) signal[o + i] += p.samples[i];
98
+ }
99
+
100
+ /* --- analyser emulation ------------------------------------------------------ */
101
+ const window_ = Float32Array.from({ length: FFT }, (_, n) => {
102
+ const a = 0.16;
103
+ return (1 - a) / 2 - 0.5 * Math.cos((2 * Math.PI * n) / FFT) + (a / 2) * Math.cos((4 * Math.PI * n) / FFT);
104
+ });
105
+
106
+ /* iterative radix-2 FFT, in place */
107
+ function fft(re, im) {
108
+ const n = re.length;
109
+ for (let i = 1, j = 0; i < n; i++) {
110
+ let bit = n >> 1;
111
+ for (; j & bit; bit >>= 1) j ^= bit;
112
+ j ^= bit;
113
+ if (i < j) {
114
+ [re[i], re[j]] = [re[j], re[i]];
115
+ [im[i], im[j]] = [im[j], im[i]];
116
+ }
117
+ }
118
+ for (let len = 2; len <= n; len <<= 1) {
119
+ const ang = (-2 * Math.PI) / len;
120
+ const wr = Math.cos(ang);
121
+ const wi = Math.sin(ang);
122
+ for (let i = 0; i < n; i += len) {
123
+ let cr = 1;
124
+ let ci = 0;
125
+ for (let k = 0; k < len / 2; k++) {
126
+ const a = i + k;
127
+ const b = a + len / 2;
128
+ const tr = re[b] * cr - im[b] * ci;
129
+ const ti = re[b] * ci + im[b] * cr;
130
+ re[b] = re[a] - tr;
131
+ im[b] = im[a] - ti;
132
+ re[a] += tr;
133
+ im[a] += ti;
134
+ const nr = cr * wr - ci * wi;
135
+ ci = cr * wi + ci * wr;
136
+ cr = nr;
137
+ }
138
+ }
139
+ }
140
+ }
141
+
142
+ /* the same index math as the orb: [round(f / (sr/2) * bins)] inclusive */
143
+ const range = (lo, hi) => [Math.round((lo / (SR / 2)) * BINS), Math.round((hi / (SR / 2)) * BINS)];
144
+ const BANDS = { low: range(0, 200), mid: range(200, 2000), high: range(2000, 20000), all: [0, BINS - 1] };
145
+
146
+ const frames = Math.ceil(total / STEP);
147
+ const result = { fps: FPS, duration: +(total / SR).toFixed(3), low: [], mid: [], high: [], all: [] };
148
+ const smoothed = new Float32Array(BINS);
149
+ const re = new Float32Array(FFT);
150
+ const im = new Float32Array(FFT);
151
+ const bytes = new Float32Array(BINS);
152
+
153
+ for (let f = 0; f < frames; f++) {
154
+ const end = Math.min((f + 1) * STEP, total);
155
+ for (let i = 0; i < FFT; i++) {
156
+ const s = end - FFT + i;
157
+ re[i] = (s >= 0 ? signal[s] : 0) * window_[i];
158
+ im[i] = 0;
159
+ }
160
+ fft(re, im);
161
+ for (let k = 0; k < BINS; k++) {
162
+ const mag = Math.hypot(re[k], im[k]) / FFT;
163
+ smoothed[k] = SMOOTH * smoothed[k] + (1 - SMOOTH) * mag;
164
+ const db = 20 * Math.log10(Math.max(smoothed[k], 1e-12));
165
+ bytes[k] = Math.min(255, Math.max(0, Math.floor((255 / (MAX_DB - MIN_DB)) * (db - MIN_DB))));
166
+ }
167
+ for (const [name, [lo, hi]] of Object.entries(BANDS)) {
168
+ let sum = 0;
169
+ for (let k = lo; k <= hi; k++) sum += bytes[k];
170
+ result[name].push(+(sum / (hi - lo + 1) / 255).toFixed(3));
171
+ }
172
+ }
173
+
174
+ /* --- output ------------------------------------------------------------------ */
175
+ const json = JSON.stringify(result);
176
+ if (!out) {
177
+ process.stdout.write(`${json}\n`);
178
+ } else {
179
+ mkdirSync(dirname(resolve(out)), { recursive: true });
180
+ writeFileSync(out, out.endsWith(".js") ? `window.VOICE_ENVELOPE = ${json};\n` : `${json}\n`);
181
+ const peak = k => Math.max(...result[k]).toFixed(2);
182
+ console.error(
183
+ `envelope: ${frames} frames (${result.duration}s) → ${out} pico low ${peak("low")} mid ${peak("mid")} high ${peak("high")} all ${peak("all")}`,
184
+ );
185
+ }
package/tools/voice.py ADDED
@@ -0,0 +1,448 @@
1
+ """omnivideo voice providers: Fish Audio, Cartesia, VoiceStudio and ElevenLabs.
2
+
3
+ The order comes from `voice.order` in the config: the first is the default, the
4
+ others are fallbacks, in that order. A video picks its own in `voice.provider`
5
+ of scenes.json (the studio screen writes there); the rest of the order stays
6
+ as fallback.
7
+
8
+ Voice profile: `voice.profiles` in the config keeps, by name, the Fish voice_id,
9
+ the Cartesia one and the ElevenLabs one, the VoiceStudio profile name and the
10
+ `order` to try them in (the first is preferred, the others are fallbacks). The
11
+ video picks its own in `voice.voice_profile` of scenes.json; without a choice,
12
+ `voice.default_profile` applies; with no profile at all, each provider's
13
+ `voice_id` and the global `voice.order`.
14
+
15
+ Each provider can tell whether it is ready (key, voice, service up), generate a
16
+ line as WAV and compute the line hash with its settings. The hash is what tells
17
+ whether the line must be generated again.
18
+
19
+ Keys: environment variable or `.env` at the omnivideo root (outside git), named
20
+ in `voice.<provider>.key_env`.
21
+ """
22
+
23
+ import hashlib
24
+ import io
25
+ import json
26
+ import os
27
+ import re
28
+ import threading
29
+ import wave
30
+
31
+ from omni_paths import DATA_ROOT as ROOT
32
+
33
+ CFG = json.loads((ROOT / "omnivideo.config.json").read_text())
34
+ NAMES = {"fish": "Fish Audio", "cartesia": "Cartesia", "voicestudio": "VoiceStudio", "elevenlabs": "ElevenLabs"}
35
+
36
+
37
+ class Failed(Exception):
38
+ """The provider did not generate this line. `disable` = no point trying the
39
+ next lines with it in this run (no key, no credit, service down)."""
40
+
41
+ def __init__(self, msg, disable=True, rate_limited=False):
42
+ super().__init__(msg)
43
+ self.disable = disable
44
+ # 429: the provider is up, just too many lines at once (the voice runs in
45
+ # parallel): wait and try the same provider again, don't drop it
46
+ self.rate_limited = rate_limited
47
+
48
+
49
+ def _env():
50
+ vals = {}
51
+ path = ROOT / ".env"
52
+ if path.exists():
53
+ for row in path.read_text().splitlines():
54
+ row = row.strip()
55
+ if row and not row.startswith("#") and "=" in row:
56
+ k, v = row.split("=", 1)
57
+ vals[k.strip()] = v.strip().strip('"').strip("'")
58
+ vals.update({k: v for k, v in os.environ.items() if v})
59
+ return vals
60
+
61
+
62
+ def spoken_text(line):
63
+ """The text sent to the voice provider. `text` is also the caption and does not
64
+ change; here the pronunciation map of `voice.pronunciation` in the config
65
+ applies (term → how it is said), for every provider and every video. A term
66
+ that does not occur leaves the text as is: old lines keep the same hash."""
67
+ text = line["text"]
68
+ for term, said_as in (CFG["voice"].get("pronunciation") or {}).items():
69
+ if term and not term.startswith("_"):
70
+ text = re.sub(rf"(?<![\w.]){re.escape(term)}(?!\w|\.\d)", said_as, text, flags=re.IGNORECASE)
71
+ return text
72
+
73
+
74
+ def _digest(line, extra):
75
+ key = json.dumps([spoken_text(line), line.get("instruct", ""), extra], ensure_ascii=False, sort_keys=True)
76
+ return hashlib.sha1(key.encode()).hexdigest()[:12]
77
+
78
+
79
+ def _speed(data, cfg):
80
+ return float({**cfg, **(data.get("voice") or {})}.get("speed", 1.0))
81
+
82
+
83
+ def voice_profile(data):
84
+ """The video's voice profile (`voice.voice_profile` in scenes.json, the id of a
85
+ `voice.profiles` item) or, without a choice, the config's `voice.default_profile`.
86
+ Returns `(profile, reason)`: with no profile registered, each provider's
87
+ `voice_id` applies (`(None, None)`); a chosen profile that no longer exists
88
+ is a reason, not a silent voice swap."""
89
+ cfg = CFG["voice"]
90
+ profiles = cfg.get("profiles") or []
91
+ requested = (data.get("voice") or {}).get("voice_profile")
92
+ if requested:
93
+ found = next((p for p in profiles if p.get("id") == requested), None)
94
+ if found:
95
+ return found, None
96
+ return None, f"o perfil de voz “{requested}” não existe mais (voice.profiles na config)"
97
+ default = cfg.get("default_profile")
98
+ found = next((p for p in profiles if p.get("id") == default), None) if default else None
99
+ return found, None
100
+
101
+
102
+ def _voice_id(cfg, data, provider):
103
+ """The provider's voice_id for this video: the profile's, or the config's without
104
+ a profile. A profile without this provider's id does not fall back to the
105
+ config's (it would be another voice): the provider is left out and the line
106
+ moves on to the next."""
107
+ profile, reason = voice_profile(data)
108
+ if reason:
109
+ return None, reason
110
+ if profile is None:
111
+ return cfg.get("voice_id"), None
112
+ voice = profile.get(provider)
113
+ if not voice:
114
+ return None, f"o perfil “{profile.get('name', profile.get('id'))}” não tem voz na {NAMES[provider]}"
115
+ return voice, None
116
+
117
+
118
+ def _http_error(name, resp):
119
+ body = resp.text[:300].replace("\n", " ")
120
+ if resp.status_code == 429:
121
+ return Failed(f"{name} respondeu 429 (muitas falas ao mesmo tempo): {body}", disable=False, rate_limited=True)
122
+ # 401/402/403: key or credit; 5xx: service. Either way, the rest of the
123
+ # lines in this run go to the next provider: consistent voice
124
+ return Failed(f"{name} respondeu {resp.status_code}: {body}", disable=True)
125
+
126
+
127
+ def is_free(model):
128
+ """`s2.1-pro-free` is the same model as `s2.1-pro`, without charging the API balance."""
129
+ return str(model or "").endswith("-free")
130
+
131
+
132
+ class Fish:
133
+ """`voice.fish.model` is tried first (the free one, by default) and
134
+ `voice.fish.fallback_model` next (the paid one, charged to the API balance).
135
+ A model that fails on key, quota or service is dropped for the rest of the run."""
136
+
137
+ id = "fish"
138
+
139
+ def __init__(self, requests, data):
140
+ self.r = requests
141
+ self.cfg = CFG["voice"]["fish"]
142
+ self.speed = _speed(data, self.cfg)
143
+ self.key = _env().get(self.cfg["key_env"])
144
+ self.voice_id, self._no_voice = _voice_id(self.cfg, data, "fish")
145
+ # the model that generated the line, per thread (the lines run in parallel):
146
+ # the usage log only charges the paid one
147
+ self._local = threading.local()
148
+ self.disabled = set()
149
+
150
+ @property
151
+ def model(self):
152
+ return getattr(self._local, "model", None)
153
+
154
+ def ready(self):
155
+ if not self.key:
156
+ return f"sem chave ({self.cfg['key_env']} no ambiente ou no .env)"
157
+ if self._no_voice:
158
+ return self._no_voice
159
+ if not self.voice_id:
160
+ return "sem voice_id (o reference_id do modelo de voz clonado, em voice.fish.voice_id)"
161
+ return None
162
+
163
+ def config_hash(self):
164
+ # free and paid give the same voice: swapping one for the other does not redo the lines
165
+ model = self.cfg["model"].removesuffix("-free")
166
+ # hash-compat: do not translate
167
+ return {"provider": "fish", "voz": self.voice_id, "modelo": model, "speed": self.speed}
168
+
169
+ def generate(self, line):
170
+ models = [
171
+ m
172
+ for m in dict.fromkeys((self.cfg["model"], self.cfg.get("fallback_model")))
173
+ if m and m not in self.disabled
174
+ ]
175
+ if not models:
176
+ raise Failed("Fish Audio: nenhum modelo disponível nesta execução")
177
+ for i, model in enumerate(models):
178
+ try:
179
+ audio = self._tts(line, model)
180
+ self._local.model = model
181
+ return audio
182
+ except Failed as exc:
183
+ # too many at once is not a reason to go to the paid model: the caller waits and retries
184
+ if exc.rate_limited:
185
+ raise
186
+ if exc.disable:
187
+ self.disabled.add(model)
188
+ if i == len(models) - 1:
189
+ raise
190
+ print(f"⚠ {line['id']}: {exc} → tentando o {models[i + 1]}")
191
+
192
+ def _tts(self, line, model):
193
+ body = {
194
+ "text": spoken_text(line),
195
+ "reference_id": self.voice_id,
196
+ "format": "wav",
197
+ "sample_rate": 44100,
198
+ "latency": "normal",
199
+ "prosody": {"speed": self.speed, "volume": 0},
200
+ }
201
+ try:
202
+ resp = self.r.post(
203
+ "https://api.fish.audio/v1/tts",
204
+ json=body,
205
+ timeout=self.cfg["timeout_s"],
206
+ headers={"Authorization": f"Bearer {self.key}", "model": model},
207
+ )
208
+ except self.r.RequestException as exc:
209
+ raise Failed(f"Fish Audio ({model}): {exc}")
210
+ if resp.status_code != 200:
211
+ raise _http_error(f"Fish Audio ({model})", resp)
212
+ return resp.content
213
+
214
+
215
+ class Cartesia:
216
+ id = "cartesia"
217
+
218
+ def __init__(self, requests, data):
219
+ self.r = requests
220
+ self.cfg = CFG["voice"]["cartesia"]
221
+ # Cartesia accepts 0.6 to 1.5
222
+ self.speed = min(1.5, max(0.6, _speed(data, self.cfg)))
223
+ self.key = _env().get(self.cfg["key_env"])
224
+ self.voice_id, self._no_voice = _voice_id(self.cfg, data, "cartesia")
225
+
226
+ def ready(self):
227
+ if not self.key:
228
+ return f"sem chave ({self.cfg['key_env']} no ambiente ou no .env)"
229
+ if self._no_voice:
230
+ return self._no_voice
231
+ if not self.voice_id:
232
+ return "sem voice_id (o id da voz clonada, em voice.cartesia.voice_id)"
233
+ return None
234
+
235
+ def config_hash(self):
236
+ # hash-compat: do not translate
237
+ return {
238
+ "provider": "cartesia",
239
+ "voz": self.voice_id,
240
+ "modelo": self.cfg["model"],
241
+ "idioma": self.cfg["language"],
242
+ "speed": self.speed,
243
+ }
244
+
245
+ def generate(self, line):
246
+ body = {
247
+ "model_id": self.cfg["model"],
248
+ "transcript": spoken_text(line),
249
+ "voice": {"mode": "id", "id": self.voice_id},
250
+ "language": self.cfg["language"],
251
+ "generation_config": {"speed": self.speed},
252
+ "output_format": {"container": "wav", "encoding": "pcm_s16le", "sample_rate": 44100},
253
+ }
254
+ try:
255
+ resp = self.r.post(
256
+ "https://api.cartesia.ai/tts/bytes",
257
+ json=body,
258
+ timeout=self.cfg["timeout_s"],
259
+ headers={"Authorization": f"Bearer {self.key}", "Cartesia-Version": self.cfg["api_version"]},
260
+ )
261
+ except self.r.RequestException as exc:
262
+ raise Failed(f"Cartesia: {exc}")
263
+ if resp.status_code != 200:
264
+ raise _http_error("Cartesia", resp)
265
+ return resp.content
266
+
267
+
268
+ class ElevenLabs:
269
+ """ElevenLabs TTS. The response is 16-bit PCM, mono, 44.1 kHz (no header): it
270
+ becomes WAV here, the same format as the other providers. No per-provider
271
+ line in `usage.py`: consumption stays in the ElevenLabs account."""
272
+
273
+ id = "elevenlabs"
274
+
275
+ def __init__(self, requests, data):
276
+ self.r = requests
277
+ self.cfg = CFG["voice"].get("elevenlabs") or {}
278
+ # ElevenLabs accepts 0.7 to 1.2
279
+ self.speed = min(1.2, max(0.7, _speed(data, self.cfg)))
280
+ self.key = _env().get(self.cfg.get("key_env", "ELEVENLABS_API_KEY"))
281
+ self.voice_id, self._no_voice = _voice_id(self.cfg, data, "elevenlabs")
282
+
283
+ def ready(self):
284
+ if not self.key:
285
+ return f"sem chave ({self.cfg.get('key_env', 'ELEVENLABS_API_KEY')} no ambiente ou no .env)"
286
+ if self._no_voice:
287
+ return self._no_voice
288
+ if not self.voice_id:
289
+ return "sem voice_id (o id da voz na ElevenLabs, em voice.elevenlabs.voice_id)"
290
+ return None
291
+
292
+ def config_hash(self):
293
+ # hash-compat: do not translate
294
+ return {
295
+ "provider": "elevenlabs",
296
+ "voz": self.voice_id,
297
+ "modelo": self.cfg.get("model"),
298
+ "idioma": self.cfg.get("language"),
299
+ "speed": self.speed,
300
+ "estabilidade": self.cfg.get("stability"),
301
+ "similaridade": self.cfg.get("similarity"),
302
+ }
303
+
304
+ def generate(self, line):
305
+ body = {
306
+ "text": spoken_text(line),
307
+ "model_id": self.cfg.get("model", "eleven_multilingual_v2"),
308
+ "voice_settings": {
309
+ "speed": self.speed,
310
+ "stability": self.cfg.get("stability", 0.5),
311
+ "similarity_boost": self.cfg.get("similarity", 0.75),
312
+ },
313
+ }
314
+ if self.cfg.get("language"):
315
+ body["language_code"] = self.cfg["language"]
316
+ try:
317
+ resp = self.r.post(
318
+ f"https://api.elevenlabs.io/v1/text-to-speech/{self.voice_id}",
319
+ params={"output_format": "pcm_44100"},
320
+ json=body,
321
+ timeout=self.cfg.get("timeout_s", 120),
322
+ headers={"xi-api-key": self.key},
323
+ )
324
+ except self.r.RequestException as exc:
325
+ raise Failed(f"ElevenLabs: {exc}")
326
+ if resp.status_code != 200:
327
+ raise _http_error("ElevenLabs", resp)
328
+ buf = io.BytesIO()
329
+ with wave.open(buf, "wb") as w:
330
+ w.setnchannels(1)
331
+ w.setsampwidth(2)
332
+ w.setframerate(44100)
333
+ w.writeframes(resp.content)
334
+ return buf.getvalue()
335
+
336
+
337
+ class VoiceStudio:
338
+ """Local, profile picked by name: the user redoes the clone and the id changes.
339
+
340
+ A nonexistent profile_id is not an error in VoiceStudio: it generates with a
341
+ generic voice, without the clone. That is why the id never goes fixed in the config.
342
+ """
343
+
344
+ id = "voicestudio"
345
+
346
+ def __init__(self, requests, data):
347
+ self.r = requests
348
+ self.cfg = CFG["voicestudio"]
349
+ self.vs = {**self.cfg, **(data.get("voice") or {})}
350
+ # the video's voice profile may point to another VoiceStudio profile
351
+ profile, _ = voice_profile(data)
352
+ if profile and profile.get("voicestudio") and not (data.get("voice") or {}).get("profile"):
353
+ self.vs["profile"] = profile["voicestudio"]
354
+ self.url = self.cfg["url"]
355
+ self._settings = None
356
+ self._reason = None
357
+
358
+ def _resolve(self):
359
+ if self._settings or self._reason:
360
+ return
361
+ try:
362
+ self.r.get(f"{self.url}/health", timeout=5).raise_for_status()
363
+ profiles = self.r.get(f"{self.url}/profiles", timeout=10).json()
364
+ except self.r.RequestException:
365
+ self._reason = f"não responde em {self.url} (abra o app ou `bun run dev:api` no repo)"
366
+ return
367
+ name = self.vs["profile"]
368
+ hits = [p for p in profiles if p.get("name") == name]
369
+ if len(hits) != 1:
370
+ found = ", ".join(f"“{p.get('name')}”" for p in profiles) or "nenhum"
371
+ self._reason = f"preciso de exatamente um perfil chamado “{name}”; achei {len(hits)}. Perfis: {found}"
372
+ return
373
+ self._settings = {k: self.vs[k] for k in ("language", "num_step", "speed")}
374
+ self._settings["profile_id"] = hits[0]["id"]
375
+
376
+ def ready(self):
377
+ self._resolve()
378
+ return self._reason
379
+
380
+ def config_hash(self):
381
+ # same format as before the providers existed: lines already generated stay valid
382
+ self._resolve()
383
+ return self._settings
384
+
385
+ def generate(self, line):
386
+ if self.ready():
387
+ raise Failed(f"VoiceStudio {self._reason}")
388
+ form = {"text": spoken_text(line), **{k: str(v) for k, v in self._settings.items()}}
389
+ if line.get("instruct"):
390
+ form["instruct"] = line["instruct"]
391
+ try:
392
+ resp = self.r.post(f"{self.url}/generate", data=form, timeout=self.cfg["timeout_s"])
393
+ except self.r.RequestException as exc:
394
+ # a timeout does not take VoiceStudio down for the next lines
395
+ raise Failed(f"VoiceStudio: {exc}", disable=False)
396
+ if resp.status_code != 200 or not resp.headers.get("content-type", "").startswith("audio/"):
397
+ raise Failed(f"VoiceStudio respondeu {resp.status_code}: {resp.text[:300]}", disable=False)
398
+ return resp.content
399
+
400
+
401
+ CLASSES = {"fish": Fish, "cartesia": Cartesia, "voicestudio": VoiceStudio, "elevenlabs": ElevenLabs}
402
+
403
+
404
+ def video_order(data):
405
+ """This video's try order. With a voice profile, it is the profile's `order`
406
+ (only the providers it configured); without one, the config's `voice.order`.
407
+ `voice.provider` in scenes.json moves one provider to the front."""
408
+ profile, _ = voice_profile(data)
409
+ base = []
410
+ if profile:
411
+ base = [p for p in (profile.get("order") or []) if p in CLASSES and profile.get(p)]
412
+ # profile without `order` (or with an old one): the configured ones, in config order
413
+ base += [p for p in CFG["voice"]["order"] if p in CLASSES and profile.get(p) and p not in base]
414
+ if not base:
415
+ base = [p for p in CFG["voice"]["order"] if p in CLASSES]
416
+ first = (data.get("voice") or {}).get("provider")
417
+ if first in CLASSES:
418
+ return [first] + [p for p in base if p != first]
419
+ return base
420
+
421
+
422
+ def preferred(data):
423
+ return video_order(data)[0]
424
+
425
+
426
+ def chain(requests, data):
427
+ """The video's provider and then the fallbacks, in profile (or config) order."""
428
+ return [CLASSES[p](requests, data) for p in video_order(data)]
429
+
430
+
431
+ def digest(provider, line):
432
+ extra = provider.config_hash()
433
+ return _digest(line, extra) if extra is not None else None
434
+
435
+
436
+ def status(requests=None):
437
+ """For the studio and for checking: each provider ready, or why not."""
438
+ if requests is None:
439
+ import requests
440
+ return [
441
+ {"id": c.id, "name": NAMES[c.id], "reason": c(requests, {}).ready()}
442
+ for c in (CLASSES[p] for p in CFG["voice"]["order"])
443
+ ]
444
+
445
+
446
+ if __name__ == "__main__":
447
+ for p in status():
448
+ print(f"{'✓' if not p['reason'] else '·'} {p['name']}{'' if not p['reason'] else ': ' + p['reason']}")