videobook 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/LICENSE +21 -0
- package/README.md +133 -2
- package/bin/videobook.js +7 -0
- package/dist/acestep/client.js +203 -0
- package/dist/acestep/config.js +62 -0
- package/dist/acestep/errors.js +8 -0
- package/dist/acestep/process.js +175 -0
- package/dist/alive-captions/analyze.js +117 -0
- package/dist/alive-captions/audio.js +179 -0
- package/dist/alive-captions/frames.js +59 -0
- package/dist/alive-captions/model.js +37 -0
- package/dist/alive-captions/schema.js +49 -0
- package/dist/alive-captions/track.js +9 -0
- package/dist/api-key.js +33 -0
- package/dist/app-state.js +56 -0
- package/dist/app.js +257 -0
- package/dist/archive-b2.js +332 -0
- package/dist/archive-store.js +169 -0
- package/dist/artifact-label.js +36 -0
- package/dist/artifact-refs.js +49 -0
- package/dist/asset-tag-extraction.js +169 -0
- package/dist/asset-tags.js +228 -0
- package/dist/asset-usage.js +23 -0
- package/dist/audio-cue.js +71 -0
- package/dist/audio-encoding.js +32 -0
- package/dist/audio-sync-request.js +1 -0
- package/dist/audio-sync.js +223 -0
- package/dist/audio-trim-range.js +31 -0
- package/dist/book-context.js +31 -0
- package/dist/book-manager.js +374 -0
- package/dist/catalog-gc.js +26 -0
- package/dist/chain-executor.js +815 -0
- package/dist/chain-generation.js +115 -0
- package/dist/chain-plan.js +746 -0
- package/dist/chain-step-review.js +60 -0
- package/dist/character-name.js +25 -0
- package/dist/chat-generation-routing.js +73 -0
- package/dist/chat-generation-settings.js +78 -0
- package/dist/chat-models.js +44 -0
- package/dist/chat-prompt-rewrite.js +67 -0
- package/dist/chat-router.js +628 -0
- package/dist/chat-view-actions.js +49 -0
- package/dist/cli/args.js +192 -0
- package/dist/cli/doctor-command.js +255 -0
- package/dist/cli/entry.js +2 -0
- package/dist/cli/main.js +91 -0
- package/dist/cli/mcp-command.js +82 -0
- package/dist/cli/mcp-relay.js +51 -0
- package/dist/cli/open-browser.js +14 -0
- package/dist/cli/run-command.js +187 -0
- package/dist/cli/serve-command.js +57 -0
- package/dist/cli/stdio-guard.js +8 -0
- package/dist/cold-open-model.js +7 -0
- package/dist/cold-open.js +79 -0
- package/dist/command-reference.js +95 -0
- package/dist/content-store.js +74 -0
- package/dist/cost.js +8 -0
- package/dist/crop-geometry.js +23 -0
- package/dist/dancing-workflow.js +446 -0
- package/dist/data-home.js +44 -0
- package/dist/diagnostics.js +109 -0
- package/dist/dialog-quality.js +34 -0
- package/dist/downloads-dest.js +56 -0
- package/dist/editing-moments.js +194 -0
- package/dist/elevenlabs-audio-extension.js +66 -0
- package/dist/engine.js +54 -0
- package/dist/env-secrets.js +184 -0
- package/dist/ffmpeg-binary.js +12 -0
- package/dist/ffmpeg.js +425 -0
- package/dist/ffprobe-binary.js +55 -0
- package/dist/forget-asset.js +216 -0
- package/dist/frame-utils.js +27 -0
- package/dist/generation-credit-error.js +11 -0
- package/dist/generation-input-mentions.js +117 -0
- package/dist/generation-input-policy.js +68 -0
- package/dist/generation-reference-mentions.js +97 -0
- package/dist/goal-characters.js +49 -0
- package/dist/goal-plan-v3.js +273 -0
- package/dist/goal-plan.js +220 -0
- package/dist/goal-planner-prompt.js +28 -0
- package/dist/goal-prompt-budget.js +4 -0
- package/dist/goal-reference-images.js +25 -0
- package/dist/goal-state.js +261 -0
- package/dist/goal-stills.js +153 -0
- package/dist/goal-store.js +144 -0
- package/dist/goal-styles.js +144 -0
- package/dist/goal-v3-runtime.js +157 -0
- package/dist/guides/character_schema_guide.md +70 -0
- package/dist/guides/gpt_image_2_5_prompting_guide.md +21 -0
- package/dist/guides/grok_imagine_prompting_guide.md +72 -0
- package/dist/guides/image_reproduction_guide.md +84 -0
- package/dist/guides/kling_3_0_prompting_guide.md +136 -0
- package/dist/guides/nano_banana_pro_prompting_guide.md +28 -0
- package/dist/guides/seedance_2_prompting_guide.md +172 -0
- package/dist/http-access.js +49 -0
- package/dist/http-byte-range.js +31 -0
- package/dist/image-compress.js +77 -0
- package/dist/image-edit-default.js +1 -0
- package/dist/image-edit-models.js +41 -0
- package/dist/image-edit-settings.js +11 -0
- package/dist/image-region-edit.js +32 -0
- package/dist/image-subject-map.js +523 -0
- package/dist/image.js +46 -0
- package/dist/index.js +12 -0
- package/dist/job-deadline.js +60 -0
- package/dist/jobs/_alibaba-upload.js +30 -0
- package/dist/jobs/_audio-chunks.js +31 -0
- package/dist/jobs/_download-into-asset.js +15 -0
- package/dist/jobs/_fal-shared.js +22 -0
- package/dist/jobs/_fal-upload.js +35 -0
- package/dist/jobs/_image-edit-adapter.js +213 -0
- package/dist/jobs/_kie-poll.js +119 -0
- package/dist/jobs/_kie-upload.js +32 -0
- package/dist/jobs/_lease.js +163 -0
- package/dist/jobs/_persist-output.js +19 -0
- package/dist/jobs/_storage-retry.js +21 -0
- package/dist/jobs/_transcribe.js +245 -0
- package/dist/jobs/_translate-transcript.js +270 -0
- package/dist/jobs/analyze-alive-captions.js +10 -0
- package/dist/jobs/analyze-editing-moments.js +104 -0
- package/dist/jobs/analyze-jokes.js +143 -0
- package/dist/jobs/analyze-keyword-placement.js +155 -0
- package/dist/jobs/analyze-music.js +14 -0
- package/dist/jobs/analyze-video.js +282 -0
- package/dist/jobs/apply-cuts.js +83 -0
- package/dist/jobs/apply-sfx.js +107 -0
- package/dist/jobs/archive-clip.js +457 -0
- package/dist/jobs/assemble-goal.js +98 -0
- package/dist/jobs/audio-boost.js +71 -0
- package/dist/jobs/audio-mix.js +161 -0
- package/dist/jobs/backfill-asset-tags.js +144 -0
- package/dist/jobs/burn-watermark.js +316 -0
- package/dist/jobs/change-video-speed.js +176 -0
- package/dist/jobs/clean-video-audio.js +207 -0
- package/dist/jobs/cold-open.js +166 -0
- package/dist/jobs/combine-goal.js +108 -0
- package/dist/jobs/complete-provider-task.js +185 -0
- package/dist/jobs/copy-library-asset.js +33 -0
- package/dist/jobs/cost-estimate-assessment.js +113 -0
- package/dist/jobs/crop-image.js +47 -0
- package/dist/jobs/crop-video.js +71 -0
- package/dist/jobs/describe-image.js +181 -0
- package/dist/jobs/detect-scenes.js +33 -0
- package/dist/jobs/doctor-fix.js +100 -0
- package/dist/jobs/download-video.js +426 -0
- package/dist/jobs/enqueue-search-index.js +24 -0
- package/dist/jobs/even-split.js +122 -0
- package/dist/jobs/export-sequence.js +365 -0
- package/dist/jobs/extend-audio.js +229 -0
- package/dist/jobs/extract-audio.js +72 -0
- package/dist/jobs/extract-frame.js +58 -0
- package/dist/jobs/extract-notebook-audio.js +82 -0
- package/dist/jobs/extract-notebook-frame.js +93 -0
- package/dist/jobs/extract-video-clip.js +8 -0
- package/dist/jobs/extract-video-poster.js +55 -0
- package/dist/jobs/extract-waveform.js +25 -0
- package/dist/jobs/finish-goal.js +255 -0
- package/dist/jobs/forget-asset.js +11 -0
- package/dist/jobs/generate-acestep-cover.js +219 -0
- package/dist/jobs/generate-media.js +70 -0
- package/dist/jobs/image-subjects.js +1059 -0
- package/dist/jobs/index.js +18 -0
- package/dist/jobs/inject-first-frame.js +175 -0
- package/dist/jobs/isolate-vocals.js +69 -0
- package/dist/jobs/kie-paygate.js +177 -0
- package/dist/jobs/label-music-sections.js +5 -0
- package/dist/jobs/osg-placement.js +135 -0
- package/dist/jobs/pad-video.js +72 -0
- package/dist/jobs/plan-goal.js +97 -0
- package/dist/jobs/post-record.js +21 -0
- package/dist/jobs/post-to-x.js +21 -0
- package/dist/jobs/prepare-search-reference.js +190 -0
- package/dist/jobs/prepare-temporal-search.js +16 -0
- package/dist/jobs/process-audio.js +67 -0
- package/dist/jobs/process-image.js +58 -0
- package/dist/jobs/process-upload.js +166 -0
- package/dist/jobs/process-video.js +438 -0
- package/dist/jobs/register-all.js +161 -0
- package/dist/jobs/relayout-notebook.js +78 -0
- package/dist/jobs/remove-sound.js +299 -0
- package/dist/jobs/render-project.js +380 -0
- package/dist/jobs/render-qr-outro.js +138 -0
- package/dist/jobs/render-timeline.js +283 -0
- package/dist/jobs/repair-search-indexes.js +42 -0
- package/dist/jobs/replace-video-audio.js +64 -0
- package/dist/jobs/restore-archive-clip.js +53 -0
- package/dist/jobs/reverse-video.js +53 -0
- package/dist/jobs/rewrite-script.js +173 -0
- package/dist/jobs/run-notebook.js +3335 -0
- package/dist/jobs/semantic-index.js +797 -0
- package/dist/jobs/smooth-scene-concat.js +257 -0
- package/dist/jobs/splice-video.js +280 -0
- package/dist/jobs/split-video.js +127 -0
- package/dist/jobs/suggest-cuts.js +149 -0
- package/dist/jobs/suggest-sfx.js +135 -0
- package/dist/jobs/sync-video-to-audio.js +329 -0
- package/dist/jobs/tag-asset.js +266 -0
- package/dist/jobs/task-failure.js +16 -0
- package/dist/jobs/timewarp-video.js +152 -0
- package/dist/jobs/transcribe-video-audio.js +117 -0
- package/dist/jobs/transcribe.js +35 -0
- package/dist/jobs/translate-transcript.js +55 -0
- package/dist/jobs/trim-audio.js +133 -0
- package/dist/jobs/trim-video.js +291 -0
- package/dist/jobs/xai-paygate.js +26 -0
- package/dist/keyword-placement-cache.js +74 -0
- package/dist/lazy-render.js +142 -0
- package/dist/library-browse.js +78 -0
- package/dist/lightning-defaults.js +206 -0
- package/dist/load-env.js +9 -0
- package/dist/local-search-models.js +206 -0
- package/dist/local-upload.js +318 -0
- package/dist/logger.js +69 -0
- package/dist/media/adapters/alibaba.js +20 -0
- package/dist/media/adapters/elevenlabs.js +58 -0
- package/dist/media/adapters/fal.js +45 -0
- package/dist/media/adapters/index.js +26 -0
- package/dist/media/adapters/kie.js +120 -0
- package/dist/media/adapters/pending.js +21 -0
- package/dist/media/adapters/types.js +5 -0
- package/dist/media/adapters/xai.js +18 -0
- package/dist/media/catalog.js +236 -0
- package/dist/media/complete.js +191 -0
- package/dist/media/cost.js +45 -0
- package/dist/media/descriptor.js +157 -0
- package/dist/media/endpoints.js +11 -0
- package/dist/media/frame-options.js +18 -0
- package/dist/media/insert-video-timing.js +17 -0
- package/dist/media/json-schema.js +75 -0
- package/dist/media/legacy-task-types.js +129 -0
- package/dist/media/listing.js +24 -0
- package/dist/media/model-id.js +14 -0
- package/dist/media/preview.js +86 -0
- package/dist/media/prompt-dialects.js +65 -0
- package/dist/media/ref-fields.js +106 -0
- package/dist/media/refs.js +131 -0
- package/dist/media/request.js +188 -0
- package/dist/media/types.js +6 -0
- package/dist/media/wire.js +161 -0
- package/dist/media-library.js +535 -0
- package/dist/media-output-path.js +33 -0
- package/dist/multyculty-workflow.js +757 -0
- package/dist/notebook-action-catalog.js +608 -0
- package/dist/notebook-cell-outputs.js +726 -0
- package/dist/notebook-credit-retry.js +45 -0
- package/dist/notebook-fixtures.js +8 -0
- package/dist/notebook-generation-contract.js +77 -0
- package/dist/notebook-generation-controls.js +145 -0
- package/dist/notebook-goal-layout.js +40 -0
- package/dist/notebook-graph.js +37 -0
- package/dist/notebook-image-edit.js +84 -0
- package/dist/notebook-initial-generation-prompt.js +222 -0
- package/dist/notebook-live-analysis.js +941 -0
- package/dist/notebook-media-ops.js +579 -0
- package/dist/notebook-mutations.js +20 -0
- package/dist/notebook-placement.js +41 -0
- package/dist/notebook-provider-failure.js +87 -0
- package/dist/notebook-published-post.js +26 -0
- package/dist/notebook-reference-resolver.js +227 -0
- package/dist/notebook-relayout.js +270 -0
- package/dist/notebook-run-plan.js +519 -0
- package/dist/notebook-shots.js +162 -0
- package/dist/notebook-workflow.js +856 -0
- package/dist/openai-intelligence.js +99 -0
- package/dist/original-media-file.js +12 -0
- package/dist/osg-assets.js +36 -0
- package/dist/osg-placement-cache.js +64 -0
- package/dist/osg-settings.js +27 -0
- package/dist/paid-tools.js +22 -0
- package/dist/platform-guidance.js +26 -0
- package/dist/posts/dropbox-auth.js +91 -0
- package/dist/posts/dropbox-upload.js +132 -0
- package/dist/posts/icloud-drive.js +134 -0
- package/dist/posts/icloud-status.js +64 -0
- package/dist/posts/post-contract.js +2 -0
- package/dist/posts/post-copy-format.js +43 -0
- package/dist/posts/post-copy.js +193 -0
- package/dist/posts/post-folder.js +18 -0
- package/dist/posts/post-metrics.js +119 -0
- package/dist/posts/post-store.js +340 -0
- package/dist/posts/x-post-target.js +43 -0
- package/dist/preview-payload.js +90 -0
- package/dist/prompt-surfaces.js +22 -0
- package/dist/provider-prompt-transform.js +77 -0
- package/dist/provider-status-link.js +21 -0
- package/dist/provider-task-record.js +78 -0
- package/dist/provider-task-status.js +47 -0
- package/dist/provider-usage.js +294 -0
- package/dist/queue.js +806 -0
- package/dist/reference-video-duration.js +17 -0
- package/dist/remotion/BlurFillVideo.js +51 -0
- package/dist/remotion/KeywordGraphicsOverlay.js +68 -0
- package/dist/remotion/OsgOverlay.js +30 -0
- package/dist/remotion/QrOutroComposition.js +47 -0
- package/dist/remotion/RenderComposition.js +11 -0
- package/dist/remotion/browser.js +59 -0
- package/dist/remotion/constants.js +35 -0
- package/dist/remotion/index.js +66 -0
- package/dist/remotion/keyword-graphics.js +151 -0
- package/dist/remotion/keyword-placement.js +182 -0
- package/dist/remotion/osg-spec.js +198 -0
- package/dist/remotion/public/qr-outro-impact.wav +0 -0
- package/dist/remotion/qr-outro-audio.js +1 -0
- package/dist/remotion/qr-outro-spec.js +211 -0
- package/dist/remotion/shared-bundle.js +37 -0
- package/dist/remotion/subtitles/AliveCaptions.js +83 -0
- package/dist/remotion/subtitles/Highlight.js +58 -0
- package/dist/remotion/subtitles/Impact.js +67 -0
- package/dist/remotion/subtitles/SubtitleOverlay.js +9 -0
- package/dist/remotion/subtitles/alive-layout.js +321 -0
- package/dist/remotion/subtitles/alive-types.js +17 -0
- package/dist/remotion/subtitles/chunker.js +68 -0
- package/dist/remotion/subtitles/segments.js +42 -0
- package/dist/remotion/subtitles/styles.js +4 -0
- package/dist/remotion/subtitles/types.js +1 -0
- package/dist/remotion/util.js +18 -0
- package/dist/remotion-cancellation.js +15 -0
- package/dist/rename-helpers.js +204 -0
- package/dist/rhythm/analyze.js +128 -0
- package/dist/rhythm/contract.js +14 -0
- package/dist/rhythm/draft-model.js +19 -0
- package/dist/rhythm/energy.js +133 -0
- package/dist/rhythm/fft.js +44 -0
- package/dist/rhythm/model.js +71 -0
- package/dist/rhythm/music-analysis.js +124 -0
- package/dist/rhythm/music-draft.js +261 -0
- package/dist/rhythm/onset.js +101 -0
- package/dist/rhythm/run-worker.js +51 -0
- package/dist/rhythm/score.js +47 -0
- package/dist/rhythm/section-labels.js +168 -0
- package/dist/rhythm/sequence-rhythm.js +67 -0
- package/dist/rhythm/synthesize.js +103 -0
- package/dist/rhythm/timing.js +98 -0
- package/dist/rhythm/track.js +320 -0
- package/dist/rhythm/visual-fit.js +4 -0
- package/dist/rhythm/worker.js +25 -0
- package/dist/scene-detect.js +200 -0
- package/dist/scene-transition.js +12 -0
- package/dist/scheduled-posts.js +200 -0
- package/dist/search-index-coverage.js +13 -0
- package/dist/search-index-media.js +71 -0
- package/dist/search-reference-store.js +60 -0
- package/dist/search-scoring.js +17 -0
- package/dist/serve.js +150 -0
- package/dist/server-lock.js +143 -0
- package/dist/server.js +42 -0
- package/dist/srt.js +16 -0
- package/dist/tag-backfill-state.js +121 -0
- package/dist/tag-settings.js +30 -0
- package/dist/tag-vocabulary.js +237 -0
- package/dist/test-queue-cleanup.js +1 -0
- package/dist/time-parse.js +50 -0
- package/dist/timeline-helpers.js +554 -0
- package/dist/timewarp.js +87 -0
- package/dist/tools/_image-inputs.js +9 -0
- package/dist/tools/_media-refs.js +84 -0
- package/dist/tools/_shared.js +59 -0
- package/dist/tools/_suggest-shared.js +279 -0
- package/dist/tools/add-timeline-audio.js +73 -0
- package/dist/tools/alive-captions.js +157 -0
- package/dist/tools/analyze-jokes.js +56 -0
- package/dist/tools/analyze-video.js +66 -0
- package/dist/tools/animate-keywords.js +217 -0
- package/dist/tools/api-status.js +42 -0
- package/dist/tools/application-commands.js +34 -0
- package/dist/tools/apply-cuts.js +112 -0
- package/dist/tools/apply-sfx.js +152 -0
- package/dist/tools/apply-watermark.js +670 -0
- package/dist/tools/archive-clip.js +74 -0
- package/dist/tools/asset-history.js +61 -0
- package/dist/tools/asset-manifest.js +24 -0
- package/dist/tools/asset-tags.js +151 -0
- package/dist/tools/assign-slot.js +97 -0
- package/dist/tools/audio-boost.js +107 -0
- package/dist/tools/audio-mix.js +121 -0
- package/dist/tools/book-usage.js +16 -0
- package/dist/tools/chain.js +136 -0
- package/dist/tools/change-video-speed.js +105 -0
- package/dist/tools/chat-history.js +5 -0
- package/dist/tools/chat.js +848 -0
- package/dist/tools/clean-video-audio.js +97 -0
- package/dist/tools/clear-failed-tasks.js +66 -0
- package/dist/tools/cold-open.js +114 -0
- package/dist/tools/command-plan.js +1680 -0
- package/dist/tools/commands.js +1666 -0
- package/dist/tools/concat-images.js +236 -0
- package/dist/tools/connect-service.js +55 -0
- package/dist/tools/copy-file-to-clipboard.js +58 -0
- package/dist/tools/create-character.js +114 -0
- package/dist/tools/create-qart.js +253 -0
- package/dist/tools/create-qr-outro.js +149 -0
- package/dist/tools/create-qrbtf.js +215 -0
- package/dist/tools/create-watermark.js +97 -0
- package/dist/tools/crop-asset.js +18 -0
- package/dist/tools/crop-image.js +120 -0
- package/dist/tools/crop-video.js +97 -0
- package/dist/tools/delete-asset.js +104 -0
- package/dist/tools/describe-image.js +69 -0
- package/dist/tools/detect-scenes.js +44 -0
- package/dist/tools/doctor.js +126 -0
- package/dist/tools/download-video.js +114 -0
- package/dist/tools/duplicate-asset.js +184 -0
- package/dist/tools/edit-timeline.js +59 -0
- package/dist/tools/editing-moments.js +105 -0
- package/dist/tools/estimate-cost.js +171 -0
- package/dist/tools/even-split.js +295 -0
- package/dist/tools/export-asset-file.js +22 -0
- package/dist/tools/export-asset.js +140 -0
- package/dist/tools/export-sequence.js +250 -0
- package/dist/tools/export-to-downloads.js +54 -0
- package/dist/tools/export-transcript-srt.js +79 -0
- package/dist/tools/extend-audio.js +100 -0
- package/dist/tools/extract-audio.js +174 -0
- package/dist/tools/extract-frame.js +108 -0
- package/dist/tools/extract-notebook-audio.js +173 -0
- package/dist/tools/extract-notebook-frame.js +160 -0
- package/dist/tools/extract-video-clip.js +101 -0
- package/dist/tools/generate-media.js +157 -0
- package/dist/tools/generation-payload.js +48 -0
- package/dist/tools/get-timeline.js +12 -0
- package/dist/tools/get-watermark-config.js +38 -0
- package/dist/tools/goal.js +644 -0
- package/dist/tools/image-subjects.js +711 -0
- package/dist/tools/index.js +323 -0
- package/dist/tools/inject-first-frame.js +154 -0
- package/dist/tools/invert-image.js +70 -0
- package/dist/tools/isolate-vocals.js +66 -0
- package/dist/tools/list-assets.js +98 -0
- package/dist/tools/list-cell-generations.js +225 -0
- package/dist/tools/list-elevenlabs-voices.js +163 -0
- package/dist/tools/log-wrapper.js +29 -0
- package/dist/tools/make-cover-song.js +95 -0
- package/dist/tools/mark-asset-seen.js +23 -0
- package/dist/tools/media-library.js +245 -0
- package/dist/tools/move-timeline-audio.js +35 -0
- package/dist/tools/music-analysis.js +60 -0
- package/dist/tools/music-draft.js +29 -0
- package/dist/tools/pad-video.js +87 -0
- package/dist/tools/post-record.js +83 -0
- package/dist/tools/post.js +168 -0
- package/dist/tools/prompt-history.js +164 -0
- package/dist/tools/provider-key-status.js +248 -0
- package/dist/tools/qrbtf-render.js +849 -0
- package/dist/tools/read-asset-metadata.js +26 -0
- package/dist/tools/read-file-at-commit.js +29 -0
- package/dist/tools/read-transcript.js +41 -0
- package/dist/tools/reconcile-project-work.js +1452 -0
- package/dist/tools/recover-asset.js +28 -0
- package/dist/tools/remove-sound.js +72 -0
- package/dist/tools/remove-timeline-audio.js +34 -0
- package/dist/tools/remove-timeline-slot.js +40 -0
- package/dist/tools/rename-all.js +434 -0
- package/dist/tools/rename-asset.js +34 -0
- package/dist/tools/render-settings.js +47 -0
- package/dist/tools/render-timeline.js +120 -0
- package/dist/tools/render.js +384 -0
- package/dist/tools/reorder-character-images.js +45 -0
- package/dist/tools/replace-video-audio.js +133 -0
- package/dist/tools/restore-archive-clips.js +209 -0
- package/dist/tools/restore-asset-version.js +37 -0
- package/dist/tools/retry-generation.js +54 -0
- package/dist/tools/reverse-video.js +74 -0
- package/dist/tools/rewrite-prompt.js +388 -0
- package/dist/tools/rewrite-script.js +67 -0
- package/dist/tools/schema-from-apicity.js +66 -0
- package/dist/tools/search-archive.js +150 -0
- package/dist/tools/semantic-search.js +1214 -0
- package/dist/tools/sequence-rhythm.js +43 -0
- package/dist/tools/set-character-metadata.js +84 -0
- package/dist/tools/set-env-secret.js +40 -0
- package/dist/tools/set-osg.js +143 -0
- package/dist/tools/show-folder.js +37 -0
- package/dist/tools/smooth-scene-concat.js +122 -0
- package/dist/tools/splice-video.js +149 -0
- package/dist/tools/split-video.js +202 -0
- package/dist/tools/suggest-cuts.js +45 -0
- package/dist/tools/suggest-sfx.js +43 -0
- package/dist/tools/sync-video-to-audio.js +115 -0
- package/dist/tools/tag-backfill.js +177 -0
- package/dist/tools/timewarp-video.js +85 -0
- package/dist/tools/tool-def.js +1 -0
- package/dist/tools/tool-json-schema.js +116 -0
- package/dist/tools/transcribe.js +90 -0
- package/dist/tools/translate-subtitles.js +59 -0
- package/dist/tools/trim-audio.js +139 -0
- package/dist/tools/trim-video.js +168 -0
- package/dist/tools/update-script.js +25 -0
- package/dist/tools/update-timeline-audio.js +40 -0
- package/dist/tools/upload-file.js +41 -0
- package/dist/tools/verify-x-mentions.js +141 -0
- package/dist/tools/video-editing-command-contract.js +291 -0
- package/dist/tools/video-source-reference.js +30 -0
- package/dist/tools/video-time-input.js +18 -0
- package/dist/tools/videobook-domain.js +2006 -0
- package/dist/tools/wait-for-asset-ready.js +143 -0
- package/dist/tools/workspace-actions.js +54 -0
- package/dist/transcript-projection.js +202 -0
- package/dist/transcript-srt.js +33 -0
- package/dist/upload.js +76 -0
- package/dist/usage-providers.js +25 -0
- package/dist/video-analysis-projection.js +46 -0
- package/dist/video-poster.js +33 -0
- package/dist/video-proxy.js +99 -0
- package/dist/vision-regions.js +111 -0
- package/dist/voice-actions.js +90 -0
- package/dist/voice-audio.js +55 -0
- package/dist/voice-config.js +122 -0
- package/dist/voice-continuation.js +54 -0
- package/dist/voice-contract.js +1 -0
- package/dist/voice-http.js +145 -0
- package/dist/voice-reply-progress.js +55 -0
- package/dist/voice-session.js +992 -0
- package/dist/watermark-badge.js +259 -0
- package/dist/watermark-config.js +111 -0
- package/dist/watermark-handle-position.js +55 -0
- package/dist/watermark-platform.js +15 -0
- package/dist/watermark-timing.js +16 -0
- package/dist/workspace-action-catalog.js +315 -0
- package/dist/workspace-action-contract.js +21 -0
- package/dist/workspace-action-groups.js +98 -0
- package/dist/workspace-action-surfaces.js +7 -0
- package/dist/x/account.js +88 -0
- package/dist/x/authenticated-handle.js +30 -0
- package/dist/x/oauth-flow.js +191 -0
- package/dist/x/post-contract.js +1 -0
- package/dist/x/post-metadata.js +44 -0
- package/dist/x/post-to-x.js +456 -0
- package/dist/x/published-post-cell.js +90 -0
- package/dist/x/x-tokens.js +69 -0
- package/dist/zod-slots.js +334 -0
- package/package.json +92 -4
- package/ui/assets/alive-captions-BWb5nLKj.css +1 -0
- package/ui/assets/alive-captions-DPEr1nKM.js +1 -0
- package/ui/assets/confirm-bodies-D8ouHXKy.js +1 -0
- package/ui/assets/confirm-bodies-DrMhaXC6.css +1 -0
- package/ui/assets/fullscreen-B7nw4YbE.js +1 -0
- package/ui/assets/goal-ui-DK-xTCK0.js +2 -0
- package/ui/assets/goal-ui-DXdUM2KF.css +1 -0
- package/ui/assets/index-DzPAnCUj.css +1 -0
- package/ui/assets/index-kS1905s5.js +37 -0
- package/ui/assets/inspector-MlzQbTVo.js +1 -0
- package/ui/assets/jobs-ui-CoinOWx0.js +1 -0
- package/ui/assets/music-video-Cbe80kmQ.js +1 -0
- package/ui/assets/music-video-DPaiKKEq.css +1 -0
- package/ui/assets/ui-Oirr9VOf.css +1 -0
- package/ui/assets/ui-xo_Y1yQg.js +2 -0
- package/ui/assets/vendor-D2rfiZ5j.css +1 -0
- package/ui/assets/vendor-DfTCWgKm.js +19 -0
- package/ui/assets/vendor-flow-BXbeWxW7.js +7 -0
- package/ui/assets/vendor-flow-BnuhLJ6X.css +1 -0
- package/ui/assets/vendor-icons-CGqhCIXV.js +1 -0
- package/ui/assets/vendor-mcp-BTlEJcHV.js +67 -0
- package/ui/assets/vendor-react-Dj7D1fD4.js +9 -0
- package/ui/assets/vendor-remotion-CH1HXrlc.js +95 -0
- package/ui/assets/videobook-Qlp9HQFu.js +20 -0
- package/ui/favicon.svg +8 -0
- package/ui/index.html +126 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { LIGHTNING_VIDEO_MODEL } from "./lightning-defaults.js";
|
|
3
|
+
import { GOAL_PROMPT_BUDGET, goalPromptWords } from "./goal-prompt-budget.js";
|
|
4
|
+
import { GOAL_STYLE_IDS, goalStyleFragment, resolveGoalStyleOptions } from "./goal-styles.js";
|
|
5
|
+
export { compileGoalV3Approval, compileGoalV3Steps, goalMediaRefSchema, goalPlanV3Schema, isGoalPlanV3, refreshGoalPlanV3 } from "./goal-plan-v3.js";
|
|
6
|
+
export { GOAL_PROMPT_BUDGET, goalPromptWords } from "./goal-prompt-budget.js";
|
|
7
|
+
export const GOAL_VIDEO_TOOL = "generate_video";
|
|
8
|
+
export const GOAL_VIDEO_MODEL = LIGHTNING_VIDEO_MODEL;
|
|
9
|
+
const GOAL_IMAGE_MODELS = {
|
|
10
|
+
"nano-banana-2-1": { text: "kie:nano-banana-2-1", edit: "kie:nano-banana-2-1" },
|
|
11
|
+
sunburst: { text: "kie:gpt-image-2-5-sunburst-text-to-image", edit: "kie:gpt-image-2-5-sunburst-image-to-image" },
|
|
12
|
+
flare: { text: "kie:gpt-image-2-5-flare-text-to-image", edit: "kie:gpt-image-2-5-flare-image-to-image" },
|
|
13
|
+
};
|
|
14
|
+
const GOAL_MAX_SHOTS = 8;
|
|
15
|
+
const goalPrompt = (limit) => z.string().trim().min(1).max(limit)
|
|
16
|
+
.regex(/^[^@]*$/u, "Use Reference 1, Reference 2 and plain text in plan prompts; input references are added automatically");
|
|
17
|
+
export const goalStyleSchema = z.object({
|
|
18
|
+
id: z.enum(GOAL_STYLE_IDS), options: z.record(z.string(), z.string()),
|
|
19
|
+
}).strict().superRefine((style, context) => {
|
|
20
|
+
try {
|
|
21
|
+
resolveGoalStyleOptions(style.id, style.options);
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
context.addIssue({ code: "custom", path: ["options"], message: error instanceof Error ? error.message : String(error), fatal: true });
|
|
25
|
+
}
|
|
26
|
+
}).overwrite(style => ({ ...style, options: resolveGoalStyleOptions(style.id, style.options) }));
|
|
27
|
+
export const legacyGoalPlanSchema = z.object({
|
|
28
|
+
title: z.string().trim().min(1).max(120),
|
|
29
|
+
continuity: z.string().trim().min(1).max(3000),
|
|
30
|
+
aspectRatio: z.enum(["16:9", "9:16", "1:1"]),
|
|
31
|
+
shots: z.array(z.object({
|
|
32
|
+
title: z.string().trim().min(1).max(120),
|
|
33
|
+
prompt: z.string().trim().min(1).max(6000),
|
|
34
|
+
durationSeconds: z.number().int().min(4).max(30),
|
|
35
|
+
})).min(1).max(GOAL_MAX_SHOTS),
|
|
36
|
+
}).strict();
|
|
37
|
+
export const goalPlanSchema = legacyGoalPlanSchema.extend({
|
|
38
|
+
version: z.literal(2),
|
|
39
|
+
style: goalStyleSchema.default({ id: "cinematic", options: {} }),
|
|
40
|
+
title: goalPrompt(120),
|
|
41
|
+
continuity: goalPrompt(3000),
|
|
42
|
+
imageModel: z.enum(["sunburst", "flare", "nano-banana-2-1"]).default("sunburst"),
|
|
43
|
+
storyboardLayout: z.enum(["four-panel", "six-panel"]).optional(),
|
|
44
|
+
referenceRoles: z.array(z.enum(["scene", "character", "prop", "reference"])).max(8).optional(),
|
|
45
|
+
imageResolution: z.enum(["1K", "2K"]).default("2K"),
|
|
46
|
+
videoResolution: z.enum(["480p", "720p"]).default("720p"),
|
|
47
|
+
frameContinuity: z.boolean().default(false),
|
|
48
|
+
frame: z.object({ prompt: goalPrompt(6000) }).strict(),
|
|
49
|
+
shots: z.array(z.object({
|
|
50
|
+
title: goalPrompt(120),
|
|
51
|
+
storyboardPrompt: goalPrompt(6000),
|
|
52
|
+
endingPrompt: goalPrompt(6000).nullable().default(null),
|
|
53
|
+
videoPrompt: goalPrompt(6000),
|
|
54
|
+
durationSeconds: z.number().int().min(4).max(30),
|
|
55
|
+
}).strict()).min(1).max(GOAL_MAX_SHOTS),
|
|
56
|
+
}).strict();
|
|
57
|
+
export const goalPlannerResponseSchema = goalPlanSchema.extend({
|
|
58
|
+
storyboardLayout: z.enum(["four-panel", "six-panel"]).default("six-panel"),
|
|
59
|
+
referenceRoles: z.array(z.enum(["scene", "character", "prop", "reference"])).max(8).default([]),
|
|
60
|
+
title: goalPrompt(80),
|
|
61
|
+
continuity: goalPrompt(600),
|
|
62
|
+
frame: z.object({ prompt: goalPrompt(400) }).strict(),
|
|
63
|
+
shots: z.array(goalPlanSchema.shape.shots.element.extend({
|
|
64
|
+
title: goalPrompt(80),
|
|
65
|
+
storyboardPrompt: goalPrompt(500),
|
|
66
|
+
endingPrompt: goalPrompt(250).nullable().default(null),
|
|
67
|
+
videoPrompt: goalPrompt(400),
|
|
68
|
+
})).min(1).max(GOAL_MAX_SHOTS),
|
|
69
|
+
});
|
|
70
|
+
export function goalPromptBudgetIssues(plan) {
|
|
71
|
+
const fields = [
|
|
72
|
+
["Title", plan.title, GOAL_PROMPT_BUDGET.title],
|
|
73
|
+
["Continuity", plan.continuity, GOAL_PROMPT_BUDGET.continuity],
|
|
74
|
+
["Frame", plan.frame.prompt, GOAL_PROMPT_BUDGET.frame],
|
|
75
|
+
...plan.shots.flatMap((shot, index) => [
|
|
76
|
+
[`Shot ${index + 1} title`, shot.title, GOAL_PROMPT_BUDGET.title],
|
|
77
|
+
[`Storyboard ${index + 1}`, shot.storyboardPrompt, plan.storyboardLayout === "six-panel" ? 48 : GOAL_PROMPT_BUDGET.storyboard],
|
|
78
|
+
[`Ending ${index + 1}`, shot.endingPrompt ?? "", GOAL_PROMPT_BUDGET.ending],
|
|
79
|
+
[`Video ${index + 1}`, shot.videoPrompt, GOAL_PROMPT_BUDGET.video],
|
|
80
|
+
]),
|
|
81
|
+
];
|
|
82
|
+
return fields.flatMap(([label, value, limit]) => {
|
|
83
|
+
const words = goalPromptWords(value);
|
|
84
|
+
return words > limit ? [`${label}: ${words} words; limit ${limit}.`] : [];
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
export function goalBriefConstraints(brief) {
|
|
88
|
+
const words = {
|
|
89
|
+
one: 1, two: 2, three: 3, four: 4, five: 5, six: 6, seven: 7, eight: 8,
|
|
90
|
+
nine: 9, ten: 10, eleven: 11, twelve: 12,
|
|
91
|
+
};
|
|
92
|
+
const modifiers = "(?:(?:short|funny|comedic|cinematic|animated|portrait|vertical|square|landscape|horizontal)[ -]+){0,3}";
|
|
93
|
+
const count = new RegExp(`\\b(\\d+|one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve)\\s+${modifiers}(?:\\d+(?:\\.\\d+)?[ -]*(?:s|secs?|seconds?)[ -]*)?${modifiers}(?:clips?|videos?)\\b`, "iu").exec(brief);
|
|
94
|
+
const multiplier = /\b(\d+)\s*[x×]\s*(\d+)\s*(?:s|secs?|seconds?)\b/iu.exec(brief);
|
|
95
|
+
const clipDuration = /\b(\d+(?:\.\d+)?)[ -]*(?:s|secs?|seconds?)[ -]*(?:clips?|shots?|scenes?|each)\b/iu.exec(brief)
|
|
96
|
+
?? /\b(?:clips?|shots?|scenes?)\s+(?:of\s+)?(\d+(?:\.\d+)?)[ -]*(?:s|secs?|seconds?)\b/iu.exec(brief);
|
|
97
|
+
const duration = /\b(\d+(?:\.\d+)?)[ -]*(?:s|secs?|seconds?)\b/iu.exec(brief);
|
|
98
|
+
const shotCount = count ? words[count[1].toLowerCase()] ?? Number(count[1])
|
|
99
|
+
: multiplier ? Number(multiplier[1]) : 1;
|
|
100
|
+
const durationSeconds = multiplier ? Number(multiplier[2]) : clipDuration ? Number(clipDuration[1]) : duration ? Number(duration[1]) : shotCount === 1 ? 20 : 10;
|
|
101
|
+
if (!Number.isInteger(shotCount) || shotCount < 1 || shotCount > GOAL_MAX_SHOTS) {
|
|
102
|
+
throw new Error(`A goal supports 1–${GOAL_MAX_SHOTS} clips. Choose a smaller shot count.`);
|
|
103
|
+
}
|
|
104
|
+
if (!Number.isInteger(durationSeconds) || durationSeconds < 4 || durationSeconds > 30) {
|
|
105
|
+
throw new Error("Each goal clip must be a whole number of seconds from 4 to 30.");
|
|
106
|
+
}
|
|
107
|
+
const aspectRatio = /\b(?:16:9|landscape|horizontal|widescreen)\b/iu.test(brief) ? "16:9"
|
|
108
|
+
: /\b(?:1:1|square)\b/iu.test(brief) ? "1:1" : "9:16";
|
|
109
|
+
return { shotCount, durationSeconds, aspectRatio };
|
|
110
|
+
}
|
|
111
|
+
function styledGoalPrompt(plan, stage, text) {
|
|
112
|
+
const fragment = goalStyleFragment(plan.style, stage);
|
|
113
|
+
return fragment ? `${text}\n\n${fragment}` : text;
|
|
114
|
+
}
|
|
115
|
+
function goalReferenceName(plan, index) {
|
|
116
|
+
return plan.storyboardLayout === "six-panel" ? `Reference ${index + 1} (${plan.referenceRoles?.[index] ?? "reference"})` : `Character ${index + 1}`;
|
|
117
|
+
}
|
|
118
|
+
function goalReferenceLegend(plan, characters, frame = null) {
|
|
119
|
+
return `${[...(frame ? [plan.storyboardLayout === "six-panel" ? "@image1 approved scene: keep clothes, props, place" : "@image1 full frame: keep clothes, props"] : []), ...characters.map((_, index) => `@image${index + (frame ? 2 : 1)} ${goalReferenceName(plan, index)}`)].join("; ")}.`;
|
|
120
|
+
}
|
|
121
|
+
function goalImageStep(id, plan, prompt, references) {
|
|
122
|
+
const models = GOAL_IMAGE_MODELS[plan.imageModel];
|
|
123
|
+
return {
|
|
124
|
+
id,
|
|
125
|
+
tool: "generate_image",
|
|
126
|
+
params: {
|
|
127
|
+
model: references.length > 0 ? models.edit : models.text,
|
|
128
|
+
prompt,
|
|
129
|
+
refs: { image: [...references] },
|
|
130
|
+
options: {
|
|
131
|
+
aspect_ratio: plan.aspectRatio,
|
|
132
|
+
resolution: plan.imageResolution,
|
|
133
|
+
...(plan.imageModel === "nano-banana-2-1" ? { output_format: "png" } : { background: "opaque" }),
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
export function compileFrameSteps(plan, characters) {
|
|
139
|
+
return [goalImageStep("frame", plan, `${styledGoalPrompt(plan, "frame", `${plan.continuity}\n\n${plan.frame.prompt}`)}\n\n${goalReferenceLegend(plan, characters)}`, characters.map(character => character.address))];
|
|
140
|
+
}
|
|
141
|
+
function storyboardLayoutPrompt(plan, index) {
|
|
142
|
+
const six = plan.storyboardLayout === "six-panel";
|
|
143
|
+
const beat = plan.shots[index].durationSeconds / (six ? 6 : 4);
|
|
144
|
+
const time = (value) => six ? Number(value.toFixed(1)) : value;
|
|
145
|
+
const times = Array.from({ length: six ? 6 : 4 }, (_, position) => `${time(position * beat)}–${time((position + 1) * beat)}s`);
|
|
146
|
+
return `${six ? "2 columns by 3 rows, six-panel" : "2x2"} storyboard. Each ${plan.aspectRatio}. Left-right, top-bottom: ${six ? "hook; setup; action; escalation; reaction; payoff" : "setup; action; reaction; ending"}. Below pictures: timing + action/camera notes. Times: ${times.join("; ")}. Pictures text-free.`;
|
|
147
|
+
}
|
|
148
|
+
export function compileStoryboardSteps(plan, characters, frame, indexes = plan.shots.map((_, index) => index)) {
|
|
149
|
+
const frames = [...indexes.map(index => ({ index, ending: false })), ...(plan.frameContinuity ? indexes.map(index => ({ index, ending: true })) : [])];
|
|
150
|
+
return frames.map(({ index, ending }) => goalImageStep(`storyboard${index + 1 + (ending ? plan.shots.length : 0)}`, plan, `${styledGoalPrompt(plan, ending ? "ending" : "storyboard", ending ? `Ending frame. ${goalEndingPrompt(plan, index)}` : `${storyboardLayoutPrompt(plan, index)}\n\n${goalStillPrompt(plan, "storyboards", index)}`)}\n\n${goalReferenceLegend(plan, characters, "full")}`, [frame, ...characters.map(character => character.address)]));
|
|
151
|
+
}
|
|
152
|
+
function goalEndingPrompt(plan, index) {
|
|
153
|
+
const shot = plan.shots[index];
|
|
154
|
+
return shot.endingPrompt ?? `Final moment: ${shot.videoPrompt}`;
|
|
155
|
+
}
|
|
156
|
+
export function goalStillPrompt(plan, stage, index) {
|
|
157
|
+
if (stage === "frame" && index === 0)
|
|
158
|
+
return plan.frame.prompt;
|
|
159
|
+
if (stage === "storyboards" && index >= 0 && index < plan.shots.length) {
|
|
160
|
+
const shot = plan.shots[index];
|
|
161
|
+
return `Shot ${index + 1}: ${shot.title} (${shot.durationSeconds}s). ${shot.storyboardPrompt}${plan.storyboardLayout === "six-panel" ? "" : `\nAction: ${shot.videoPrompt}`}`;
|
|
162
|
+
}
|
|
163
|
+
if (stage === "storyboards" && plan.frameContinuity && index >= plan.shots.length && index < plan.shots.length * 2)
|
|
164
|
+
return goalEndingPrompt(plan, index - plan.shots.length);
|
|
165
|
+
throw new Error("There is no still at this stage and index");
|
|
166
|
+
}
|
|
167
|
+
export function compileReviseStillStep(plan, characters, stage, index, current, feedback, frame) {
|
|
168
|
+
const basePrompt = goalStillPrompt(plan, stage, index);
|
|
169
|
+
const storyboard = stage === "storyboards" && index < plan.shots.length;
|
|
170
|
+
if (stage === "storyboards" && !frame)
|
|
171
|
+
throw new Error("Choose a full frame before revising storyboards");
|
|
172
|
+
const references = ["@image1: previous version", ...(frame ? ["@image2: full frame"] : []),
|
|
173
|
+
...characters.map((_, position) => `@image${position + (frame ? 3 : 2)}: ${goalReferenceName(plan, position)}`)];
|
|
174
|
+
return goalImageStep(stage === "frame" ? "frame" : `storyboard${index + 1}`, plan, `${styledGoalPrompt(plan, "revise", `${storyboard ? `${storyboardLayoutPrompt(plan, index)}\n\n` : ""}${basePrompt}\n\nChange @image1: ${feedback.replace(/[.!?]+$/u, "")}. Keep rest.${storyboard ? plan.storyboardLayout === "six-panel" ? " Six distinct moments." : " Four distinct moments." : ""}`)}\n\n${references.join("; ")}.`, [current, ...(frame ? [frame] : []), ...characters.map(character => character.address)]);
|
|
175
|
+
}
|
|
176
|
+
function goalShotParams(plan, index, storyboard, ending, options) {
|
|
177
|
+
const shot = plan.shots[index];
|
|
178
|
+
const fragment = goalStyleFragment(plan.style, "video");
|
|
179
|
+
const six = plan.storyboardLayout === "six-panel";
|
|
180
|
+
const references = [storyboard, ...(ending ? [ending] : []), ...(six && options.frame ? [options.frame] : []), ...(six ? (options.characters ?? []).map(character => character.address) : [])];
|
|
181
|
+
const sceneSlot = ending ? 3 : 2;
|
|
182
|
+
const legend = six ? [options.frame ? `@image${sceneSlot} approved scene` : "", ...(options.characters ?? []).map((_, position) => `@image${sceneSlot + (options.frame ? 1 : 0) + position} ${goalReferenceName(plan, position)}`)].filter(Boolean).join("; ") : "";
|
|
183
|
+
return {
|
|
184
|
+
model: GOAL_VIDEO_MODEL,
|
|
185
|
+
prompt: `${shot.videoPrompt}${options.feedback ? `\nRevision: ${options.feedback}` : ""}\n\n@image1 storyboard. ${six ? "Six beats in reading order, follow its timestamps" : `Four beats in order, ${shot.durationSeconds / 4}s each`}. Same people, place. Full-screen.${fragment ? ` ${fragment}` : ""}${ending ? " @image2 ending pose." : ""}${legend ? ` ${legend}.` : ""} No grid, borders, notes, text. No music.`,
|
|
186
|
+
refs: { image: references },
|
|
187
|
+
mode: "r2v",
|
|
188
|
+
options: {
|
|
189
|
+
duration: shot.durationSeconds,
|
|
190
|
+
aspect_ratio: plan.aspectRatio,
|
|
191
|
+
resolution: plan.videoResolution,
|
|
192
|
+
generate_audio: true,
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
export function compileVideoSteps(goalId, plan, storyboards, endings = [], options = {}) {
|
|
197
|
+
if (storyboards.length !== plan.shots.length)
|
|
198
|
+
throw new Error("Approve a storyboard for every shot before generating videos");
|
|
199
|
+
if (plan.frameContinuity && endings.length !== plan.shots.length)
|
|
200
|
+
throw new Error("Approve an ending frame for every shot before generating videos");
|
|
201
|
+
const steps = [
|
|
202
|
+
...(options.indexes ?? plan.shots.map((_, index) => index)).map(index => ({
|
|
203
|
+
id: `shot${index + 1}`,
|
|
204
|
+
tool: GOAL_VIDEO_TOOL,
|
|
205
|
+
params: goalShotParams(plan, index, storyboards[index], plan.frameContinuity ? endings[index] : undefined, options),
|
|
206
|
+
})),
|
|
207
|
+
];
|
|
208
|
+
if (plan.shots.length > 1 && options.assemble !== false)
|
|
209
|
+
steps.push({
|
|
210
|
+
id: "assemble",
|
|
211
|
+
tool: "assemble_goal",
|
|
212
|
+
params: {
|
|
213
|
+
goal_id: goalId,
|
|
214
|
+
artifact_ids: plan.shots.map((_, index) => ({
|
|
215
|
+
$ref: { step_id: `shot${index + 1}`, output: "asset_id" },
|
|
216
|
+
})),
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
return steps;
|
|
220
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { GOAL_PROMPT_BUDGET, goalBriefConstraints } from "./goal-plan.js";
|
|
2
|
+
import { goalStyle, goalStyleSummary, resolveGoalStyleOptions } from "./goal-styles.js";
|
|
3
|
+
const cinematicInstructions = `Plan video. Return version 2 JSON. Barebones prompts: short noun/verb fragments, present tense. No filler, prose, jargon lists or stacked adjectives. Name inputs Reference 1, Reference 2. Infer referenceRoles in input order: scene, character, prop or reference; use actual pixels and the brief. Continuity: identity, props, place, style once. Frame: the edited scene with the requested characters and props. storyboardLayout: six-panel. Storyboard: six semicolon-separated action/camera notes, hook/setup/action/escalation/reaction/payoff; 2 columns by 3 rows with timestamps and shot notes below pictures. Let the storyboard guide the video. videoPrompt: only essential motion and sound; do not retell the panels. Sound: ambience or speech. No music. Opening, escalation, payoff. Word ceilings, not targets: title ${GOAL_PROMPT_BUDGET.title}, continuity ${GOAL_PROMPT_BUDGET.continuity}, frame ${GOAL_PROMPT_BUDGET.frame}, storyboard 48, ending ${GOAL_PROMPT_BUDGET.ending}, video ${GOAL_PROMPT_BUDGET.video}. Match shot count, seconds and aspect ratio. Portrait default; centre subjects, leave headroom. Nano Banana 2.1, 2K, Seedance 2.5 720p unless specified. One continuous 20-second video by default; create multiple clips only when explicitly requested. frameContinuity false; endingPrompt null. No @tokens, tools, invented assets or continuity promises.`;
|
|
4
|
+
export function goalPlannerInstructions(selection, constraints) {
|
|
5
|
+
const style = goalStyle(selection.id);
|
|
6
|
+
if (!style.planner)
|
|
7
|
+
return cinematicInstructions;
|
|
8
|
+
const options = resolveGoalStyleOptions(selection.id, selection.options);
|
|
9
|
+
const beats = style.options.find(option => option.key === "format")?.choices.find(choice => choice.value === options.format)?.beats;
|
|
10
|
+
return `${cinematicInstructions}\n\n${style.planner} Selected style: ${goalStyleSummary(selection)}.${beats ? ` Content arc: ${beats}; expand to six beats.` : ""} Use ${constraints.aspectRatio} throughout.`;
|
|
11
|
+
}
|
|
12
|
+
export function constrainGoalPlannerPlan(plan, brief, selection) {
|
|
13
|
+
const constraints = goalBriefConstraints(brief);
|
|
14
|
+
if (plan.shots.length !== constraints.shotCount)
|
|
15
|
+
throw new Error("The planner returned a different shot count. Try planning again.");
|
|
16
|
+
const defaults = goalStyle(selection.id).defaults;
|
|
17
|
+
const imageModel = /\b(flare|sunburst)\b/iu.exec(brief)?.[1]?.toLowerCase();
|
|
18
|
+
const imageResolution = /\b(1k|2k)\b/iu.exec(brief)?.[1]?.toUpperCase();
|
|
19
|
+
const videoResolution = /\b(480p|720p)\b/iu.exec(brief)?.[1]?.toLowerCase();
|
|
20
|
+
return {
|
|
21
|
+
...plan, style: { id: selection.id, options: resolveGoalStyleOptions(selection.id, selection.options) },
|
|
22
|
+
aspectRatio: constraints.aspectRatio, frameContinuity: false, storyboardLayout: "six-panel",
|
|
23
|
+
imageModel: imageModel ?? "nano-banana-2-1",
|
|
24
|
+
imageResolution: imageResolution ?? defaults.imageResolution ?? "2K",
|
|
25
|
+
videoResolution: videoResolution ?? defaults.videoResolution ?? "720p",
|
|
26
|
+
shots: plan.shots.map(shot => ({ ...shot, durationSeconds: constraints.durationSeconds, endingPrompt: null })),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { findPrimaryMediaFile } from "videobook-engine";
|
|
2
|
+
import { mimeForImageFilename } from "./jobs/describe-image.js";
|
|
3
|
+
export async function goalPlannerImages(engine, references) {
|
|
4
|
+
return Promise.all(references.map(async (reference, index) => {
|
|
5
|
+
const manifest = await engine.files.manifest(reference.artifactId);
|
|
6
|
+
const file = manifest.ok ? findPrimaryMediaFile(manifest.value.files, "image") : null;
|
|
7
|
+
if (!file)
|
|
8
|
+
throw new Error(`Reference ${index + 1} has no readable image`);
|
|
9
|
+
const bytes = await engine.files.read(reference.artifactId, file.name);
|
|
10
|
+
if (!bytes.ok)
|
|
11
|
+
throw new Error(bytes.error.message);
|
|
12
|
+
return [
|
|
13
|
+
{ type: "input_text", text: `Reference ${index + 1}: ${reference.label}` },
|
|
14
|
+
{ type: "input_image", image_url: `data:${mimeForImageFilename(file.name)};base64,${bytes.value.toString("base64")}`, detail: "high" },
|
|
15
|
+
];
|
|
16
|
+
})).then(images => images.flat());
|
|
17
|
+
}
|
|
18
|
+
export async function goalReferenceHashes(engine, artifactIds) {
|
|
19
|
+
return Promise.all(artifactIds.map(async (artifactId) => {
|
|
20
|
+
const artifact = engine.artifacts.get(artifactId);
|
|
21
|
+
const manifest = await engine.files.manifest(artifactId);
|
|
22
|
+
const file = artifact.ok && manifest.ok ? findPrimaryMediaFile(manifest.value.files, artifact.value.kind) : null;
|
|
23
|
+
return { artifactId, objectHash: file?.objectHash ?? null };
|
|
24
|
+
}));
|
|
25
|
+
}
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { notebookGridTag } from "videobook-engine/notebook-grid";
|
|
2
|
+
import { cancelChainRun, getChainRun } from "./chain-executor.js";
|
|
3
|
+
import { coldOpenSource } from "./cold-open.js";
|
|
4
|
+
import { emptyGoalEstimates, findGoalCell, goalContent, requireGoalContent, saveGoal, writeGoalContent } from "./goal-store.js";
|
|
5
|
+
import { GOAL_VIDEO_TOOL, refreshGoalPlanV3 } from "./goal-plan.js";
|
|
6
|
+
import { suggestedColdOpen } from "./lightning-defaults.js";
|
|
7
|
+
import { mutateNotebook } from "./notebook-mutations.js";
|
|
8
|
+
import { goalStageAttempts, goalStillStaleReason } from "./goal-stills.js";
|
|
9
|
+
export const LEGACY_GOAL_MESSAGE = "Goal mode now starts from character images. Start a new /goal.";
|
|
10
|
+
export function activeGoalStage(goal) {
|
|
11
|
+
return goal.stage === "assembly" ? "videos" : goal.stage;
|
|
12
|
+
}
|
|
13
|
+
function outputString(outputs, key) {
|
|
14
|
+
const value = outputs?.[key];
|
|
15
|
+
return typeof value === "string" ? value : null;
|
|
16
|
+
}
|
|
17
|
+
function producerCell(document, chainId, step) {
|
|
18
|
+
const tagged = document.cells.filter(cell => cell.inputs?.chainRunId === `${chainId}:${step.id}`).sort((a, b) => b.id.localeCompare(a.id))[0];
|
|
19
|
+
const outputId = outputString(step.outputs, "cell_id");
|
|
20
|
+
const edge = document.edges.find(candidate => candidate.target === outputId && candidate.targetInput === "media");
|
|
21
|
+
return tagged ?? document.cells.find(cell => cell.id === edge?.source);
|
|
22
|
+
}
|
|
23
|
+
function goalChainSteps(engine, document, chainId) {
|
|
24
|
+
const chain = chainId ? getChainRun(engine, chainId) : null;
|
|
25
|
+
return chain?.steps.map(step => {
|
|
26
|
+
const producer = producerCell(document, chain.chainId, step);
|
|
27
|
+
return {
|
|
28
|
+
id: step.id, chainId: chain.chainId, status: step.status, error: step.error,
|
|
29
|
+
artifactId: outputString(step.outputs, "asset_id") ?? outputString(step.outputs, "img_asset_id") ?? outputString(step.outputs, "vid_asset_id"),
|
|
30
|
+
cellId: producer?.id ?? null,
|
|
31
|
+
address: producer ? notebookGridTag(producer.slot) : outputString(step.outputs, "address"),
|
|
32
|
+
outputCellId: outputString(step.outputs, "cell_id"), startedAt: step.startedAt ?? null,
|
|
33
|
+
};
|
|
34
|
+
}) ?? [];
|
|
35
|
+
}
|
|
36
|
+
function stageOutputIndex(stepId) {
|
|
37
|
+
if (stepId === "frame" || stepId === "scene" || stepId === "video")
|
|
38
|
+
return 0;
|
|
39
|
+
return Number(/\d+$/u.exec(stepId)?.[0]) - 1;
|
|
40
|
+
}
|
|
41
|
+
function completedStageOutputs(engine, goal, document) {
|
|
42
|
+
return goalChainSteps(engine, document, goal.chainId).flatMap(step => {
|
|
43
|
+
if (step.status !== "completed" || !step.artifactId || step.id === "assemble")
|
|
44
|
+
return [];
|
|
45
|
+
const index = stageOutputIndex(step.id);
|
|
46
|
+
if (!Number.isInteger(index) || index < 0)
|
|
47
|
+
return [];
|
|
48
|
+
const cell = document.cells.find(candidate => candidate.id === step.outputCellId);
|
|
49
|
+
return [{ index, artifactId: step.artifactId, cellId: step.outputCellId, producerCellId: step.cellId, address: cell ? notebookGridTag(cell.slot) : null }];
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
function allGoalChainSteps(engine, document, chainId) {
|
|
53
|
+
return goalChainSteps(engine, document, chainId).flatMap(step => {
|
|
54
|
+
const producers = document.cells.filter(cell => cell.inputs?.chainRunId === `${chainId}:${step.id}`);
|
|
55
|
+
if (producers.length === 0)
|
|
56
|
+
return [step];
|
|
57
|
+
return producers.map(producer => {
|
|
58
|
+
const outputCellId = document.edges.find(edge => edge.source === producer.id && edge.targetInput === "media")?.target ?? null;
|
|
59
|
+
return { ...step, cellId: producer.id, outputCellId, address: notebookGridTag(producer.slot) };
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
export async function writeGoalCombinationState(engine, goal, error) {
|
|
64
|
+
if (!goal.combined)
|
|
65
|
+
return goal;
|
|
66
|
+
const updated = await mutateNotebook(engine, goal.notebookId, "goal_combine_complete", document => ({
|
|
67
|
+
...document, cells: document.cells.map(cell => cell.id === goal.combined?.cellId ? { ...cell, inputs: { ...cell.inputs, state: error ? "error" : "completed" } } : cell),
|
|
68
|
+
}));
|
|
69
|
+
if (!updated.ok)
|
|
70
|
+
throw new Error(updated.error.message);
|
|
71
|
+
return saveGoal(engine, { ...goal, combined: { ...goal.combined, status: error ? "failed" : "completed", error } });
|
|
72
|
+
}
|
|
73
|
+
export function reconcileFinish(engine, goal) {
|
|
74
|
+
const finish = goal.finish;
|
|
75
|
+
if (goal.planVersion !== 3 || !finish || finish.status !== "running")
|
|
76
|
+
return goal;
|
|
77
|
+
const job = finish.jobId ? engine.jobs.queue.get(finish.jobId) : null;
|
|
78
|
+
if (job && ["queued", "running", "completing"].includes(job.state))
|
|
79
|
+
return goal;
|
|
80
|
+
if (job?.state === "done") {
|
|
81
|
+
const result = job.result && typeof job.result === "object" ? job.result : null;
|
|
82
|
+
const finalArtifactId = finish.finalArtifactId ?? (typeof result?.finalArtifactId === "string" && result.finalArtifactId ? result.finalArtifactId : null);
|
|
83
|
+
if (finalArtifactId && engine.artifacts.get(finalArtifactId).ok) {
|
|
84
|
+
const coldOpenArtifactId = finish.coldOpenArtifactId ?? (typeof result?.coldOpenArtifactId === "string" ? result.coldOpenArtifactId : null);
|
|
85
|
+
return saveGoal(engine, { ...goal, status: "completed", error: null, finish: { ...finish, status: "completed", finalArtifactId, coldOpenArtifactId, error: null } });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return saveGoal(engine, { ...goal, status: "review", finish: { ...finish, status: "failed", error: job?.error?.message ?? "Finishing was interrupted. Retry the cold open." } });
|
|
89
|
+
}
|
|
90
|
+
async function coldOpenReview(engine, goal) {
|
|
91
|
+
if (goal.planVersion !== 3 || !goal.finish?.sourceArtifactId)
|
|
92
|
+
return null;
|
|
93
|
+
try {
|
|
94
|
+
const source = await coldOpenSource(engine, goal.finish.sourceArtifactId, goal.finish.sourceObjectHash ?? undefined);
|
|
95
|
+
const approved = goal.finish.status !== "review" && goal.finish.startSeconds != null && goal.finish.endSeconds != null;
|
|
96
|
+
const range = approved
|
|
97
|
+
? { startSeconds: goal.finish.startSeconds, endSeconds: goal.finish.endSeconds }
|
|
98
|
+
: suggestedColdOpen(source.durationSeconds);
|
|
99
|
+
return { ...range, durationSeconds: source.durationSeconds, sourceObjectHash: source.file.objectHash, analysisUsd: 0 };
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
export async function reconcileGoal(engine, goal, document) {
|
|
106
|
+
if (goal.planVersion === 1) {
|
|
107
|
+
if (!["planning", "review", "running"].includes(goal.status))
|
|
108
|
+
return goal;
|
|
109
|
+
if (goal.chainId)
|
|
110
|
+
cancelChainRun(engine, goal.chainId);
|
|
111
|
+
if (goal.planningJobId)
|
|
112
|
+
await engine.jobs.queue.abort(goal.planningJobId, LEGACY_GOAL_MESSAGE);
|
|
113
|
+
return saveGoal(engine, { ...goal, status: "failed", error: LEGACY_GOAL_MESSAGE });
|
|
114
|
+
}
|
|
115
|
+
if (goal.combined?.status === "running") {
|
|
116
|
+
const job = goal.combined.jobId ? engine.jobs.queue.get(goal.combined.jobId) : null;
|
|
117
|
+
if (!job || ["failed", "aborted"].includes(job.state))
|
|
118
|
+
goal = await writeGoalCombinationState(engine, goal, job?.error?.message ?? "Combining was interrupted. Retry the combined video.");
|
|
119
|
+
}
|
|
120
|
+
if (["completed", "cancelled", "review"].includes(goal.status))
|
|
121
|
+
return goal;
|
|
122
|
+
if (goal.status === "planning") {
|
|
123
|
+
const job = goal.planningJobId ? engine.jobs.queue.get(goal.planningJobId) : null;
|
|
124
|
+
return job && ["failed", "aborted"].includes(job.state)
|
|
125
|
+
? saveGoal(engine, { ...goal, status: "failed", error: job.error?.message ?? "Planning was interrupted" }) : goal;
|
|
126
|
+
}
|
|
127
|
+
const stage = activeGoalStage(goal);
|
|
128
|
+
const run = goal.stages[stage];
|
|
129
|
+
const chain = run ? getChainRun(engine, run.chainId) : null;
|
|
130
|
+
if (!run || !chain)
|
|
131
|
+
return goal;
|
|
132
|
+
const merged = new Map(run.outputs.map(output => [output.index, output]));
|
|
133
|
+
for (const output of completedStageOutputs(engine, goal, document))
|
|
134
|
+
merged.set(output.index, output);
|
|
135
|
+
const outputs = [...merged.values()].sort((a, b) => a.index - b.index);
|
|
136
|
+
const attempts = run.attempts?.map(attempt => {
|
|
137
|
+
if (attempt.artifactId)
|
|
138
|
+
return attempt;
|
|
139
|
+
const step = goalChainSteps(engine, document, attempt.chainId).find(candidate => candidate.id === attempt.stepId);
|
|
140
|
+
return step?.status === "completed" && step.artifactId ? { ...attempt, artifactId: step.artifactId, cellId: step.outputCellId } : attempt;
|
|
141
|
+
});
|
|
142
|
+
let next = { ...goal, stages: { ...goal.stages, [stage]: { ...run, outputs, ...(attempts ? { attempts } : {}) } } };
|
|
143
|
+
const assembled = stage === "videos" && goal.assembly?.artifactIds && JSON.stringify(goal.assembly.artifactIds) === JSON.stringify(outputs.map(output => output.artifactId));
|
|
144
|
+
if (chain.status === "completed" || assembled && !goal.resumeStage) {
|
|
145
|
+
const content = requireGoalContent(engine, goal);
|
|
146
|
+
if (goal.planVersion === 3) {
|
|
147
|
+
const cleared = { resumeStage: null, resumeStatus: null, resumeOutputs: null, resumeFinish: null };
|
|
148
|
+
if (outputs.length !== 1)
|
|
149
|
+
next = { ...next, status: "failed", error: "The stage finished without all of its media outputs. Retry the stage.", ...cleared };
|
|
150
|
+
else if (stage === "videos") {
|
|
151
|
+
next = {
|
|
152
|
+
...next, status: "review", error: null, ...cleared,
|
|
153
|
+
finish: { status: "review", sourceArtifactId: outputs[0].artifactId, finalArtifactId: null, error: null },
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
const previousScene = run.outputs[0]?.artifactId ?? null;
|
|
158
|
+
await writeGoalContent(engine, goal, { ...content, revision: content.revision + 1 });
|
|
159
|
+
const sceneChanged = previousScene !== null && previousScene !== outputs[0].artifactId;
|
|
160
|
+
const finish = sceneChanged ? null : goal.finish ?? goal.resumeFinish ?? null;
|
|
161
|
+
const keepCompleted = finish?.status === "completed" && Boolean(finish.finalArtifactId);
|
|
162
|
+
next = {
|
|
163
|
+
...next, stage: goal.resumeStage ?? "videos", status: keepCompleted ? "completed" : "review", error: null, ...cleared,
|
|
164
|
+
finish,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
const stagedPlan = content.plan && "shots" in content.plan ? content.plan : null;
|
|
170
|
+
const shotCount = stagedPlan?.shots.length ?? 0;
|
|
171
|
+
const expected = stage === "frame" ? 1 : stage === "storyboards" && stagedPlan && "frameContinuity" in stagedPlan && stagedPlan.frameContinuity ? 2 * shotCount : shotCount;
|
|
172
|
+
if (outputs.length !== expected)
|
|
173
|
+
next = { ...next, status: "failed", error: "The stage finished without all of its media outputs. Retry the stage." };
|
|
174
|
+
else if (stage === "videos") {
|
|
175
|
+
next = { ...next, status: goal.assembly || expected === 1 || goal.resumeStage ? "completed" : "failed", error: goal.assembly || expected === 1 || goal.resumeStage ? null : "The finished clips still need to be assembled" };
|
|
176
|
+
if (goal.combined && JSON.stringify(run.outputs.map(output => output.artifactId)) !== JSON.stringify(outputs.map(output => output.artifactId)))
|
|
177
|
+
next.combined = { ...goal.combined, status: "failed", error: "Video selection changed. Assemble the selected clips before combining again." };
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
await writeGoalContent(engine, goal, { ...content, revision: content.revision + 1 });
|
|
181
|
+
next = { ...next, stage: goal.resumeStage ?? (stage === "frame" ? "storyboards" : "videos"), status: goal.resumeStatus ?? "review", error: null };
|
|
182
|
+
}
|
|
183
|
+
next = { ...next, resumeStage: null, resumeStatus: null, resumeOutputs: null };
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
else if (["failed", "interrupted", "cancelled"].includes(chain.status)) {
|
|
187
|
+
next = { ...next, status: chain.status, error: chain.error,
|
|
188
|
+
...(goal.resumeOutputs ? { stages: { ...next.stages, [stage]: { ...next.stages[stage], outputs: goal.resumeOutputs } } } : {}),
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
next = { ...next, stage: chain.steps.some(step => step.id === "assemble" && step.status === "running") ? "assembly" : stage, status: "running", error: null };
|
|
193
|
+
}
|
|
194
|
+
return JSON.stringify(next) === JSON.stringify(goal) ? goal : saveGoal(engine, next);
|
|
195
|
+
}
|
|
196
|
+
export async function publicGoal(engine, goal, document) {
|
|
197
|
+
const cell = findGoalCell(document, goal);
|
|
198
|
+
const content = cell ? goalContent(cell) : { brief: "", characters: [], revision: 0, plan: null, estimate: emptyGoalEstimates() };
|
|
199
|
+
const stages = Object.fromEntries(await Promise.all(Object.entries(goal.stages).map(async ([stage, run]) => [stage, {
|
|
200
|
+
...run,
|
|
201
|
+
...(stage === "videos" ? { attempts: goalStageAttempts(engine, goal, "videos") } : {}),
|
|
202
|
+
outputs: await Promise.all(run.outputs.map(async (output) => {
|
|
203
|
+
const outputCell = document.cells.find(candidate => candidate.id === output.cellId);
|
|
204
|
+
const versions = goalStageAttempts(engine, goal, stage).filter(attempt => attempt.index === output.index).slice().reverse().map(attempt => {
|
|
205
|
+
const versionCell = document.cells.find(candidate => candidate.id === attempt.cellId);
|
|
206
|
+
const step = getChainRun(engine, attempt.chainId)?.steps.find(candidate => candidate.id === attempt.stepId);
|
|
207
|
+
return {
|
|
208
|
+
...attempt, address: versionCell ? notebookGridTag(versionCell.slot) : null,
|
|
209
|
+
status: step?.status ?? "interrupted", current: attempt.artifactId === output.artifactId,
|
|
210
|
+
};
|
|
211
|
+
});
|
|
212
|
+
const stagedPlan = content.plan && "version" in content.plan && content.plan.version === 2 ? content.plan : null;
|
|
213
|
+
const stale = stagedPlan && (stage === "frame" || stage === "storyboards" || stage === "videos")
|
|
214
|
+
? await goalStillStaleReason(engine, goal, document, stagedPlan, content.characters, stage, output) : null;
|
|
215
|
+
return { ...output, address: outputCell ? notebookGridTag(outputCell.slot) : null, versions, stale };
|
|
216
|
+
})),
|
|
217
|
+
steps: goalChainSteps(engine, document, run.chainId),
|
|
218
|
+
allSteps: [...run.previousChainIds, run.chainId].flatMap(chainId => allGoalChainSteps(engine, document, chainId)),
|
|
219
|
+
}])));
|
|
220
|
+
const shotPlan = content.plan && "shots" in content.plan ? content.plan : null;
|
|
221
|
+
const stillCount = shotPlan ? shotPlan.shots.length * ("frameContinuity" in shotPlan && shotPlan.frameContinuity ? 2 : 1) : 1;
|
|
222
|
+
const displayedPlan = content.plan && "version" in content.plan && content.plan.version === 3 ? refreshGoalPlanV3(content.plan, document) : content.plan;
|
|
223
|
+
const v3Plan = displayedPlan && "version" in displayedPlan && displayedPlan.version === 3;
|
|
224
|
+
const still = v3Plan
|
|
225
|
+
? goal.stage === "videos" || goal.stage === "assembly" ? content.estimate.videos : content.estimate.frame
|
|
226
|
+
: goal.stage === "videos"
|
|
227
|
+
? { ...content.estimate.storyboards, usd: content.estimate.storyboards.usd === null ? null : content.estimate.storyboards.usd / stillCount }
|
|
228
|
+
: content.estimate.frame;
|
|
229
|
+
return {
|
|
230
|
+
kind: "video-goal", ...goal, ...content, plan: displayedPlan,
|
|
231
|
+
finish: goal.finish ?? null,
|
|
232
|
+
finalArtifactId: goal.finish?.finalArtifactId ?? null,
|
|
233
|
+
coldOpen: await coldOpenReview(engine, goal),
|
|
234
|
+
estimate: { ...content.estimate, still },
|
|
235
|
+
address: cell ? notebookGridTag(cell.slot) : null,
|
|
236
|
+
assemblyStale: goalAssemblyStale(engine, goal),
|
|
237
|
+
stages: stages,
|
|
238
|
+
steps: goalChainSteps(engine, document, goal.chainId),
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
export function goalAssemblyStale(engine, goal) {
|
|
242
|
+
if (!goal.assembly)
|
|
243
|
+
return false;
|
|
244
|
+
const clips = engine.sequences.getPrimary().clips;
|
|
245
|
+
const sources = goal.assembly.artifactIds ?? goal.clipIds.map(id => clips.find(clip => clip.clipId === id)?.source.artifactId ?? null);
|
|
246
|
+
return JSON.stringify(sources) !== JSON.stringify(completedGoalVideos(engine, goal));
|
|
247
|
+
}
|
|
248
|
+
export function completedGoalVideos(engine, goal) {
|
|
249
|
+
const run = goal.stages.videos;
|
|
250
|
+
const selected = new Map((run?.outputs ?? []).map(output => [output.index, output.artifactId]));
|
|
251
|
+
const chainId = run?.chainId ?? (goal.planVersion === 1 ? goal.chainId : null);
|
|
252
|
+
if (goal.status === "running" || selected.size === 0) {
|
|
253
|
+
for (const step of chainId ? getChainRun(engine, chainId)?.steps ?? [] : []) {
|
|
254
|
+
const id = outputString(step.outputs, "asset_id") ?? outputString(step.outputs, "vid_asset_id");
|
|
255
|
+
const index = stageOutputIndex(step.id);
|
|
256
|
+
if (step.tool === GOAL_VIDEO_TOOL && step.status === "completed" && id && Number.isInteger(index) && index >= 0)
|
|
257
|
+
selected.set(index, id);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return [...selected.entries()].sort(([left], [right]) => left - right).map(([, id]) => id);
|
|
261
|
+
}
|