@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.
- package/.env.example +41 -0
- package/README.md +2 -2
- package/bin/omni.mjs +18 -0
- package/docs/onboarding/clone-voice.png +0 -0
- package/docs/onboarding/getting-started.png +0 -0
- package/docs/onboarding/new-video.png +0 -0
- package/docs/onboarding/providers.png +0 -0
- package/docs/onboarding/publish.png +0 -0
- package/docs/onboarding/references.png +0 -0
- package/docs/onboarding.md +212 -0
- package/omnivideo.config.example.json +437 -0
- package/package.json +40 -4
- package/reference/captions.md +156 -0
- package/reference/carousel-contract.md +91 -0
- package/reference/cloud.md +96 -0
- package/reference/learnings.md +792 -0
- package/reference/omnichat.md +305 -0
- package/reference/premiere-mcp.md +91 -0
- package/reference/publish.md +305 -0
- package/reference/scenes-contract.md +109 -0
- package/reference/studio.md +666 -0
- package/reference/voicestudio.md +81 -0
- package/reference/writing-for-voice.md +67 -0
- package/requirements.txt +3 -0
- package/studio/api/app.ts +87 -0
- package/studio/api/lib/errors.ts +33 -0
- package/studio/api/lib/files.ts +41 -0
- package/studio/api/lib/hono.ts +9 -0
- package/studio/api/lib/result.ts +14 -0
- package/studio/api/lib/validate.ts +31 -0
- package/studio/api/lib/video.ts +10 -0
- package/studio/api/middleware/json-charset.ts +11 -0
- package/studio/api/middleware/local-only.ts +32 -0
- package/studio/api/middleware/no-timeout.ts +10 -0
- package/studio/api/routes/admin.ts +18 -0
- package/studio/api/routes/agent-cascade.ts +100 -0
- package/studio/api/routes/agent-history.ts +23 -0
- package/studio/api/routes/agent.ts +162 -0
- package/studio/api/routes/analytics.ts +31 -0
- package/studio/api/routes/approval.ts +60 -0
- package/studio/api/routes/archive.ts +12 -0
- package/studio/api/routes/captions.ts +28 -0
- package/studio/api/routes/carousel.ts +38 -0
- package/studio/api/routes/clone.ts +27 -0
- package/studio/api/routes/create.ts +19 -0
- package/studio/api/routes/events.ts +11 -0
- package/studio/api/routes/export.ts +14 -0
- package/studio/api/routes/media.ts +30 -0
- package/studio/api/routes/omnichat.ts +113 -0
- package/studio/api/routes/onboarding.ts +14 -0
- package/studio/api/routes/publish.ts +40 -0
- package/studio/api/routes/queue.ts +14 -0
- package/studio/api/routes/references.ts +40 -0
- package/studio/api/routes/runner.ts +17 -0
- package/studio/api/routes/schedule.ts +47 -0
- package/studio/api/routes/services.ts +11 -0
- package/studio/api/routes/sessions.ts +27 -0
- package/studio/api/routes/settings.ts +102 -0
- package/studio/api/routes/sound.ts +45 -0
- package/studio/api/routes/thumbnail.ts +18 -0
- package/studio/api/routes/usage.ts +13 -0
- package/studio/api/routes/videos.ts +41 -0
- package/studio/api/routes/voices.ts +71 -0
- package/studio/api/services/admin.ts +113 -0
- package/studio/api/services/agent-cascade.ts +975 -0
- package/studio/api/services/agent-claude.ts +47 -0
- package/studio/api/services/agent-history.ts +103 -0
- package/studio/api/services/agent-persona.ts +77 -0
- package/studio/api/services/agent-tools.ts +2237 -0
- package/studio/api/services/agent-ui-tools.ts +259 -0
- package/studio/api/services/agent-wait.ts +32 -0
- package/studio/api/services/agent.ts +553 -0
- package/studio/api/services/analytics.ts +92 -0
- package/studio/api/services/approval.ts +134 -0
- package/studio/api/services/archive.ts +38 -0
- package/studio/api/services/captions.ts +268 -0
- package/studio/api/services/carousel.ts +206 -0
- package/studio/api/services/classify.ts +196 -0
- package/studio/api/services/clone-phrases.ts +75 -0
- package/studio/api/services/clone.ts +722 -0
- package/studio/api/services/create.ts +421 -0
- package/studio/api/services/events.ts +215 -0
- package/studio/api/services/media.ts +32 -0
- package/studio/api/services/omnichat.ts +624 -0
- package/studio/api/services/onboarding.ts +77 -0
- package/studio/api/services/publish.ts +287 -0
- package/studio/api/services/queue.ts +150 -0
- package/studio/api/services/references.ts +269 -0
- package/studio/api/services/root.ts +60 -0
- package/studio/api/services/services.ts +114 -0
- package/studio/api/services/sessions.ts +1007 -0
- package/studio/api/services/settings.ts +983 -0
- package/studio/api/services/sound.ts +311 -0
- package/studio/api/services/thumbnail.ts +133 -0
- package/studio/api/services/usage.ts +27 -0
- package/studio/api/services/videos.ts +418 -0
- package/studio/api/services/voice-pause.ts +98 -0
- package/studio/api/services/voices.ts +324 -0
- package/studio/runner/runner.ts +244 -0
- package/studio/shared/omni-actions.ts +68 -0
- package/studio/shared/omni-admin.ts +52 -0
- package/studio/shared/omni-farewell.ts +48 -0
- package/studio/shared/omni-hotword.ts +20 -0
- package/studio/shared/omni-screens.ts +228 -0
- package/studio/shared/schemas/admin.ts +15 -0
- package/studio/shared/schemas/agent-cascade.ts +58 -0
- package/studio/shared/schemas/agent-history.ts +73 -0
- package/studio/shared/schemas/agent.ts +50 -0
- package/studio/shared/schemas/analytics.ts +6 -0
- package/studio/shared/schemas/approval.ts +22 -0
- package/studio/shared/schemas/captions.ts +19 -0
- package/studio/shared/schemas/carousel.ts +26 -0
- package/studio/shared/schemas/clone.ts +40 -0
- package/studio/shared/schemas/common.ts +16 -0
- package/studio/shared/schemas/omnichat.ts +54 -0
- package/studio/shared/schemas/onboarding.ts +5 -0
- package/studio/shared/schemas/publish.ts +45 -0
- package/studio/shared/schemas/references.ts +20 -0
- package/studio/shared/schemas/settings.ts +82 -0
- package/studio/shared/schemas/sound.ts +22 -0
- package/studio/shared/schemas/videos.ts +38 -0
- package/studio/shared/schemas/voices.ts +23 -0
- package/studio/shared/types/api.ts +1290 -0
- package/studio/studio.ts +148 -0
- package/studio/tsconfig.json +15 -0
- package/studio/web/assets/logos/cartesia.svg +4 -0
- package/studio/web/assets/logos/elevenlabs.svg +12 -0
- package/studio/web/assets/logos/fish.svg +24 -0
- package/studio/web/assets/logos/omni-black.png +0 -0
- package/studio/web/assets/logos/omni-white.png +0 -0
- package/studio/web/assets/logos/voicestudio.svg +21 -0
- package/studio/web/components/AgentMark.tsx +7 -0
- package/studio/web/components/AgentWalking.tsx +11 -0
- package/studio/web/components/ApiKeyField.tsx +104 -0
- package/studio/web/components/AppShell.tsx +64 -0
- package/studio/web/components/ApprovalNotice.tsx +65 -0
- package/studio/web/components/AvatarGroupIcon.tsx +22 -0
- package/studio/web/components/AvatarNameField.tsx +86 -0
- package/studio/web/components/AvatarPicker.tsx +84 -0
- package/studio/web/components/BrandLogo.tsx +16 -0
- package/studio/web/components/CartesiaPlanSetting.tsx +90 -0
- package/studio/web/components/ClaudeWalking.tsx +32 -0
- package/studio/web/components/CloneVoiceDialog/GuidedScript.tsx +39 -0
- package/studio/web/components/CloneVoiceDialog/LanguageBar.tsx +37 -0
- package/studio/web/components/CloneVoiceDialog/LevelMeter.tsx +31 -0
- package/studio/web/components/CloneVoiceDialog/ProviderChoice.tsx +81 -0
- package/studio/web/components/CloneVoiceDialog/QualityPanel.tsx +62 -0
- package/studio/web/components/CloneVoiceDialog/RecordStep.tsx +50 -0
- package/studio/web/components/CloneVoiceDialog/RecorderBar.tsx +79 -0
- package/studio/web/components/CloneVoiceDialog/RedoSummary.tsx +54 -0
- package/studio/web/components/CloneVoiceDialog/SaveStep.tsx +91 -0
- package/studio/web/components/CloneVoiceDialog/SourceBar.tsx +30 -0
- package/studio/web/components/CloneVoiceDialog/index.tsx +141 -0
- package/studio/web/components/CloneVoiceDialog/types.ts +28 -0
- package/studio/web/components/CloneVoiceDialog/useCloneVoice.ts +528 -0
- package/studio/web/components/ConnectionSignals.tsx +51 -0
- package/studio/web/components/EditVoiceDialog/EditVoiceFooter.tsx +72 -0
- package/studio/web/components/EditVoiceDialog/ProviderCards.tsx +81 -0
- package/studio/web/components/EditVoiceDialog/index.tsx +256 -0
- package/studio/web/components/ErrorBanner.tsx +115 -0
- package/studio/web/components/FinishedTask.tsx +41 -0
- package/studio/web/components/FishModelSetting.tsx +69 -0
- package/studio/web/components/FishPriceSetting.tsx +44 -0
- package/studio/web/components/HumanBanner.tsx +64 -0
- package/studio/web/components/Lightbox.tsx +41 -0
- package/studio/web/components/LogStream.tsx +42 -0
- package/studio/web/components/Markdown.tsx +21 -0
- package/studio/web/components/MissingDmsPanel/BatchSend.tsx +137 -0
- package/studio/web/components/MissingDmsPanel/CommentCard.tsx +168 -0
- package/studio/web/components/MissingDmsPanel/CommentFeed.tsx +45 -0
- package/studio/web/components/MissingDmsPanel/DmBubble.tsx +38 -0
- package/studio/web/components/MissingDmsPanel/ReplyComposer.tsx +63 -0
- package/studio/web/components/MissingDmsPanel/index.tsx +98 -0
- package/studio/web/components/NotFound.tsx +48 -0
- package/studio/web/components/Omni/OmniAvatarPicker.tsx +92 -0
- package/studio/web/components/Omni/OmniCards.tsx +202 -0
- package/studio/web/components/Omni/OmniGlow.tsx +183 -0
- package/studio/web/components/Omni/OmniHistory.tsx +119 -0
- package/studio/web/components/Omni/OmniHotword.tsx +40 -0
- package/studio/web/components/Omni/OmniMicSelect.tsx +92 -0
- package/studio/web/components/Omni/OmniVoiceSample.tsx +81 -0
- package/studio/web/components/Omni/OmniWidget.tsx +743 -0
- package/studio/web/components/Omni/PendingActions.tsx +200 -0
- package/studio/web/components/Omni/Transcript.tsx +127 -0
- package/studio/web/components/OmnichatAccountStatus.tsx +87 -0
- package/studio/web/components/OmnichatRulePanel/RuleConfig.tsx +150 -0
- package/studio/web/components/OmnichatRulePanel/RuleDryRun.tsx +15 -0
- package/studio/web/components/OmnichatRulePanel/RuleEnableConfirm.tsx +42 -0
- package/studio/web/components/OmnichatRulePanel/RuleSkeleton.tsx +18 -0
- package/studio/web/components/OmnichatRulePanel/RuleStatus.tsx +55 -0
- package/studio/web/components/OmnichatRulePanel/index.tsx +115 -0
- package/studio/web/components/OmnichatRulePanel/rule-steps.ts +67 -0
- package/studio/web/components/OmnichatRulePanel/steps/MessageStep.tsx +91 -0
- package/studio/web/components/OmnichatRulePanel/steps/RepliesStep.tsx +201 -0
- package/studio/web/components/OmnichatRulePanel/steps/TriggerStep.tsx +76 -0
- package/studio/web/components/OmnichatRulePanel/steps/types.ts +11 -0
- package/studio/web/components/Palette.tsx +17 -0
- package/studio/web/components/PlatformCard.tsx +109 -0
- package/studio/web/components/PlatformIcon.tsx +19 -0
- package/studio/web/components/ProviderCard.tsx +80 -0
- package/studio/web/components/ProviderDetail.tsx +367 -0
- package/studio/web/components/ProviderLogo.tsx +20 -0
- package/studio/web/components/QueuePanel.tsx +77 -0
- package/studio/web/components/ReferenceActions.tsx +89 -0
- package/studio/web/components/ReferencePicker.tsx +136 -0
- package/studio/web/components/ResultMessage.tsx +7 -0
- package/studio/web/components/RuleForm.tsx +138 -0
- package/studio/web/components/RuleLog.tsx +151 -0
- package/studio/web/components/RunningTask.tsx +23 -0
- package/studio/web/components/SampleList.tsx +55 -0
- package/studio/web/components/SessionPanel.tsx +352 -0
- package/studio/web/components/SettingRow.tsx +14 -0
- package/studio/web/components/SettingsCategorySkeleton.tsx +25 -0
- package/studio/web/components/SidePanel.tsx +77 -0
- package/studio/web/components/Sidebar.tsx +289 -0
- package/studio/web/components/StageStepper.tsx +87 -0
- package/studio/web/components/StudioOffline.tsx +80 -0
- package/studio/web/components/TaskName.tsx +17 -0
- package/studio/web/components/TopBanners.tsx +30 -0
- package/studio/web/components/VideoOmnichatPanel.tsx +287 -0
- package/studio/web/components/VoiceAvatar.tsx +33 -0
- package/studio/web/components/VoiceCarousel.tsx +196 -0
- package/studio/web/components/VoiceCategory.tsx +9 -0
- package/studio/web/components/VoiceModal.tsx +39 -0
- package/studio/web/components/VoiceProfileCard.tsx +75 -0
- package/studio/web/components/VoiceProfilePicker.tsx +107 -0
- package/studio/web/components/VoiceProfilesSettings.tsx +150 -0
- package/studio/web/components/VoiceProviderCard.tsx +118 -0
- package/studio/web/components/VoiceProvidersCard.tsx +26 -0
- package/studio/web/components/WaitList.tsx +21 -0
- package/studio/web/components/icons.tsx +247 -0
- package/studio/web/components/ui/accordion.tsx +47 -0
- package/studio/web/components/ui/badge.tsx +18 -0
- package/studio/web/components/ui/calendar.tsx +43 -0
- package/studio/web/components/ui/chart.tsx +229 -0
- package/studio/web/components/ui/checkbox.tsx +26 -0
- package/studio/web/components/ui/cx.ts +2 -0
- package/studio/web/components/ui/date-time-picker.tsx +84 -0
- package/studio/web/components/ui/panel.tsx +23 -0
- package/studio/web/components/ui/picker.tsx +58 -0
- package/studio/web/components/ui/popover.tsx +24 -0
- package/studio/web/components/ui/select.tsx +89 -0
- package/studio/web/components/ui/skeleton.tsx +10 -0
- package/studio/web/components/ui/slider.tsx +68 -0
- package/studio/web/components/ui/switch.tsx +13 -0
- package/studio/web/components/ui/tooltip.tsx +59 -0
- package/studio/web/hooks/useAccounts.ts +27 -0
- package/studio/web/hooks/useAdmin.ts +12 -0
- package/studio/web/hooks/useAnalytics.ts +21 -0
- package/studio/web/hooks/useAvatarAudioReactive.ts +87 -0
- package/studio/web/hooks/useBrowserConnect.ts +48 -0
- package/studio/web/hooks/useClonePlan.ts +14 -0
- package/studio/web/hooks/useConnected.ts +7 -0
- package/studio/web/hooks/useLiveQuery.ts +29 -0
- package/studio/web/hooks/useMenuCollapsed.ts +32 -0
- package/studio/web/hooks/useMicDevices.ts +53 -0
- package/studio/web/hooks/useMissingDms.ts +211 -0
- package/studio/web/hooks/useMissingSetupSteps.ts +26 -0
- package/studio/web/hooks/useNow.ts +11 -0
- package/studio/web/hooks/useOmniAgent.ts +278 -0
- package/studio/web/hooks/useOmniClaude.ts +124 -0
- package/studio/web/hooks/useOmniDock.ts +142 -0
- package/studio/web/hooks/useOmniGreeting.ts +60 -0
- package/studio/web/hooks/useOmniHotword.ts +128 -0
- package/studio/web/hooks/useOmnichat.ts +26 -0
- package/studio/web/hooks/useOnboarding.ts +27 -0
- package/studio/web/hooks/usePost.ts +23 -0
- package/studio/web/hooks/useQueue.ts +10 -0
- package/studio/web/hooks/useReferences.ts +20 -0
- package/studio/web/hooks/useRuleEditor.ts +101 -0
- package/studio/web/hooks/useRunner.ts +24 -0
- package/studio/web/hooks/useSaveAction.ts +23 -0
- package/studio/web/hooks/useSchedule.ts +11 -0
- package/studio/web/hooks/useServices.ts +10 -0
- package/studio/web/hooks/useSessions.ts +15 -0
- package/studio/web/hooks/useSettings.ts +11 -0
- package/studio/web/hooks/useSettingsSection.ts +30 -0
- package/studio/web/hooks/useSidePanel.ts +23 -0
- package/studio/web/hooks/useSound.ts +30 -0
- package/studio/web/hooks/useUsage.ts +11 -0
- package/studio/web/hooks/useVideoVoice.ts +36 -0
- package/studio/web/hooks/useVideos.ts +21 -0
- package/studio/web/hooks/useVoiceProfilePreview.ts +52 -0
- package/studio/web/hooks/useVoiceProfiles.ts +7 -0
- package/studio/web/hooks/useVoiceRunning.ts +10 -0
- package/studio/web/hooks/useVoiceStudioClosed.ts +8 -0
- package/studio/web/index.html +19 -0
- package/studio/web/lib/agent.ts +7 -0
- package/studio/web/lib/alerts.ts +210 -0
- package/studio/web/lib/analytics-metrics.ts +351 -0
- package/studio/web/lib/api.ts +70 -0
- package/studio/web/lib/audio.ts +39 -0
- package/studio/web/lib/avatars.ts +41 -0
- package/studio/web/lib/calendar.ts +218 -0
- package/studio/web/lib/caption-position.ts +249 -0
- package/studio/web/lib/carousel.ts +24 -0
- package/studio/web/lib/chime.ts +42 -0
- package/studio/web/lib/clone-voice.ts +55 -0
- package/studio/web/lib/export.ts +11 -0
- package/studio/web/lib/files.ts +8 -0
- package/studio/web/lib/format.ts +93 -0
- package/studio/web/lib/media.ts +29 -0
- package/studio/web/lib/mic-device.ts +72 -0
- package/studio/web/lib/missing-dms.ts +70 -0
- package/studio/web/lib/omni-cards.ts +42 -0
- package/studio/web/lib/omni-cascade.ts +1067 -0
- package/studio/web/lib/omni-greeting-audio.ts +24 -0
- package/studio/web/lib/omni-hotword.ts +231 -0
- package/studio/web/lib/omni-panel.ts +67 -0
- package/studio/web/lib/omni-realtime.ts +811 -0
- package/studio/web/lib/omni-ui.ts +402 -0
- package/studio/web/lib/omni-wait.ts +174 -0
- package/studio/web/lib/omnichat-api.ts +44 -0
- package/studio/web/lib/orb.ts +270 -0
- package/studio/web/lib/pcm-recorder.ts +115 -0
- package/studio/web/lib/platforms.ts +17 -0
- package/studio/web/lib/providers.ts +27 -0
- package/studio/web/lib/publish-draft.ts +52 -0
- package/studio/web/lib/publish-settings.ts +81 -0
- package/studio/web/lib/query-client.ts +122 -0
- package/studio/web/lib/references.ts +13 -0
- package/studio/web/lib/services.ts +40 -0
- package/studio/web/lib/sessions.ts +103 -0
- package/studio/web/lib/settings-categories.ts +58 -0
- package/studio/web/lib/sound-audio.ts +33 -0
- package/studio/web/lib/sound-timeline.ts +291 -0
- package/studio/web/lib/sse.ts +86 -0
- package/studio/web/lib/stage-history.ts +141 -0
- package/studio/web/lib/stages.ts +344 -0
- package/studio/web/lib/studio-offline.ts +60 -0
- package/studio/web/lib/upload.ts +24 -0
- package/studio/web/lib/video-tabs.ts +118 -0
- package/studio/web/lib/voice-profile-draft.ts +34 -0
- package/studio/web/lib/voice-profiles.ts +3 -0
- package/studio/web/main.tsx +14 -0
- package/studio/web/pages/AdminPage/index.tsx +123 -0
- package/studio/web/pages/AgentPage/components/Measurements.tsx +102 -0
- package/studio/web/pages/AgentPage/index.tsx +28 -0
- package/studio/web/pages/AnalyticsPage/components/About.tsx +52 -0
- package/studio/web/pages/AnalyticsPage/components/ChannelCards.tsx +106 -0
- package/studio/web/pages/AnalyticsPage/components/ChannelView.tsx +137 -0
- package/studio/web/pages/AnalyticsPage/components/CollectionStatus.tsx +46 -0
- package/studio/web/pages/AnalyticsPage/components/Comparison.tsx +68 -0
- package/studio/web/pages/AnalyticsPage/components/Diagnosis.tsx +50 -0
- package/studio/web/pages/AnalyticsPage/components/Highlights.tsx +49 -0
- package/studio/web/pages/AnalyticsPage/components/Hint.tsx +11 -0
- package/studio/web/pages/AnalyticsPage/components/HookVsBody.tsx +100 -0
- package/studio/web/pages/AnalyticsPage/components/HourlyWave.tsx +133 -0
- package/studio/web/pages/AnalyticsPage/components/Insights.tsx +70 -0
- package/studio/web/pages/AnalyticsPage/components/MetricCard.tsx +46 -0
- package/studio/web/pages/AnalyticsPage/components/PlanSection.tsx +40 -0
- package/studio/web/pages/AnalyticsPage/components/ProgressionChart.tsx +155 -0
- package/studio/web/pages/AnalyticsPage/components/ReelBarsChart.tsx +154 -0
- package/studio/web/pages/AnalyticsPage/components/ReelThumb.tsx +19 -0
- package/studio/web/pages/AnalyticsPage/components/ReelView.tsx +290 -0
- package/studio/web/pages/AnalyticsPage/components/ReelsTable.tsx +269 -0
- package/studio/web/pages/AnalyticsPage/components/RequestWithFocus.tsx +38 -0
- package/studio/web/pages/AnalyticsPage/components/SegmentedControl.tsx +35 -0
- package/studio/web/pages/AnalyticsPage/components/VsMedian.tsx +31 -0
- package/studio/web/pages/AnalyticsPage/index.tsx +17 -0
- package/studio/web/pages/GettingStartedPage/index.tsx +174 -0
- package/studio/web/pages/NewVideoPage/components/DeliverablesPicker.tsx +62 -0
- package/studio/web/pages/NewVideoPage/components/FormatPicker.tsx +33 -0
- package/studio/web/pages/NewVideoPage/index.tsx +133 -0
- package/studio/web/pages/NotFoundPage/index.tsx +9 -0
- package/studio/web/pages/OmnichatPage/components/NewFromLink.tsx +59 -0
- package/studio/web/pages/OmnichatPage/components/RuleListItem.tsx +64 -0
- package/studio/web/pages/OmnichatPage/components/RulePreview.tsx +14 -0
- package/studio/web/pages/OmnichatPage/components/RuleView.tsx +88 -0
- package/studio/web/pages/OmnichatPage/index.tsx +153 -0
- package/studio/web/pages/QueuePage/index.tsx +22 -0
- package/studio/web/pages/ReferencePage/components/ReferenceTexts.tsx +131 -0
- package/studio/web/pages/ReferencePage/index.tsx +216 -0
- package/studio/web/pages/ReferencesPage/components/NewReferenceForm.tsx +150 -0
- package/studio/web/pages/ReferencesPage/components/ReferenceCard.tsx +42 -0
- package/studio/web/pages/ReferencesPage/components/ReferenceGridSkeleton.tsx +31 -0
- package/studio/web/pages/ReferencesPage/index.tsx +67 -0
- package/studio/web/pages/SchedulePage/components/CalendarSkeleton.tsx +18 -0
- package/studio/web/pages/SchedulePage/components/DayView.tsx +177 -0
- package/studio/web/pages/SchedulePage/components/HourPicker.tsx +103 -0
- package/studio/web/pages/SchedulePage/components/MonthDayCell.tsx +111 -0
- package/studio/web/pages/SchedulePage/components/MonthView.tsx +47 -0
- package/studio/web/pages/SchedulePage/components/PlatformIcons.tsx +12 -0
- package/studio/web/pages/SchedulePage/components/PostThumb.tsx +15 -0
- package/studio/web/pages/SchedulePage/components/WeekColumn.tsx +75 -0
- package/studio/web/pages/SchedulePage/components/WeekView.tsx +35 -0
- package/studio/web/pages/SchedulePage/index.tsx +285 -0
- package/studio/web/pages/SettingsPage/components/AgentAccounts.tsx +263 -0
- package/studio/web/pages/SettingsPage/components/AgentCategory.tsx +20 -0
- package/studio/web/pages/SettingsPage/components/ModelPicker.tsx +49 -0
- package/studio/web/pages/SettingsPage/components/ModelsSettings.tsx +138 -0
- package/studio/web/pages/SettingsPage/components/PlatformsCategory.tsx +23 -0
- package/studio/web/pages/SettingsPage/components/PublishingCategory/AccountStatus.tsx +29 -0
- package/studio/web/pages/SettingsPage/components/PublishingCategory/AutoPublishPanel.tsx +90 -0
- package/studio/web/pages/SettingsPage/components/PublishingCategory/BrowserConnect.tsx +62 -0
- package/studio/web/pages/SettingsPage/components/PublishingCategory/CheckField.tsx +19 -0
- package/studio/web/pages/SettingsPage/components/PublishingCategory/InstagramPanel.tsx +113 -0
- package/studio/web/pages/SettingsPage/components/PublishingCategory/OmnichatSettingsPanel.tsx +146 -0
- package/studio/web/pages/SettingsPage/components/PublishingCategory/SaveBar.tsx +33 -0
- package/studio/web/pages/SettingsPage/components/PublishingCategory/SchedulePanel.tsx +132 -0
- package/studio/web/pages/SettingsPage/components/PublishingCategory/SettingsCard.tsx +47 -0
- package/studio/web/pages/SettingsPage/components/PublishingCategory/SettingsCardGroup.tsx +19 -0
- package/studio/web/pages/SettingsPage/components/PublishingCategory/TextPanel.tsx +55 -0
- package/studio/web/pages/SettingsPage/components/PublishingCategory/TiktokPanel.tsx +123 -0
- package/studio/web/pages/SettingsPage/components/PublishingCategory/YoutubePanel.tsx +100 -0
- package/studio/web/pages/SettingsPage/components/PublishingCategory/badges.tsx +15 -0
- package/studio/web/pages/SettingsPage/components/PublishingCategory/index.tsx +40 -0
- package/studio/web/pages/SettingsPage/components/PublishingCategory/types.ts +10 -0
- package/studio/web/pages/SettingsPage/components/UsageCategory.tsx +389 -0
- package/studio/web/pages/SettingsPage/components/VoiceProvidersCategory.tsx +97 -0
- package/studio/web/pages/SettingsPage/index.tsx +85 -0
- package/studio/web/pages/VideoPage/components/AudioPlayer.tsx +113 -0
- package/studio/web/pages/VideoPage/components/CaptionPositionEditor/AxisField.tsx +50 -0
- package/studio/web/pages/VideoPage/components/CaptionPositionEditor/CaptionList.tsx +56 -0
- package/studio/web/pages/VideoPage/components/CaptionPositionEditor/CaptionListItem.tsx +92 -0
- package/studio/web/pages/VideoPage/components/CaptionPositionEditor/CaptionMargins.tsx +57 -0
- package/studio/web/pages/VideoPage/components/CaptionPositionEditor/CaptionStyleFields.tsx +70 -0
- package/studio/web/pages/VideoPage/components/CaptionPositionEditor/CaptionTextForm.tsx +87 -0
- package/studio/web/pages/VideoPage/components/CaptionPositionEditor/ColorField.tsx +37 -0
- package/studio/web/pages/VideoPage/components/CaptionPositionEditor/MarginField.tsx +59 -0
- package/studio/web/pages/VideoPage/components/CaptionPositionEditor/NumberField.tsx +57 -0
- package/studio/web/pages/VideoPage/components/CaptionPositionEditor/PositionControls.tsx +140 -0
- package/studio/web/pages/VideoPage/components/CaptionPositionEditor/captionEditor.ts +215 -0
- package/studio/web/pages/VideoPage/components/CaptionPositionEditor/useCaptionPosition.ts +154 -0
- package/studio/web/pages/VideoPage/components/CarouselChangeRequest.tsx +72 -0
- package/studio/web/pages/VideoPage/components/CarouselCompositionStage.tsx +80 -0
- package/studio/web/pages/VideoPage/components/CarouselCreate.tsx +63 -0
- package/studio/web/pages/VideoPage/components/CarouselMissing.tsx +19 -0
- package/studio/web/pages/VideoPage/components/CarouselPublishStage.tsx +173 -0
- package/studio/web/pages/VideoPage/components/CarouselScriptStage.tsx +82 -0
- package/studio/web/pages/VideoPage/components/CarouselSlideCard.tsx +49 -0
- package/studio/web/pages/VideoPage/components/CarouselViewer.tsx +55 -0
- package/studio/web/pages/VideoPage/components/ChangedVoices.tsx +53 -0
- package/studio/web/pages/VideoPage/components/CompositionStage.tsx +78 -0
- package/studio/web/pages/VideoPage/components/CoverPicker.tsx +32 -0
- package/studio/web/pages/VideoPage/components/EditorStage.tsx +53 -0
- package/studio/web/pages/VideoPage/components/LineTakes.tsx +160 -0
- package/studio/web/pages/VideoPage/components/LineTextEditor.tsx +124 -0
- package/studio/web/pages/VideoPage/components/LineVoicePicker.tsx +37 -0
- package/studio/web/pages/VideoPage/components/MediaImage.tsx +25 -0
- package/studio/web/pages/VideoPage/components/MotionScriptStage.tsx +39 -0
- package/studio/web/pages/VideoPage/components/NarrationStage.tsx +70 -0
- package/studio/web/pages/VideoPage/components/PublishForm.tsx +387 -0
- package/studio/web/pages/VideoPage/components/PublishStage.tsx +76 -0
- package/studio/web/pages/VideoPage/components/PublishText.tsx +149 -0
- package/studio/web/pages/VideoPage/components/ScenesApprovalPanel.tsx +278 -0
- package/studio/web/pages/VideoPage/components/ScheduledInfo.tsx +72 -0
- package/studio/web/pages/VideoPage/components/ScriptApprovalPanel.tsx +189 -0
- package/studio/web/pages/VideoPage/components/ScriptStage.tsx +17 -0
- package/studio/web/pages/VideoPage/components/SequencePlayer.tsx +146 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/AddSound.tsx +117 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/AreaSelection.tsx +74 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/BedPanel.tsx +94 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/BedRow.tsx +45 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/CaptionOverlay.tsx +44 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/CaptionSaveBar.tsx +32 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/CaptionsPanel.tsx +22 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/ClipBlock.tsx +111 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/ClipPanel.tsx +76 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/CutsPanel.tsx +44 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/EditorTabBar.tsx +73 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/EffectChoice.tsx +46 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/EffectItem.tsx +89 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/EffectPanel.tsx +232 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/EffectsRow.tsx +21 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/Lane.tsx +39 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/MarkerRow.tsx +40 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/MuteButton.tsx +36 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/NumberInput.tsx +37 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/PlanWarnings.tsx +19 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/PlayerResizeHandle.tsx +50 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/PreviewControls.tsx +51 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/SceneRow.tsx +54 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/SettingsPanel.tsx +65 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/SoundLibrary.tsx +99 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/SoundPreview.tsx +82 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/SpeedPanel.tsx +128 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/TimelineDock.tsx +56 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/TimelineFooter.tsx +26 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/TimelineSkeleton.tsx +21 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/TimelineToolbar.tsx +160 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/TimelineTracks.tsx +121 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/TrackSidebar.tsx +30 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/VoiceRow.tsx +53 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/Waveform.tsx +97 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/clipActions.ts +50 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/effectActions.ts +161 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/index.tsx +161 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/timelineGeometry.ts +45 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/types.ts +43 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/useAutosave.ts +107 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/useEditorShortcuts.ts +168 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/useFitHeight.ts +40 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/useFollowPlayhead.ts +21 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/useListenMix.ts +43 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/usePlayback.ts +231 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/usePlayerWidth.ts +27 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/useSoundData.ts +124 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/useSoundTimeline.ts +511 -0
- package/studio/web/pages/VideoPage/components/StageDrawer/CompareVersions.tsx +111 -0
- package/studio/web/pages/VideoPage/components/StageDrawer/VersionCard.tsx +81 -0
- package/studio/web/pages/VideoPage/components/StageDrawer/VersionMedia.tsx +24 -0
- package/studio/web/pages/VideoPage/components/StageDrawer/VersionText.tsx +15 -0
- package/studio/web/pages/VideoPage/components/StageDrawer/index.tsx +109 -0
- package/studio/web/pages/VideoPage/components/StageProcess.tsx +126 -0
- package/studio/web/pages/VideoPage/components/StageResult.tsx +69 -0
- package/studio/web/pages/VideoPage/components/SwapReference.tsx +92 -0
- package/studio/web/pages/VideoPage/components/ThumbnailPanel.tsx +252 -0
- package/studio/web/pages/VideoPage/components/TimingPanel.tsx +89 -0
- package/studio/web/pages/VideoPage/components/VideoPlayer.tsx +18 -0
- package/studio/web/pages/VideoPage/components/VideoSkeleton.tsx +50 -0
- package/studio/web/pages/VideoPage/components/VideoVoiceInfo.tsx +33 -0
- package/studio/web/pages/VideoPage/components/VideoVoicePicker.tsx +165 -0
- package/studio/web/pages/VideoPage/components/VoicePauseBanner.tsx +56 -0
- package/studio/web/pages/VideoPage/components/WithLogo.tsx +10 -0
- package/studio/web/pages/VideoPage/index.tsx +295 -0
- package/studio/web/pages/VideosPage/components/PublishedOn.tsx +85 -0
- package/studio/web/pages/VideosPage/components/ServicesPanel.tsx +81 -0
- package/studio/web/pages/VideosPage/components/SessionsPanel.tsx +62 -0
- package/studio/web/pages/VideosPage/components/VideoListSkeleton.tsx +25 -0
- package/studio/web/pages/VideosPage/components/VideoRow.tsx +122 -0
- package/studio/web/pages/VideosPage/index.tsx +134 -0
- package/studio/web/pages/VoicePage/index.tsx +28 -0
- package/studio/web/router.tsx +201 -0
- package/studio/web/styles/base/elements.css +55 -0
- package/studio/web/styles/base/utilities.css +25 -0
- package/studio/web/styles/components/badge.css +33 -0
- package/studio/web/styles/components/claude-running.css +27 -0
- package/studio/web/styles/components/claude-session.css +212 -0
- package/studio/web/styles/components/claude-walking.css +92 -0
- package/studio/web/styles/components/clone-voice-save.css +136 -0
- package/studio/web/styles/components/clone-voice.css +379 -0
- package/studio/web/styles/components/controls.css +70 -0
- package/studio/web/styles/components/data-and-image.css +54 -0
- package/studio/web/styles/components/forms.css +132 -0
- package/studio/web/styles/components/human-banner.css +99 -0
- package/studio/web/styles/components/lightbox.css +50 -0
- package/studio/web/styles/components/markdown.css +68 -0
- package/studio/web/styles/components/modal.css +70 -0
- package/studio/web/styles/components/omni-cards.css +105 -0
- package/studio/web/styles/components/omni.css +859 -0
- package/studio/web/styles/components/omnichat.css +538 -0
- package/studio/web/styles/components/panel.css +115 -0
- package/studio/web/styles/components/platform-card.css +140 -0
- package/studio/web/styles/components/provider-card.css +132 -0
- package/studio/web/styles/components/queue.css +185 -0
- package/studio/web/styles/components/reference-actions.css +133 -0
- package/studio/web/styles/components/reference-picker.css +146 -0
- package/studio/web/styles/components/results-drawer.css +224 -0
- package/studio/web/styles/components/segmented.css +115 -0
- package/studio/web/styles/components/side-panel-tab.css +23 -0
- package/studio/web/styles/components/status-signals.css +66 -0
- package/studio/web/styles/components/stepper.css +208 -0
- package/studio/web/styles/components/table.css +19 -0
- package/studio/web/styles/components/tabs.css +24 -0
- package/studio/web/styles/components/ui/accordion.css +71 -0
- package/studio/web/styles/components/ui/chart.css +162 -0
- package/studio/web/styles/components/ui/checkbox.css +37 -0
- package/studio/web/styles/components/ui/date-picker.css +153 -0
- package/studio/web/styles/components/ui/select.css +110 -0
- package/studio/web/styles/components/ui/skeleton.css +25 -0
- package/studio/web/styles/components/ui/slider.css +81 -0
- package/studio/web/styles/components/ui/switch.css +41 -0
- package/studio/web/styles/components/ui/tooltip.css +24 -0
- package/studio/web/styles/components/voice-carousel.css +255 -0
- package/studio/web/styles/components/voice-editor.css +179 -0
- package/studio/web/styles/components/voice-grid.css +56 -0
- package/studio/web/styles/components/voice-profiles.css +192 -0
- package/studio/web/styles/design-system.md +136 -0
- package/studio/web/styles/index.css +116 -0
- package/studio/web/styles/layout/page.css +41 -0
- package/studio/web/styles/layout/sidebar-groups.css +39 -0
- package/studio/web/styles/layout/sidebar.css +111 -0
- package/studio/web/styles/pages/agent.css +26 -0
- package/studio/web/styles/pages/analytics/dashboard.css +614 -0
- package/studio/web/styles/pages/analytics/insights.css +48 -0
- package/studio/web/styles/pages/getting-started.css +166 -0
- package/studio/web/styles/pages/new-video/form.css +32 -0
- package/studio/web/styles/pages/not-found.css +59 -0
- package/studio/web/styles/pages/references.css +204 -0
- package/studio/web/styles/pages/schedule/calendar.css +541 -0
- package/studio/web/styles/pages/schedule/chip-states.css +60 -0
- package/studio/web/styles/pages/settings/models.css +72 -0
- package/studio/web/styles/pages/settings/shell.css +361 -0
- package/studio/web/styles/pages/settings/usage.css +63 -0
- package/studio/web/styles/pages/studio-offline.css +105 -0
- package/studio/web/styles/pages/video/caption-controls.css +194 -0
- package/studio/web/styles/pages/video/captions-grid.css +70 -0
- package/studio/web/styles/pages/video/captions.css +79 -0
- package/studio/web/styles/pages/video/editor-chat.css +103 -0
- package/studio/web/styles/pages/video/export.css +50 -0
- package/studio/web/styles/pages/video/frames.css +29 -0
- package/studio/web/styles/pages/video/fullscreen-stage.css +400 -0
- package/studio/web/styles/pages/video/instagram-carousel.css +309 -0
- package/studio/web/styles/pages/video/layout.css +285 -0
- package/studio/web/styles/pages/video/narration.css +138 -0
- package/studio/web/styles/pages/video/preview-overview.css +11 -0
- package/studio/web/styles/pages/video/processing-notice.css +34 -0
- package/studio/web/styles/pages/video/publish-when.css +76 -0
- package/studio/web/styles/pages/video/publish.css +97 -0
- package/studio/web/styles/pages/video/result.css +96 -0
- package/studio/web/styles/pages/video/scenes.css +167 -0
- package/studio/web/styles/pages/video/sound-timeline.css +672 -0
- package/studio/web/styles/pages/video/sound-toolbar.css +149 -0
- package/studio/web/styles/pages/video/take-player.css +100 -0
- package/studio/web/styles/pages/video/thumbnail.css +157 -0
- package/studio/web/styles/pages/video/timeline.css +117 -0
- package/studio/web/styles/pages/video/voice-picker.css +14 -0
- package/studio/web/styles/pages/video-list/active-row.css +27 -0
- package/studio/web/styles/pages/video-list/card-link.css +49 -0
- package/studio/web/styles/pages/video-list/list.css +86 -0
- package/studio/web/styles/theme/identity/details.css +204 -0
- package/studio/web/styles/theme/identity/shell.css +249 -0
- package/studio/web/styles/theme/identity/surfaces.css +183 -0
- package/studio/web/styles/theme/identity/titles-and-buttons.css +120 -0
- package/studio/web/styles/theme/no-borders/blocks.css +208 -0
- package/studio/web/styles/theme/no-borders/controls.css +256 -0
- package/studio/web/styles/tokens.css +245 -0
- package/studio/web/types/api.ts +2 -0
- package/studio/web/types/assets.d.ts +9 -0
- package/templates/brief.md +27 -0
- package/templates/captions.html +204 -0
- package/templates/carousel-composition.md +47 -0
- package/templates/carousel-script.md +60 -0
- package/templates/components/voiceavatar/README.md +87 -0
- package/templates/components/voiceavatar/demo.html +50 -0
- package/templates/components/voiceavatar/voiceavatar.js +415 -0
- package/templates/motion.md +80 -0
- package/templates/playbook.md +43 -0
- package/templates/reference.md +123 -0
- package/templates/scene.html +112 -0
- package/templates/scenes.example.json +96 -0
- package/templates/script.md +63 -0
- package/templates/slide.html +71 -0
- package/templates/wireframe.html +91 -0
- package/tools/analysis.mjs +300 -0
- package/tools/approval.mjs +442 -0
- package/tools/assemble.mjs +942 -0
- package/tools/build-motion.mjs +437 -0
- package/tools/captions.mjs +246 -0
- package/tools/carousel.mjs +558 -0
- package/tools/compose.mjs +307 -0
- package/tools/cuts.mjs +74 -0
- package/tools/editor.mjs +419 -0
- package/tools/epidemic-capture.js +54 -0
- package/tools/fanout.mjs +69 -0
- package/tools/history.mjs +70 -0
- package/tools/history.py +61 -0
- package/tools/job-queue.mjs +180 -0
- package/tools/job_queue.py +430 -0
- package/tools/lib/agents.mjs +412 -0
- package/tools/lib/channel.mjs +399 -0
- package/tools/lib/claude-settings.mjs +67 -0
- package/tools/lib/cli.mjs +9 -0
- package/tools/lib/fanout.mjs +234 -0
- package/tools/lib/http.mjs +94 -0
- package/tools/lib/index.js +14 -0
- package/tools/lib/mcp-server.mjs +69 -0
- package/tools/lib/metrics/metrics.mjs +204 -0
- package/tools/lib/narration.mjs +15 -0
- package/tools/lib/oauth-client.mjs +295 -0
- package/tools/lib/omnichat/config.mjs +139 -0
- package/tools/lib/omnichat/errors.mjs +42 -0
- package/tools/lib/omnichat/extract.mjs +82 -0
- package/tools/lib/omnichat/gate.mjs +151 -0
- package/tools/lib/omnichat/manual.mjs +166 -0
- package/tools/lib/omnichat/match.mjs +78 -0
- package/tools/lib/omnichat/pass.mjs +393 -0
- package/tools/lib/omnichat/record.mjs +208 -0
- package/tools/lib/omnichat/text.mjs +315 -0
- package/tools/lib/omnichat/webhook.mjs +166 -0
- package/tools/lib/pairing.mjs +99 -0
- package/tools/lib/paths.mjs +41 -0
- package/tools/lib/platforms/instagram.mjs +469 -0
- package/tools/lib/platforms/tiktok.mjs +343 -0
- package/tools/lib/platforms/youtube.mjs +228 -0
- package/tools/lib/publish/publish.mjs +244 -0
- package/tools/lib/schedule/schedule.mjs +191 -0
- package/tools/lib/service.mjs +134 -0
- package/tools/lib/skill-client.mjs +126 -0
- package/tools/lib/skills-release.mjs +196 -0
- package/tools/lib/testing.mjs +52 -0
- package/tools/lib/up.mjs +187 -0
- package/tools/metrics.mjs +415 -0
- package/tools/models.mjs +134 -0
- package/tools/narrate.mjs +257 -0
- package/tools/omni.mjs +298 -0
- package/tools/omni_paths.py +12 -0
- package/tools/omnichat.mjs +1577 -0
- package/tools/omnivideo.py +1122 -0
- package/tools/package.mjs +130 -0
- package/tools/platforms.mjs +236 -0
- package/tools/premiere.mjs +533 -0
- package/tools/publish.mjs +1496 -0
- package/tools/reference.mjs +1408 -0
- package/tools/render.mjs +426 -0
- package/tools/runner.mjs +156 -0
- package/tools/setup.mjs +466 -0
- package/tools/skills-release.mjs +93 -0
- package/tools/sound.mjs +1323 -0
- package/tools/soundtrack.mjs +217 -0
- package/tools/thumbnail.mjs +271 -0
- package/tools/timing.mjs +108 -0
- package/tools/usage.py +628 -0
- package/tools/voice-envelope.mjs +185 -0
- package/tools/voice.py +448 -0
- package/tools/voice_sample.py +75 -0
- package/tools/wireframe.mjs +230 -0
|
@@ -0,0 +1,666 @@
|
|
|
1
|
+
# OmniVideo (studio)
|
|
2
|
+
|
|
3
|
+
The OmniVideo screen follows a design system taken from the ElevenLabs app: light
|
|
4
|
+
neutrals, Schibsted Grotesk for text and Geist Mono for numbers and code, from Google
|
|
5
|
+
Fonts. Tokens in `studio/web/styles/tokens.css`; the rest in the partials that
|
|
6
|
+
`studio/web/styles/index.css` imports, in cascade order. Details and where each rule
|
|
7
|
+
lives: `studio/web/styles/design-system.md`.
|
|
8
|
+
|
|
9
|
+
A local app that is the hub of this folder's pipeline. You can **start from
|
|
10
|
+
it**: a new video and a new reference become a Claude session (`claude -p`,
|
|
11
|
+
with the same skills as the chat), which the screen follows live and answers.
|
|
12
|
+
It also covers what the chat does poorly: seeing several videos at once,
|
|
13
|
+
reviewing media (players), coordinating the machine when videos run in
|
|
14
|
+
parallel, and browsing the reference library.
|
|
15
|
+
|
|
16
|
+
Each video's files remain the source of truth. The studio reads, watches and
|
|
17
|
+
calls the tools in `tools/`; it has no database and never redoes any tool's
|
|
18
|
+
work. Every tool works the same with the studio on or off, and the chat still
|
|
19
|
+
counts: a video touched from the chat shows up the same way.
|
|
20
|
+
|
|
21
|
+
## Start
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
cd <project folder>/studio && bun start # http://127.0.0.1:5432
|
|
25
|
+
bun start --open # and open it in the browser
|
|
26
|
+
bun run dev # with hot reload (for working on the studio)
|
|
27
|
+
bun studio.ts --videos <dir> # also show the videos from another folder
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
`bun studio.ts --runner` (or `cloud.studio: true` in the config) also connects this server to the
|
|
31
|
+
hosted studio (`cloud/studio`, `reference/cloud.md`): the same app answers the requests that come
|
|
32
|
+
through the cloud, so the hosted screen is this one. It connects with the key that
|
|
33
|
+
`node tools/runner.mjs login` writes into `.env`, and picks a new one up within 30 s. `bun run build` writes the web the cloud serves
|
|
34
|
+
into `studio/dist/`.
|
|
35
|
+
|
|
36
|
+
`bun start` installs the dependencies (react, react-dom, marked) if missing
|
|
37
|
+
and starts the server. It listens only on `127.0.0.1:5432` (3900 and 3901
|
|
38
|
+
belong to VoiceStudio). Bun 1.4, TypeScript, React; no build step: Bun
|
|
39
|
+
bundles the web app at startup, and with `start` only at startup (changed the
|
|
40
|
+
web app, restart). `bun run dev` turns on Bun's development server (`--dev`):
|
|
41
|
+
a change in `web/` shows up in the browser right away (HMR, React and CSS
|
|
42
|
+
without reloading), and `--watch` restarts the server when `studio.ts` or
|
|
43
|
+
`server/` changes. Claude sessions and queue tools run in their own processes
|
|
44
|
+
and survive the restart. `bun run types` checks the types.
|
|
45
|
+
|
|
46
|
+
## How the screen works
|
|
47
|
+
|
|
48
|
+
**Content, not just video.** Each folder in `videos/` is a piece of content that
|
|
49
|
+
delivers a video, an Instagram carousel or both (`content.json`, written by the
|
|
50
|
+
request). A new request picks it in the "O que criar" (What to create) cards;
|
|
51
|
+
carousel only hides format and voice. The content page has one stepper per
|
|
52
|
+
deliverable, with the Vídeo | Carrossel (Video | Carousel) switch on top when there
|
|
53
|
+
are two; the stages of each pipeline are in `web/lib/stages.ts` (`PIPELINES`) and
|
|
54
|
+
each one's result in `web/pages/VideoPage/components/StageResult.tsx` (the
|
|
55
|
+
carousel's in `web/pages/VideoPage/components/Carousel*.tsx`). A new pipeline = a
|
|
56
|
+
list in `PIPELINES`, its state in `getStageState` and one component per stage. The
|
|
57
|
+
carousel: **Roteiro** (Script: the slides, `script.md` and the caption, with "Pedir
|
|
58
|
+
alteração" (Request a change)), **Composição** (Composition: the slides in swipe
|
|
59
|
+
order, each with the build warnings and "Pedir alteração", the sheet and
|
|
60
|
+
`composition.md`) and **Publicar** (Publish: a one-slide-at-a-time viewer, the
|
|
61
|
+
editable caption, the button that uploads to Instagram, the JPEGs and the post's
|
|
62
|
+
omnichat). A video without a carousel gets "Fazer carrossel" (Make carousel) in the
|
|
63
|
+
header. The data comes from `api/services/carousel.ts`, which imports the diagnosis from
|
|
64
|
+
`tools/carousel.mjs`.
|
|
65
|
+
|
|
66
|
+
**The banners on top** (`web/components/TopBanners.tsx`, fixed on every screen; their joint height is
|
|
67
|
+
`--banner`): the studio not connected, in the hosted one (`RunnerBanner`), then **what broke** in red
|
|
68
|
+
(`ErrorBanner`, the rules in `web/lib/alerts.ts`), then **what waits for a decision** (`HumanBanner`:
|
|
69
|
+
script, scenes, voice being chosen). The red one lists a scheduled post that failed or missed its time, a
|
|
70
|
+
platform whose last publish failed (video or carousel), a video whose status can't be read, a studio
|
|
71
|
+
session that stopped with an error in the last 24 h, no agent account free (all at their limit, none
|
|
72
|
+
switched on or none logged in; one at its limit is the rotation working), a connected platform that
|
|
73
|
+
refuses its token or whose token expires within 7 days, and the omnichat paused by Meta or its cloud not
|
|
74
|
+
answering. Each line links to where it is fixed and leaves on its own when the condition clears; × hides
|
|
75
|
+
it in that browser until it fails again (the key names the occurrence). Settings, the platform accounts
|
|
76
|
+
and the omnichat state have no SSE notice, so the banner asks for them again every 3 min.
|
|
77
|
+
|
|
78
|
+
The video list has four tabs: **Em processamento** (In progress: pipeline
|
|
79
|
+
incomplete), **Finalizados** (Finished: exported, not published),
|
|
80
|
+
**Publicados** (Published: with a link on some platform, or scheduled; the
|
|
81
|
+
scheduled ones on top, with each platform's icon) and **Arquivados**
|
|
82
|
+
(Archived: the "Arquivar" (Archive) button at the end of the row;
|
|
83
|
+
"Desarquivar" (Unarchive) puts the video back in the group it would be in).
|
|
84
|
+
The classification is done by `web/lib/video-tabs.ts`.
|
|
85
|
+
|
|
86
|
+
The **Publicar** (Publish) stage also has the **Thumbnail (opcional)**
|
|
87
|
+
section, with four slots: "Gerar 3 aleatórias" (Generate 3 random) asks the
|
|
88
|
+
video's Claude for three randomly drawn ideas (v1–v3), and the fourth slot has
|
|
89
|
+
a box for your instruction (v4). The two requests are separate: redoing one
|
|
90
|
+
does not erase the other. All of them come out in the video's style and
|
|
91
|
+
aspect ratio, show up on screen by themselves, and each has "Usar este na
|
|
92
|
+
publicação" (Use this one in the post) and "Baixar" (Download) below it. The
|
|
93
|
+
selected one becomes `thumbnail/thumbnail.png` and is the post's cover: the
|
|
94
|
+
publish form shows which one goes, and `publish.mjs` sends it to YouTube and
|
|
95
|
+
Instagram at upload time (when scheduled, whichever is selected when the
|
|
96
|
+
scheduler runs goes). TikTok gets a frame from the video. "Selecionada ·
|
|
97
|
+
tirar" (Selected · remove) goes back to the video frame.
|
|
98
|
+
|
|
99
|
+
Every finished version has **"Pedir alteração"** (Request a change): the user
|
|
100
|
+
writes only what changes and Claude starts from that version's HTML, keeps
|
|
101
|
+
everything else literally (spot changes, no redesign) and renders only that
|
|
102
|
+
version. The previous HTML and PNG stay in `history/thumbnail/<vN>/`, the
|
|
103
|
+
request in `thumbnail/request.json` (`changes`), and "Alterada: …"
|
|
104
|
+
(Changed: …) appears under the version. Redoing the version from scratch
|
|
105
|
+
(drawing another 3, redoing yours) clears that mark; changing the selected one
|
|
106
|
+
removes the selection.
|
|
107
|
+
|
|
108
|
+
The screen is organized by the pipeline stages. Each video has a **stepper**
|
|
109
|
+
with the 11 stages of `status` (1, 2, P, 3…8), and each stage shows its
|
|
110
|
+
current state:
|
|
111
|
+
|
|
112
|
+
| state | when |
|
|
113
|
+
|---|---|
|
|
114
|
+
| feita (done) | `status` marks the stage |
|
|
115
|
+
| rodando · 1min12s (running) | a tool of this stage holds the turn, for this video |
|
|
116
|
+
| na fila · 2º (queued) | waiting for its turn (the position is in the whole machine's queue) |
|
|
117
|
+
| esperando você (waiting for you) | an approval round (script or scenes) waiting for the user |
|
|
118
|
+
| atenção (warning) | assembly outdated, or render with a changed start |
|
|
119
|
+
| erro (error) | the last run of this stage failed and it is not done |
|
|
120
|
+
| próxima (next) | the next stage in `status` |
|
|
121
|
+
|
|
122
|
+
| page | what |
|
|
123
|
+
|---|---|
|
|
124
|
+
| `/videos` | all videos at once, each with its stepper; clicking a stage opens the video on it. Alongside, the Claude sessions, the machine queue and the services |
|
|
125
|
+
| `/new` | new video: request, subject, format, duration, reference → folder, `source/request.md` and a Claude session |
|
|
126
|
+
| `/videos/<slug>[/<stage>]` | the stepper, the video's **Claude session** (below), and for the chosen stage: on the left the **process** (state, the running run with its live log or the last one with its log, who does it, command, what it writes); on the right the **result**; and the **"Resultados anteriores"** (Previous results) button, which opens the drawer |
|
|
127
|
+
| `/styles` | the reference library (cover, essence, measured summary, palette) and the request for a new one |
|
|
128
|
+
| `/voice` | **"Perfil de voz"** (Voice profile) in the menu (icon: two randomly drawn avatars): the voice carousel (profiles, active, edit, clone), the same as Configurações → Voz (Settings → Voice), straight from the menu |
|
|
129
|
+
| `/styles/<name>` | one reference: Claude session, palette, motion strip, videos, frames and sheets, guide and tokens in tabs, videos that use it |
|
|
130
|
+
| `/queue` | who is running (with a live log), who is waiting, the latest ones with each one's log |
|
|
131
|
+
| `/schedule[/week\|/day]` | the scheduled posts of every video (`schedule` in publication.json) by month, week and day, with each post's platforms and the selected cover. Only the day view shows the time, with the post at the exact time (posts at the same time share the width) and the now line; in the month view, hovering a day shows its videos with each one's time; in the week view, hovering a post shows its time. Clicking a post opens the video's Publicar stage; clicking a day (month) or the header (week) opens the day. **Dragging reschedules** (`POST /api/videos/<slug>/publish/schedule/reschedule`, `{ when }` → `publish.mjs schedule --reschedule`): in month and week, dropping on another day opens the time picker (starting at the post's current time); in the day view, the time is where the card lands, in 5 min steps, with a guide showing the time while dragging. A day or time already past does not accept the drop; a post being published does not drag |
|
|
132
|
+
| `/` and `/analytics/<slug>` | **Dashboard**, the first menu item and the home page, following shadcn's dashboard-01 pattern. At the top, the collection state ("coleta de 4 em 4 h · última leitura há…"; clicking turns the agent on and off), "Coletar agora" (Collect now; GET-only on the API) and "Gerar análise" (Generate analysis). Four metric cards: followers (change since the previous snapshot), views in 7 days (sum of the Reels published in the week, against the previous one), 3 s skip rate and % watched (medians of the last 5, against the 5 before; the "before OmniVideo" figure comes from the playbook). Progression: the hour-by-hour sum of the Reels (`progression` from the context) as a wave line with area, of views, comments, saves or followers, over 24 h, 7 or 30 days, with a dot at each published Reel. Comparing the Reels (y bar with metric tabs and the median; the ones checked in the table are highlighted) and Highlights (best hook, most saves and most comments per thousand views). Tabs: Reels (the data table: sort by column, Columns to show and hide, filter by pillar and goal, search, selection with "Comparar" (Compare) side by side with the median and the best of each row, pagination from 20), Insights (the summary sentence of the latest `channel/analyses/<day>.md` and each insight in an accordion; the full analysis and the request with a focus also in accordions; the Claude session, type `analysis`, target `channel`), Plano (Plan) and Gancho × miolo (Hook × body). Explanations live in ⓘ or in the footer accordions. `/analytics/<slug>`: one Reel, with the "Dashboard / title" breadcrumb, "Analisar este Reel" (Analyze this Reel), four cards against the channel median (views, 3 s skip rate, % watched, saves per thousand), the hour-by-hour wave line (`instagram.hourly`, 24 h and 72 h marked) and the tabs Diagnóstico (Diagnosis: the `analysis.md`, sections in accordions), Abertura (Opening: the sheet of the first 3 seconds and the script's hook), Marcos (Milestones) and Roteiro (Script); the other metrics in an accordion |
|
|
133
|
+
|
|
134
|
+
Without a stage in the URL, the page opens on what matters now: what is
|
|
135
|
+
running, what is waiting for you, what failed, what is queued, the next one; a
|
|
136
|
+
finished video opens on the export.
|
|
137
|
+
|
|
138
|
+
The result of each stage:
|
|
139
|
+
|
|
140
|
+
| stage | result |
|
|
141
|
+
|---|---|
|
|
142
|
+
| 1 script | `script.md` and `brief.md` |
|
|
143
|
+
| 2 motion script | table of the scenes (title, hero, goal) and `motion.md` |
|
|
144
|
+
| 3 composition | still and sheet of each scene with the minimum duration, the transition, the beats and how many sounds; the cut sheets, the overall ones |
|
|
145
|
+
| 4 narration | the lines of each scene and each beat's `enters_with` |
|
|
146
|
+
| 5 voice | each line with a player, duration and words per second |
|
|
147
|
+
| 5 timing | the video's timeline (scenes, lines and beats at the computed times) and the table |
|
|
148
|
+
| 6 render | each scene's MP4 and the **sound timeline** (editor below) |
|
|
149
|
+
| 7 captions | **position on screen** (editor below), and the captions from `captions.srt` |
|
|
150
|
+
| 8 export | the MP4 from `assemble.mjs`, what went in (scenes, lines, effects, captions, loudness) and the check sheet |
|
|
151
|
+
|
|
152
|
+
**Caption position** (stage 7): a preview of `templates/captions.html` (the
|
|
153
|
+
same HTML that `assemble.mjs` renders) over the scene's real frame, with the
|
|
154
|
+
safe area marked. You pick the scope (all, the scene of the selected caption,
|
|
155
|
+
only that caption), the platform's safe area and the style (only in all), the
|
|
156
|
+
anchor in the 3×3 grid and X/Y (slider, number, ±10, dragging the caption in
|
|
157
|
+
the preview). In All, also the safe area **Margens** (Margins: top, bottom,
|
|
158
|
+
left, right, in % of the frame; changing the platform resets the adjustment).
|
|
159
|
+
A caption may sit outside the safe area: the preview shows the warning with
|
|
160
|
+
the side and the px, and does not block. Inherited values appear faded. Below
|
|
161
|
+
the position, **Cor, contorno e sombra** (Color, outline and shadow: text,
|
|
162
|
+
pill background and opacity, outline, shadow with color, opacity, blur and
|
|
163
|
+
X/Y), in the same chosen scope (all, scene or only that caption); each field
|
|
164
|
+
has ↺ to go back to the level above or the style default. **"Gravar
|
|
165
|
+
posição"** (Save position) writes `captions` in `scenes.json`; it applies at
|
|
166
|
+
the next assembly. Rules in `reference/captions.md`.
|
|
167
|
+
|
|
168
|
+
**Sound timeline** (stage 6, full screen, without the result header): on the
|
|
169
|
+
left, the preview filling the space, with the controls in a floating pill in
|
|
170
|
+
the bottom corner (play, start, voice volume from 0 to 100% only for listening
|
|
171
|
+
here (the export keeps the voice at its own level; the effects' ducking stays
|
|
172
|
+
the same, so you hear the effects as they sit under the voice), time, zoom, ?
|
|
173
|
+
with the voice level and the tips), and the timeline below, with Renderizar
|
|
174
|
+
(Render) and the save state; both stay fixed, fitting the window. On the
|
|
175
|
+
right, the only column that scrolls: Resultados anteriores, the plan's
|
|
176
|
+
warnings, the chosen effect's panel (the column scrolls back to the top when
|
|
177
|
+
choosing), the **sound library** in 3-column cards (the ones captured for the
|
|
178
|
+
video and the kit by role, with search and ▶ to hear the file) and, after it,
|
|
179
|
+
the scene renders in 2 columns. The timeline has a ruler, scenes, lines (with
|
|
180
|
+
the ducking segments shaded), a diamond per beat (▲ at scene changes), the
|
|
181
|
+
effects in lanes colored by kit role, and the bed. Clicking an empty diamond
|
|
182
|
+
puts a sound on the beat (▲: on the scene change); dragging an effect changes
|
|
183
|
+
its `offset`, and with **Shift** duplicates it (the original stays; the copy,
|
|
184
|
+
with the same settings, goes to the beat nearest to where it was dropped);
|
|
185
|
+
**Ctrl or Cmd + click** selects several, which drag together and, with Shift,
|
|
186
|
+
duplicate together (each copy with the same duration and the same spacing, and
|
|
187
|
+
the copies stay selected); **Delete** or Backspace removes whatever is
|
|
188
|
+
selected (one, several or the bed). A library sound goes in by dragging it to
|
|
189
|
+
the timeline, or with one click on it and another on the timeline (Esc
|
|
190
|
+
cancels): on a diamond it becomes a layer of that beat, on an effect it
|
|
191
|
+
replaces that effect's sound, on the bed row it becomes the bed (music only
|
|
192
|
+
goes there), and on empty space it goes to the nearest beat, with the
|
|
193
|
+
`offset` that puts the peak where it was dropped. The chosen effect's panel
|
|
194
|
+
has the sound (the kit by role and what was captured for the video), anchor,
|
|
195
|
+
`offset`, volume, the **trim** on the file's waveform (drag the edges),
|
|
196
|
+
fades, stretch (`dur`) or speed, pitch, ducking, layers (+ layer) and remove.
|
|
197
|
+
The bed has sound, volume, from/to, fades, loop and ducking. Every adjustment
|
|
198
|
+
is planned by `tools/sound.mjs` itself in a draft (`.sound-draft/<slug>/`,
|
|
199
|
+
outside the video folder, so it doesn't trigger the watcher), and the screen
|
|
200
|
+
plays the finished WAVs with the voice in Web Audio, with the muted export on
|
|
201
|
+
top just for the picture: what you hear is what `assemble.mjs` mixes. Change
|
|
202
|
+
something while playing and it restarts from the same point; Space plays and
|
|
203
|
+
pauses. **Saving is automatic**: 5 s after the last edit, the draft goes to
|
|
204
|
+
`scenes.json` (`sound` on the beats, `transition.sound` and `bed`), and
|
|
205
|
+
leaving the screen before that saves on the way out. Saving does not render:
|
|
206
|
+
**Renderizar** (runs `assemble.mjs` through the queue) lights up when there is
|
|
207
|
+
a saved edit the export doesn't have yet (`scenes.json` newer than the
|
|
208
|
+
export MP4).
|
|
209
|
+
|
|
210
|
+
A stage's **previous results** drawer has the versions the tools kept (see
|
|
211
|
+
below), each with the queue run that produced it and its log, to play, view
|
|
212
|
+
and **compare side by side with the current one**; and every run of the stage
|
|
213
|
+
for this video, with its log.
|
|
214
|
+
|
|
215
|
+
Stage and next come from `omnivideo.py status --json`; each beat's time from
|
|
216
|
+
`tools/timing.mjs` (`beatTime`), the same one the build uses. Everything
|
|
217
|
+
updates by itself: the server watches the video folders and `.queue/` and
|
|
218
|
+
notifies the screen over SSE.
|
|
219
|
+
|
|
220
|
+
## Previous versions (`history/`)
|
|
221
|
+
|
|
222
|
+
The tools overwrite their result on every run. So the drawer has something to
|
|
223
|
+
show, before overwriting they copy the version that was there, with the
|
|
224
|
+
file's original time, into the video:
|
|
225
|
+
|
|
226
|
+
```
|
|
227
|
+
videos/<slug>/history/<stage>/[<key>/]<stamp>/
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
| stage | who keeps it | what | key |
|
|
231
|
+
|---|---|---|---|
|
|
232
|
+
| `composition` | `build-motion.mjs --stills/--sheets/--transitions` | the scene's still and sheet; the cut's sheet | `c01`, `transition-c01-c02` |
|
|
233
|
+
| `voice` | `omnivideo.py voice` | the WAV and `voice/<line>.json` (the text it said) | `c01f1` |
|
|
234
|
+
| `render` | `build-motion.mjs --render` | the scene's MP4 and `render.json` | `c01` |
|
|
235
|
+
| `sound` | `sound.mjs` (and `assemble.mjs`) | `plan.json` and `preview.wav` | — |
|
|
236
|
+
| `captions` | `omnivideo.py captions` | `captions.srt` | — |
|
|
237
|
+
| `assembly` | `assemble.mjs` (and `premiere.mjs assemble`, if used) | the MP4, the sheet and `assembly.json` (or `premiere.json`) | — |
|
|
238
|
+
| `check` | `premiere.mjs check` (optional) | the frames | — |
|
|
239
|
+
|
|
240
|
+
- **Only what changed is kept.** If the new result comes out equal to the
|
|
241
|
+
previous one (deterministic render of a scene that didn't change, `sound`
|
|
242
|
+
with no change), the copy is discarded.
|
|
243
|
+
- **The last 5 are kept** for each result (`history.versions` in the config).
|
|
244
|
+
- **The version leads to the run by time.** The stamp is the time the
|
|
245
|
+
original result was written, which falls inside the run that produced it;
|
|
246
|
+
the screen finds that run in the queue. Composition doesn't go through the
|
|
247
|
+
queue, so its versions have no run.
|
|
248
|
+
- **Loose exports:** the stage 8 drawer also lists the other MP4s in
|
|
249
|
+
`export/` (an old Premiere export, for example).
|
|
250
|
+
- The helper is `tools/history.mjs` (`keepPrevious`) and its Python twin
|
|
251
|
+
`tools/history.py` (`keep_previous`). `history/` is outside git.
|
|
252
|
+
- Since this version, `voice` writes `voice/<line>.json` next to each WAV
|
|
253
|
+
(text, instruction, hash, duration), like the MP4's `render.json`: that is
|
|
254
|
+
what lets a line's previous version show what it said.
|
|
255
|
+
|
|
256
|
+
Services, without touching anything:
|
|
257
|
+
|
|
258
|
+
| service | how it is seen |
|
|
259
|
+
|---|---|
|
|
260
|
+
| VoiceStudio | `GET /health` |
|
|
261
|
+
| Premiere | the heartbeat the MCP Bridge panel writes to `<premiere.temp_dir>/bridge-heartbeat.json` every ~1 s. Up to 2.5 s the bridge is connected (the same limit as the MCP server); up to 15 s, slow bridge (the panel lags with Premiere busy); beyond that, bridge down |
|
|
262
|
+
| memory | `sysctl vm.swapusage kern.memorystatus_level kern.memorystatus_vm_pressure_level`; swap ≥ 90 % turns red: the voice starts blowing past the 305 s |
|
|
263
|
+
|
|
264
|
+
The studio does **not** call `premiere.mjs status`: `verify_premiere_connection`
|
|
265
|
+
opens Premiere if it is closed and sends a script to the bridge, which runs
|
|
266
|
+
one at a time, in the middle of an assembly.
|
|
267
|
+
|
|
268
|
+
## Machine queue
|
|
269
|
+
|
|
270
|
+
One heavy task at a time on the machine, whether it comes from the chat, the
|
|
271
|
+
terminal or the studio:
|
|
272
|
+
|
|
273
|
+
| tool | goes through the queue | stays out |
|
|
274
|
+
|---|---|---|
|
|
275
|
+
| `omnivideo.py` | `voice` (only if there is a line to generate), `captions` | `status`, `timing` |
|
|
276
|
+
| `build-motion.mjs` | with `--render` | stills, sheets and transitions |
|
|
277
|
+
| `sound.mjs` | `sound.mjs videos/<slug>` | `capture`, `download`, `effects`; the `prepareSound` inside `assemble` is already in its turn |
|
|
278
|
+
| `assemble.mjs` | assembling and exporting, and `--sheet` (tool `assembly`) | — |
|
|
279
|
+
| `premiere.mjs` (optional) | `assemble`, `check`, `export` | `status`, `call` |
|
|
280
|
+
|
|
281
|
+
`check` goes in because the Premiere bridge runs one script at a time and
|
|
282
|
+
`export` locks Premiere: a `check` in the middle of an export would get no
|
|
283
|
+
answer. For the same reason, `premiere.mjs status` with `assemble`, `check`
|
|
284
|
+
or `export` running answers "Premiere ocupado" (Premiere busy) right away
|
|
285
|
+
(exit 0), without talking to the bridge, and `call` refuses (exit 1) until the
|
|
286
|
+
task ends.
|
|
287
|
+
|
|
288
|
+
Whatever can be checked without the machine is checked **before** waiting for
|
|
289
|
+
the turn: `voice` with no new line, `captions` without audio or without
|
|
290
|
+
timing, `--render` with provisional timing, `assemble` with a missing MP4 or
|
|
291
|
+
captions, `check` or `export` without `premiere.json`. The error comes out
|
|
292
|
+
right away; a chat doesn't wait half an hour in the queue to find out a file
|
|
293
|
+
was missing.
|
|
294
|
+
|
|
295
|
+
### How it works
|
|
296
|
+
|
|
297
|
+
The mechanism is always the same, with the studio on or not: **the file lock
|
|
298
|
+
is the queue, and the studio only shows it.** So nothing changes when the
|
|
299
|
+
studio crashes, restarts or starts in the middle of a task.
|
|
300
|
+
|
|
301
|
+
1. The tool writes a ticket, `.queue/<id>.json`: tool, video, command, pid,
|
|
302
|
+
origin (`chat`, `terminal`, `studio`), arrival.
|
|
303
|
+
2. It waits to be the oldest live ticket waiting and then takes the `flock`
|
|
304
|
+
on `.queue/machine.lock`. The kernel releases the flock when the process
|
|
305
|
+
dies, even with `kill -9`: there is no forgotten lock.
|
|
306
|
+
3. On its turn, the tool's output goes to the terminal as always **and** to
|
|
307
|
+
`.queue/<id>.log`, which the screen shows live.
|
|
308
|
+
4. On exit, the ticket becomes `ok`, `error` (with the exit code) or
|
|
309
|
+
`interrupted`.
|
|
310
|
+
|
|
311
|
+
A `waiting` or `running` ticket of a dead process, or without a heartbeat for
|
|
312
|
+
more than 15 s (a live ticket renews its mtime every 2 s; that is what catches
|
|
313
|
+
a reused pid), counts as interrupted and gets out of the way. The last 100
|
|
314
|
+
finished tickets are kept, with their logs.
|
|
315
|
+
|
|
316
|
+
The protocol lives in `tools/job_queue.py` (`with turn("voice", dir):`). The
|
|
317
|
+
Node tools use `tools/job-queue.mjs` (`await turn("render", dir)`), which
|
|
318
|
+
runs `job_queue.py` as a guardian: a Python process that waits for the turn,
|
|
319
|
+
holds the flock while the Node process lives and releases it when the stdin
|
|
320
|
+
pipe closes.
|
|
321
|
+
|
|
322
|
+
Details:
|
|
323
|
+
|
|
324
|
+
- **Without contention, nothing changes in the output.** The waiting notice
|
|
325
|
+
only appears when there is a wait.
|
|
326
|
+
- **Whoever waits re-reads `scenes.json` on its turn.** The wait can be long
|
|
327
|
+
and the chat may have changed the video meanwhile.
|
|
328
|
+
- **A child inside its parent's turn doesn't wait.** The task holding the turn
|
|
329
|
+
passes `OMNIVIDEO_TURN` to its children; a child asking for the turn inside
|
|
330
|
+
it goes straight through (avoids the deadlock of a heavy tool calling
|
|
331
|
+
another).
|
|
332
|
+
- **`OMNIVIDEO_QUEUE=0` turns the queue off.** Emergencies only: without it,
|
|
333
|
+
two heavy tasks fight over the machine again.
|
|
334
|
+
|
|
335
|
+
### In the terminal
|
|
336
|
+
|
|
337
|
+
```bash
|
|
338
|
+
PY=.venv/bin/python
|
|
339
|
+
$PY tools/job_queue.py # who runs, who waits, the latest ones
|
|
340
|
+
$PY tools/job_queue.py --json # the same as data
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
Whoever waits sees on stderr (the message text is PT):
|
|
344
|
+
|
|
345
|
+
```
|
|
346
|
+
fila: esperando a vez de voz · rodando voz · 2026-10-12-x (chat 04e33526, pid 86225) há 2min13s; 1 na frente
|
|
347
|
+
fila: vez de voz depois de 2min30s de espera
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
## The chat and the queue
|
|
351
|
+
|
|
352
|
+
- **A heavy tool may wait for its turn.** With another video running, a
|
|
353
|
+
`voice` or a `--render` can sit for minutes before starting. Run heavy tools
|
|
354
|
+
in the background (`run_in_background`) and wait for the completion
|
|
355
|
+
notification, instead of a command with a timeout: the Bash timeout kills
|
|
356
|
+
the tool mid-wait, and it loses its place in the queue.
|
|
357
|
+
- **Before concluding it is stuck,** look at `$PY tools/job_queue.py`: if it is
|
|
358
|
+
`waiting`, it is in the queue; if it is `running`, the log is in
|
|
359
|
+
`.queue/<id>.log`.
|
|
360
|
+
- **Don't kill another video's task** to jump ahead. It belongs to another
|
|
361
|
+
session.
|
|
362
|
+
|
|
363
|
+
## Scene approval in the studio
|
|
364
|
+
|
|
365
|
+
After the motion script, `av-motion` opens an approval round
|
|
366
|
+
(`tools/approval.mjs open`) and the session sits in `approval.mjs wait`. In
|
|
367
|
+
stage 2 of the video page, each scene shows title, hero, goal and visual,
|
|
368
|
+
with **"Aprovar"** (Approve) and **"Pedir alteração"** (Request a change; the
|
|
369
|
+
comment is required: it is what Claude will adjust), and **"Aprovar todas"**
|
|
370
|
+
(Approve all) at the bottom.
|
|
371
|
+
|
|
372
|
+
- **Notice.** On any studio page, a new round plays a short sound (Web Audio,
|
|
373
|
+
synthesized; the browser only plays it after some click on the page) and the
|
|
374
|
+
tab title gets `● Aprovar cenas 9:41 · …` with the countdown. A round that
|
|
375
|
+
was already waiting when the page opened only shows in the title.
|
|
376
|
+
- **Automatic approval.** With nobody touching it, the pending scenes approve
|
|
377
|
+
themselves at the deadline (`motion.approval.auto_approve_min`, 10 min) and
|
|
378
|
+
the session moves on to the composition. Whoever reads first after the deadline
|
|
379
|
+
applies it: `wait`, the studio or `status`.
|
|
380
|
+
- **Editing pauses the countdown.** Clicking Pedir alteração (or typing in the
|
|
381
|
+
comment) calls `scenes/pause`: what remains goes to `remaining_ms` and the
|
|
382
|
+
title shows `⏸ 7:32`, stopped. Sending or cancelling the last open change
|
|
383
|
+
calls `scenes/resume`, and the countdown picks up where it stopped
|
|
384
|
+
(`deadline = now + remaining_ms`). A screen that didn't pause (another tab,
|
|
385
|
+
reloaded page) shows **"Retomar contagem"** (Resume countdown). Approving a
|
|
386
|
+
scene pauses nothing.
|
|
387
|
+
- **Who waits.** The session stays in `wait` during the pause. Only if the
|
|
388
|
+
pause runs past the whole deadline (abandoned edit) does it exit with 4 and
|
|
389
|
+
the session stops. Then the studio handles the rest: a per-video alarm
|
|
390
|
+
applies the deadline when the countdown resumes and expires, and resumes the
|
|
391
|
+
stopped session (only for a round finished less than 1 h ago, so as not to
|
|
392
|
+
wake an old session).
|
|
393
|
+
- **End of the round.** With all decided, a session still in `wait` moves on
|
|
394
|
+
by itself; one that stopped is resumed by the studio (also when the decision
|
|
395
|
+
finishes while the turn is closing). With changes, `av-motion` adjusts only
|
|
396
|
+
those scenes and opens another round, with a new deadline; approved scenes
|
|
397
|
+
stay approved.
|
|
398
|
+
|
|
399
|
+
`motion.approval.enabled: false` in the config turns it off: the round is born
|
|
400
|
+
approved. The file, `motion/approval.json`, is described in the header of
|
|
401
|
+
`tools/approval.mjs`; in the chat, the user answers and Claude runs
|
|
402
|
+
`approval.mjs decide`.
|
|
403
|
+
|
|
404
|
+
## Swapping the reference mid-video
|
|
405
|
+
|
|
406
|
+
In stage 2 (or by clicking "Estilo visual: …" at the top of the video), **"Trocar
|
|
407
|
+
estilo visual"** (Swap visual style) opens the same picker as the new video, with
|
|
408
|
+
the current one marked, and an optional comment. The request goes to the
|
|
409
|
+
video's session (resumes the stopped one or opens one), which follows the
|
|
410
|
+
`omnivideo` skill, the "visual style" row: new style in `motion.md`,
|
|
411
|
+
scenes reopened only if the visual changed, and then the composition (base and
|
|
412
|
+
scenes), render and export.
|
|
413
|
+
|
|
414
|
+
The state changes right away, without waiting for Claude: the studio writes
|
|
415
|
+
the request to `motion/reference-swap.json`. Stage 2 shows **refazendo**
|
|
416
|
+
(redoing) until `motion.md` is rewritten with the new reference. Script,
|
|
417
|
+
lines and voice stay. The screen says what will be redone depending on where
|
|
418
|
+
the video is. With the session running, the request is refused: wait for the
|
|
419
|
+
turn or stop the session.
|
|
420
|
+
|
|
421
|
+
## Claude sessions
|
|
422
|
+
|
|
423
|
+
A request made on the screen becomes a `claude -p` session at the omnivideo
|
|
424
|
+
root, with the project's skills and CLAUDE.md. Each turn runs until Claude
|
|
425
|
+
stops (at an abandoned approval, at a blocker, at delivery); the next reply resumes
|
|
426
|
+
**the same session** (`--resume`), with everything it already knows.
|
|
427
|
+
|
|
428
|
+
| starts | prompt | goes until |
|
|
429
|
+
|---|---|---|
|
|
430
|
+
| New video (`/new`) | `/omnivideo` + the folder already created + `source/request.md` | delivery (it waits at the approvals, which resume it) |
|
|
431
|
+
| Continue with Claude (video without a session) | `/omnivideo` resuming from where `status` says | the next stop |
|
|
432
|
+
| New reference | skill `av-reference`: measure, Tipo, curves, GUIDE.md, proof in code compared with the piece | guide written and proof |
|
|
433
|
+
| Ask Claude (reference) | the request, with the reference folder | what was asked |
|
|
434
|
+
| Reply | the text, in the same session | the next stop |
|
|
435
|
+
|
|
436
|
+
**Scenes decided in the studio resume the session** that stopped at scene
|
|
437
|
+
approval (section Scene approval in the studio).
|
|
438
|
+
|
|
439
|
+
**One running session per video or reference.** Replying mid-turn is refused;
|
|
440
|
+
the **"Parar"** (Stop) button ends the whole process group (claude and the
|
|
441
|
+
tools it opened). Whatever was already written stays; "continuar" (continue)
|
|
442
|
+
resumes.
|
|
443
|
+
|
|
444
|
+
**Where it lives.** `.sessions/<id>/session.json` (type, target, turns with
|
|
445
|
+
prompt and pid) and `turn-<n>.jsonl` (each turn's stream-json output). The
|
|
446
|
+
state comes from there: a turn with a `result` finished (stopped or error),
|
|
447
|
+
without `result` and with a live pid it is running, without both it was
|
|
448
|
+
interrupted. claude runs in its own process group and writes straight to the
|
|
449
|
+
file: the studio can go down and come up mid-turn. To open it in the
|
|
450
|
+
terminal: `claude --resume <id>`.
|
|
451
|
+
|
|
452
|
+
**Permissions.** With no terminal, nobody approves anything: whatever asks for
|
|
453
|
+
approval is denied immediately (`--permission-prompts none`). `auto` mode
|
|
454
|
+
denies almost everything in that situation; that's why it is `acceptEdits`
|
|
455
|
+
plus a list in `studio.claude.allow` (config), with `{python}` replaced by
|
|
456
|
+
VoiceStudio's Python. A Bash rule doesn't match a shell variable, so the
|
|
457
|
+
session gets a system prompt asking for the full Python path, one command per
|
|
458
|
+
call and heavy tools in the foreground (the Bash timeout goes up to
|
|
459
|
+
`studio.claude.bash_timeout_ms`). Whatever is denied shows on screen in red;
|
|
460
|
+
add the rule to the config and reply asking it to try again.
|
|
461
|
+
|
|
462
|
+
| `studio.claude` | |
|
|
463
|
+
|---|---|
|
|
464
|
+
| `bin` | the executable (`claude`) |
|
|
465
|
+
| account | not a field here: `agents.accounts` (below) |
|
|
466
|
+
| the model | the session's model and effort come from `models.orchestrator` (video) and `models.reference` in the config, re-read every turn (`--model`, `--effort`). The stages run in a fork with the model from `models.stages`, written into the skills by `tools/models.mjs apply` |
|
|
467
|
+
| `permission_mode` | `auto` (the classifier approves the common and denies the risky; re-read every turn). `acceptEdits` goes back to depending only on the allowlist |
|
|
468
|
+
| `budget_usd` | ceiling per turn (`--max-budget-usd`), `null` for no ceiling |
|
|
469
|
+
| `bash_timeout_ms` | the limit of one command: 2 h, for long voice and render runs |
|
|
470
|
+
| `allow` | allowed tools and Bash rules |
|
|
471
|
+
|
|
472
|
+
### Agent accounts
|
|
473
|
+
|
|
474
|
+
`agents` in the config, the logic in `tools/lib/agents.mjs` (shared with `publish.mjs text` and the
|
|
475
|
+
omnichat classifier), the screen in Configurações → Agente de IA → Contas de agente.
|
|
476
|
+
|
|
477
|
+
| `agents` | |
|
|
478
|
+
|---|---|
|
|
479
|
+
| `accounts` | `[{ id, provider: "claude" \| "codex", dir, label?, enabled? }]`, in rotation order. `dir` is the account's `CLAUDE_CONFIG_DIR` or `CODEX_HOME`, each with its own login (`claude auth login` / `codex login` with the variable set; the screen's "Entrar" runs it). Empty: `~/.claude-personal`. `~/.claude` is refused in code |
|
|
480
|
+
| `codex.model`, `codex.effort` | the model of every Codex session (`-m`, `model_reasoning_effort`); the options come from the account's `models_cache.json` |
|
|
481
|
+
| `codex.sandbox` | `auto` (default): workspace-write with network and the library writable, `approval_policy=on-request` with Codex's `auto_review` reviewer, so what must leave the sandbox (Chrome in the renders) asks and is decided like Claude's auto mode. `full`: no sandbox |
|
|
482
|
+
| `codex.bin` | the CLI; default `codex` on the PATH or the one the ChatGPT app ships |
|
|
483
|
+
|
|
484
|
+
- **Rotation:** a new session goes to the next ready account (on, logged in, not over its limit);
|
|
485
|
+
`.accounts/state.json` keeps the cursor and the limits. A session stays on its account while it
|
|
486
|
+
is free.
|
|
487
|
+
- **Limit:** a turn that ends on the account's limit (Claude: the 429 with the rejected
|
|
488
|
+
`rate_limit_event` and its `resetsAt`; Codex: `turn.failed` with the usage-limit message) sets the
|
|
489
|
+
account aside until the reset (1 h when unknown) and starts a `failover` turn on the next one, at
|
|
490
|
+
most once per account. Claude to Claude copies the transcript (`<dir>/projects/<cwd>/<id>.jsonl`
|
|
491
|
+
and its subagents) and resumes the same conversation; to or from Codex the turn opens a new
|
|
492
|
+
conversation whose prompt carries the first request, the last turns and the instruction to check
|
|
493
|
+
the files before acting (`handoff` in the turn). With no account free, the turn stays on the
|
|
494
|
+
limit message and "Liberar" on the screen puts an account back by hand.
|
|
495
|
+
- **Codex turns:** `codex exec --json` (then `exec resume <thread>`), the thread id read from the
|
|
496
|
+
turn's first event, `CLAUDE.md` as the project doc (`project_doc_fallback_filenames`), the
|
|
497
|
+
studio's context plus the Codex notes as `developer_instructions`, and the skills through
|
|
498
|
+
`.agents/skills`. It reports tokens, not dollars.
|
|
499
|
+
|
|
500
|
+
## References
|
|
501
|
+
|
|
502
|
+
The library (`references.dir` in the config) is each video's design system:
|
|
503
|
+
the motion script picks a reference and takes from it the palette,
|
|
504
|
+
typography, curves and rhythm of every scene. On the screen, each reference
|
|
505
|
+
shows what exists in its folder: guide (`GUIDE.md` or the first `.md`),
|
|
506
|
+
`measurements.json` (summary, palette by area, motion strip), tokens
|
|
507
|
+
(`*tokens*.json`, where the palette comes from when there are no
|
|
508
|
+
measurements), videos, `frames/` and `sheets/`. **"Novo estilo visual"** (New
|
|
509
|
+
visual style) asks for a link and/or photos (nothing else: the name comes from
|
|
510
|
+
the link or the photos) and opens a session that measures and writes the guide. Each reference's toggle adds or removes a
|
|
511
|
+
`.disabled` in the folder: disabled, it is faded and listed separately,
|
|
512
|
+
disappears from the new video picker, and `reference.mjs list` only mentions
|
|
513
|
+
it at the end, as "não use" (don't use). Deleting moves the folder to the
|
|
514
|
+
Trash.
|
|
515
|
+
|
|
516
|
+
## Settings
|
|
517
|
+
|
|
518
|
+
`/settings`, by category. **Voz** (Voice): just the voice carousel (profiles
|
|
519
|
+
in `voice.profiles`), in the ElevenLabs style: the voice in the center becomes
|
|
520
|
+
the **active** one (`voice.default_profile`, the one preselected for a new
|
|
521
|
+
video) or opens for **editing**; the last item adds a voice. Adding or editing
|
|
522
|
+
opens a drawer: the voice's name and its **providers** (Fish Audio, Cartesia,
|
|
523
|
+
VoiceStudio). Each provider is where you enter what it needs: API key (in
|
|
524
|
+
`.env`, masked), Fish's model and fallback, Voice ID (or the VoiceStudio
|
|
525
|
+
profile, by name) and, on a new voice, recording or uploading audio to clone.
|
|
526
|
+
With more than one provider on the voice, the list order is the priority of
|
|
527
|
+
use (the profile's `order`): the 1st is used and the following ones are
|
|
528
|
+
fallbacks. API key and model apply to every voice of the provider and save
|
|
529
|
+
right away; Voice ID and order save on "Gravar voz" (Save voice). The avatar
|
|
530
|
+
is a liquid orb whose colors come from the voice's id; every avatar on the page is drawn by
|
|
531
|
+
`web/lib/orb.ts` (one shared WebGL context, the same shader as the video scenes'
|
|
532
|
+
`VoiceAvatar` component) and reacts to the voice while it plays. **Agente de IA:** the agent accounts (Claude
|
|
533
|
+
and Codex, in rotation order, each with its login, its limit and "Entrar"), Codex's model and
|
|
534
|
+
sandbox, and the Claude models per stage. The keys go
|
|
535
|
+
to `.env` and come back to the screen only masked; the rest goes to
|
|
536
|
+
`omnivideo.config.json`. The Uso (Usage) screen only shows what is chosen.
|
|
537
|
+
|
|
538
|
+
## API
|
|
539
|
+
|
|
540
|
+
Everything on `127.0.0.1:5432`. A request with another `Host` is refused (DNS
|
|
541
|
+
rebinding); POST requires `content-type: application/json` and, if `Origin`
|
|
542
|
+
comes, that it be the studio's own.
|
|
543
|
+
|
|
544
|
+
| route | what |
|
|
545
|
+
|---|---|
|
|
546
|
+
| `GET /api/videos` | a summary per video: `status` (the JSON from `status --json`), the scenes' `approval` (with the expired deadline already applied), cover |
|
|
547
|
+
| `POST /api/videos` | `{ request, deliverables?, subject?, format?, duration?, reference? }` → folder, `content.json`, `source/request.md` and session; returns `slug` and `session`. `deliverables`: `["video"]` (default), `["carousel"]` or both |
|
|
548
|
+
| `POST /api/videos/<slug>/carousel/create` | `{ note? }` → puts the carousel in the deliverables and asks the content's session (from the folder's video, if any) |
|
|
549
|
+
| `POST /api/videos/<slug>/carousel/adjust` | `{ slide?, request, stage }` → a change asked of the session (`av-carousel … ajustar:`) |
|
|
550
|
+
| `POST /api/videos/<slug>/carousel/caption` | `{ caption }` → `carousel/publication.json` |
|
|
551
|
+
| `POST /api/videos/<slug>/carousel/publish` | `{ caption?, republish? }` → `publish.mjs carousel --confirm` (minutes) |
|
|
552
|
+
| `POST /api/videos/<slug>/reference` | `{ reference, note? }` → a request to the video's session to swap the style reference (`""` = Claude picks another); refuses a video without a motion script and the same reference without a note |
|
|
553
|
+
| `POST /api/videos/<slug>/claude` | `{ message }` → resumes the video's latest session, or opens one |
|
|
554
|
+
| `GET /api/sessions` | the sessions, most recent first: state, current turn, what Claude is doing now, final answer, denied permissions, cost |
|
|
555
|
+
| `GET /api/sessions/<id>` | the session with all its turns |
|
|
556
|
+
| `GET /api/sessions/<id>/log` | SSE: the session's readable transcript (`log`) and `end` when the turn stops |
|
|
557
|
+
| `POST /api/sessions/<id>/reply` | `{ message }` → new turn in the same session |
|
|
558
|
+
| `POST /api/sessions/<id>/stop` | ends the running turn |
|
|
559
|
+
| `GET /api/references` · `/api/references/<name>` | the library; one reference with texts, media and where it is used |
|
|
560
|
+
| `POST /api/references` | `{ name, source, note? }` → session that measures and writes the guide |
|
|
561
|
+
| `POST /api/references/<name>/claude` | `{ message }` → a request about a reference |
|
|
562
|
+
| `POST /api/references/<name>/enable` | `{ enabled }` → removes or adds the `.disabled` |
|
|
563
|
+
| `POST /api/references/<name>/delete` | moves the reference folder to the Trash |
|
|
564
|
+
| `GET /ref/<name>/<path>` | a file of a reference (image, video, text) |
|
|
565
|
+
| `GET /api/videos/<slug>` | everything about the video: `scenes.json`, resolved times, texts (scripts and `captions.srt`), media files with mtime, `history` (the kept versions), `runs` (every queue run of this video), `premiere.json`, `sound/plan.json` |
|
|
566
|
+
| `POST /api/videos/<slug>/scenes/decide` | `{ scene, decision: "approve" \| "change", comment? }` → `motion/approval.json`; change requires a comment. A finished round resumes the video's stopped session |
|
|
567
|
+
| `POST /api/videos/<slug>/scenes/approve-all` | the round's pending scenes become approved |
|
|
568
|
+
| `POST /api/videos/<slug>/scenes/pause` | the user opened a change: `mode: "paused"`, what remains goes to `remaining_ms` |
|
|
569
|
+
| `POST /api/videos/<slug>/scenes/resume` | the change was sent or cancelled: the countdown picks up where it stopped |
|
|
570
|
+
| `POST /api/videos/<slug>/voice/regenerate` | `{ line }` → `omnivideo.py voice --regenerate <line>`: another reading of the same text, through the queue, **of that line only** (even with the video's provider changed, the others are not redone); answers right away (only an error from before the queue comes back) |
|
|
571
|
+
| `POST /api/videos/<slug>/voice/generate` | `{}` → `omnivideo.py voice` with no arguments: generates the lines that are missing, changed text or were requested from another provider. It is the “Gerar as N falas com <voz>” (Generate the N lines with <voice>) button of the Voz stage picker, which only appears when the chosen provider differs from that of some already generated line |
|
|
572
|
+
| `POST /api/videos/<slug>/voice/use` | `{ line, version }` → `omnivideo.py voice --use <line> <version>`: the reading kept in `history/voice/<line>/<version>/` becomes current again and the current one goes to the history; refuses a version of another text and while the video's voice is running |
|
|
573
|
+
| `POST /api/videos/<slug>/line/text` | `{ line, text }` → the line's `text` in `scenes.json`; a line with voice gets the new reading right away (`voice --regenerate`). A text that removes the word an `enters_with` points at is not saved: it becomes a request to the video's session (`chatWith`), which saves the line, changes the beat with it and generates the voice |
|
|
574
|
+
| `POST /api/videos/<slug>/publish/text` | `{ redo }` → `publish.mjs text`: title, description, post caption and tags from the script (Claude writes them; without it, they come from the script). Without `redo`, only what is empty. The Publicar stage calls it by itself when the video has no text yet |
|
|
575
|
+
| `POST /api/videos/<slug>/publish` | `{ title, description, caption, privacy, tiktok_privacy, youtube, instagram, tiktok, republish }` → text in `publication.json` and `publish.mjs --confirm` (waits for the upload; one per video) |
|
|
576
|
+
| `POST /api/videos/<slug>/archive` | `{ archive }` (default `true`) → writes or deletes `archived.json` (`{ at }`) in the video folder. Archiving takes it off the work list, deleting nothing; refuses a video with a scheduled post or a task running/queued. Only the screen reads the file: no pipeline stage does |
|
|
577
|
+
| `POST /api/videos/<slug>/thumbnail` | `{ mode: "random" }` or `{ mode: "manual", instruction }` → writes the request to `thumbnail/request.json` (`random` or `manual`) and asks the video's Claude for the three random ones (v1–v3) or the manual version (v4) (skill `av-thumbnail`). `{ mode: "change", version, instruction }` changes an already created version (keeps the previous HTML and PNG in `history/` and asks Claude only for the change, preserving the rest). Refuses while the video's session is running. Generates nothing by itself: only with this request |
|
|
578
|
+
| `POST /api/videos/<slug>/thumbnail/choose` | `{ version: "v1"\|"v2"\|"v3"\|"v4"\|"none" }` → `thumbnail.mjs choose`: the version becomes `thumbnail/thumbnail.png`, the cover that `publish.mjs` sends; `none` removes the selection |
|
|
579
|
+
| `GET /api/publish/accounts` | `publish.mjs status --json`, cached for 5 min (`?refresh` asks again) |
|
|
580
|
+
| `GET /api/queue` | `{ running, waiting, recent }` |
|
|
581
|
+
| `GET /api/queue/<id>/log` | SSE: the log so far and what arrives (`log`), and `end` when the task finishes |
|
|
582
|
+
| `GET /api/services` | VoiceStudio, Premiere, memory |
|
|
583
|
+
| `GET /api/videos/<slug>/captions` | captions with id and scene, resolved position, the format's safe areas, anchors and styles |
|
|
584
|
+
| `GET /api/videos/<slug>/captions/page` | the video's `captions.html` (`?preview=1` marks the safe area and accepts `postMessage({ position, t })`) |
|
|
585
|
+
| `GET /api/videos/<slug>/captions/frame?t=` | JPEG of the motion at second `t` of the video, from the scene's MP4 |
|
|
586
|
+
| `POST /api/videos/<slug>/captions/position` | `{ scope: "video" \| "scene" \| "caption", target?, value }` → `captions` in `scenes.json`; `value: null` removes a scene's or caption's adjustment |
|
|
587
|
+
| `GET /api/videos/<slug>/sound` | sound timeline: total, scenes, lines (video time), sound places (each beat with its instant and each scene change, with the sounds), the bed, the catalog (kit by role and the video's `sfx/`, measured) and the export's base MP4 |
|
|
588
|
+
| `POST /api/videos/<slug>/sound/plan` | `{ places, bed }` (draft) → the `sound.mjs` plan with the URL of each finished WAV under `/sound-draft/<slug>/…`; nothing is written to the video |
|
|
589
|
+
| `POST /api/videos/<slug>/sound` | `{ places, bed }` → `sound` on the beats, `transition.sound` and `bed` in `scenes.json` (validated; defaults are dropped) |
|
|
590
|
+
| `POST /api/videos/<slug>/sound/reassemble` | runs `assemble.mjs` (waits for its turn in the queue) |
|
|
591
|
+
| `GET /kit/<name>` | MP3 from the global kit (`kit/sfx/`), for the waveform |
|
|
592
|
+
| `GET /api/usage` | `tools/usage.py summary --json`: Fish balance, Cartesia quota, usage per video and latest generations (only reads the local log) |
|
|
593
|
+
| `POST /api/usage/refresh` | the same with `--live`: asks the platforms now and writes the reading to `.usage/` |
|
|
594
|
+
| `GET /api/analytics` | `tools/analysis.mjs context` (account, medians at the current reading and at the 24 h and 72 h milestones, also per thousand views, the channel's hour-by-hour `progression` and the `publications` in it, the playbook's `before` range, each Reel with metrics, strategy and hook, the agent) plus the text of the latest channel analysis |
|
|
595
|
+
| `GET /api/analytics/reels/<slug>` | one Reel from the context, with the `hourly` series from metrics.json, the medians and its `analysis.md` |
|
|
596
|
+
| `POST /api/analytics/collect` | `metrics.mjs collect --force`: redoes the current reading of all of them (GET-only on the API) |
|
|
597
|
+
| `POST /api/analytics/agent` | `{ enable }` → installs or uninstalls the metrics agent (launchd, every 4 hours) |
|
|
598
|
+
| `POST /api/analytics/analyze` | `{ slug?, focus? }` → the `av-analysis` skill in the analysis session (resumes the stopped one, or opens one) |
|
|
599
|
+
| `POST /api/analytics/claude` | `{ message }` → replies to the analysis session (or opens one with the question) |
|
|
600
|
+
| `GET /api/settings` | what the Settings screen edits; keys only masked, and the accounts of each Claude folder |
|
|
601
|
+
| `POST /api/settings/key` | `{ name, value }` → `FISH_API_KEY`, `CARTESIA_API_KEY` or `CARTESIA_ADMIN_API_KEY` in `.env` (`""` removes) |
|
|
602
|
+
| `POST /api/settings/default-voice` | `{ provider }` → `fish`, `cartesia` or `voicestudio` moves to the front of `voice.order`; the others stay as fallbacks in the order they were |
|
|
603
|
+
| `POST /api/settings/voice-profile` | `{ id?, name, order?, fish?, cartesia?, voicestudio? }` → creates (without `id`) or edits an item of `voice.profiles`; at least one provider (Fish: 32 hex; Cartesia: UUID; VoiceStudio: profile name); `order` is the priority among them; the first voice created becomes the active one |
|
|
604
|
+
| `POST /api/settings/voice-profile/default` | `{ id }` → `voice.default_profile` (the active voice) |
|
|
605
|
+
| `POST /api/settings/voice-profile/remove` | `{ id }` → removes the profile from `voice.profiles` (if it was the default, the first remaining one takes over) |
|
|
606
|
+
| `POST /api/settings/voice-id` | `{ provider, voice_id }` → `voice.<provider>.voice_id` (Fish: 32 hex; Cartesia: UUID) |
|
|
607
|
+
| `POST /api/settings/fish-model` | `{ model, fallback }` → `voice.fish.model` and `voice.fish.fallback_model` (`s2.1-pro-free` or `s2.1-pro`; `fallback: null` turns it off) |
|
|
608
|
+
| `POST /api/settings/fish-price` | `{ value }` → `usage.fish.usd_per_million_bytes` |
|
|
609
|
+
| `POST /api/settings/models` | `{ orchestrator?, reference?, stages?: { <skill>: { model, effort? } } }` → `models` in the config and `tools/models.mjs apply` on the skills |
|
|
610
|
+
| `POST /api/settings/claude` | `{ config_dir }` → `studio.claude.config_dir` (`null` = `~/.claude`) |
|
|
611
|
+
| `GET /api/events` | SSE: `queue`, `services` and `sessions` with the data, `video` with the slug that changed, `reference` with the name |
|
|
612
|
+
| `GET /media/<slug>/<path>` | a video file, with Range (206) for the players; only media and text, never outside the video folder |
|
|
613
|
+
|
|
614
|
+
## Files
|
|
615
|
+
|
|
616
|
+
```
|
|
617
|
+
studio/studio.ts entry: args, HTML routes, Bun.serve; the rest goes to the API
|
|
618
|
+
studio/api/app.ts the Hono app: local-only guard, every router, errors (AppType)
|
|
619
|
+
studio/api/routes/ one thin router per domain: validate (zod) → service → respond
|
|
620
|
+
studio/api/services/ videos (status, times, media, history), queue (reading
|
|
621
|
+
and log), services,
|
|
622
|
+
approval (motion script scenes), events,
|
|
623
|
+
sessions (claude -p, state, transcript), create (video,
|
|
624
|
+
reference, chat, verdict), references, usage,
|
|
625
|
+
analytics (metrics, collect, agent, Claude analysis),
|
|
626
|
+
sound (sound timeline: data, draft, save), carousel…
|
|
627
|
+
studio/api/test/ bun test: guard, validation, routes on a fixture root
|
|
628
|
+
(OMNIVIDEO_ROOT points the services at another root)
|
|
629
|
+
studio/shared/ zod schemas (shared/schemas) and response types
|
|
630
|
+
(shared/types), used by the API and the web
|
|
631
|
+
studio/web/ React (TanStack Router + Query, typed hono/client in lib/api.ts):
|
|
632
|
+
pages/VideosPage (overview),
|
|
633
|
+
NewVideoPage, VideoPage (stepper, process, result,
|
|
634
|
+
StageDrawer, SoundTimeline, one component per stage),
|
|
635
|
+
components/SessionPanel (session and reply), ReferencesPage,
|
|
636
|
+
components/StageStepper, lib/stages.ts (what each stage is),
|
|
637
|
+
QueuePage, SettingsPage (UsageCategory), AnalyticsPage,
|
|
638
|
+
components/ui/chart.tsx (shadcn charts on Recharts, no Tailwind),
|
|
639
|
+
components/ApprovalNotice (sound and title)
|
|
640
|
+
.sound-draft/ WAVs of the sound timeline draft (generated)
|
|
641
|
+
tools/approval.mjs scene approval: open, wait, decide, deadline
|
|
642
|
+
.sessions/ one folder per Claude session (outside git)
|
|
643
|
+
.usage/events.jsonl usage log of the paid voices (outside git)
|
|
644
|
+
tools/usage.py usage logging and reading (Fish, Cartesia)
|
|
645
|
+
tools/metrics.mjs Reel metrics; tools/analysis.mjs gathers what the Analytics screen shows
|
|
646
|
+
tools/job_queue.py the queue protocol (and the guardian of the Node tools)
|
|
647
|
+
tools/job-queue.mjs the turn for the Node tools
|
|
648
|
+
tools/history.mjs, .py previous versions of the results
|
|
649
|
+
.queue/ tickets, logs and the lock (outside git)
|
|
650
|
+
videos/<slug>/history/ previous versions of each video (outside git)
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
## For later (the architecture already allows it)
|
|
654
|
+
|
|
655
|
+
- **Studio MCP for the chat**, with a tool that waits for the approval made
|
|
656
|
+
on the screen: it means watching `script-approval.json` and
|
|
657
|
+
`motion/approval.json`, which are already where the decisions land.
|
|
658
|
+
- **Actions from the screen** (regenerate a line, fix pronunciation,
|
|
659
|
+
re-render a scene, assemble, export): the studio calls the tool with
|
|
660
|
+
`OMNIVIDEO_ORIGIN=studio`, and it joins the queue by itself, like any other.
|
|
661
|
+
- **Timeline** with waveform and beats: `/api/videos/<slug>` already returns
|
|
662
|
+
the resolved times and the WAVs.
|
|
663
|
+
- **Attaching a file to the request** (PDF, style video) straight into
|
|
664
|
+
`source/`: today it comes in by path, and the POST only accepts JSON.
|
|
665
|
+
- **Granting permission from the screen**: the denied notice would get a
|
|
666
|
+
button that writes the rule to `studio.claude.allow`.
|