videobook 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +54 -0
- package/LICENSE +21 -0
- package/README.md +195 -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 +242 -0
- package/dist/cli/config-command.js +103 -0
- package/dist/cli/doctor-command.js +289 -0
- package/dist/cli/entry.js +2 -0
- package/dist/cli/key-report.js +16 -0
- package/dist/cli/main.js +104 -0
- package/dist/cli/mcp-command.js +88 -0
- package/dist/cli/mcp-relay.js +51 -0
- package/dist/cli/open-browser.js +14 -0
- package/dist/cli/run-command.js +231 -0
- package/dist/cli/serve-command.js +62 -0
- package/dist/cli/setup-command.js +60 -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 +447 -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-file.js +91 -0
- package/dist/env-secrets.js +148 -0
- package/dist/ffmpeg-binary.js +12 -0
- package/dist/ffmpeg.js +425 -0
- package/dist/ffprobe-binary.js +77 -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/hydration.js +56 -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 +458 -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 +427 -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 +12 -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/one-password.js +200 -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 +810 -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 +125 -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 +2021 -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/ytdlp-binary.js +65 -0
- package/dist/zod-slots.js +334 -0
- package/package.json +84 -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,245 @@
|
|
|
1
|
+
import { v7 as uuidv7 } from "uuid";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { searchLibraryAssets, setNotebookMediaSlot, } from "../media-library.js";
|
|
4
|
+
import { queue } from "../queue.js";
|
|
5
|
+
import { searchReferenceStore } from "../search-reference-store.js";
|
|
6
|
+
import { isLibraryBrowseStatus, LIBRARY_BROWSE_STATUSES } from "../library-browse.js";
|
|
7
|
+
import { tagSelectorSchema } from "./asset-tags.js";
|
|
8
|
+
const mediaKinds = ["audio", "image", "video"];
|
|
9
|
+
function json(value) {
|
|
10
|
+
return {
|
|
11
|
+
content: [{
|
|
12
|
+
type: "text",
|
|
13
|
+
text: JSON.stringify(value),
|
|
14
|
+
}],
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function failure(cause) {
|
|
18
|
+
return {
|
|
19
|
+
isError: true,
|
|
20
|
+
content: [{
|
|
21
|
+
type: "text",
|
|
22
|
+
text: cause instanceof Error ? cause.message : String(cause),
|
|
23
|
+
}],
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export function mediaLibraryDefs(books) {
|
|
27
|
+
return [
|
|
28
|
+
{
|
|
29
|
+
name: "repair_library_search_indexes",
|
|
30
|
+
description: "Index ready visual media that is missing Similar-search coverage across books",
|
|
31
|
+
scope: "global",
|
|
32
|
+
schema: { kinds: z.array(z.enum(["image", "video"])).min(1).optional() },
|
|
33
|
+
handler: async (args) => {
|
|
34
|
+
await books.ready();
|
|
35
|
+
const kinds = [...new Set(args.kinds ?? ["image", "video"])].sort();
|
|
36
|
+
const summaries = books.list();
|
|
37
|
+
const jobs = await Promise.all(summaries.map(async (book) => {
|
|
38
|
+
const job = await queue.enqueue(`repair-search:${book.bookId}:${kinds.join(",")}`, {
|
|
39
|
+
type: "repair_search_indexes", bookId: book.bookId, kinds,
|
|
40
|
+
});
|
|
41
|
+
return { bookId: book.bookId, jobId: job.id };
|
|
42
|
+
}));
|
|
43
|
+
return json({ jobs });
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: "prepare_search_reference",
|
|
48
|
+
description: "Prepare an existing image or video asset for temporary reverse search",
|
|
49
|
+
scope: "global",
|
|
50
|
+
schema: {
|
|
51
|
+
source_book_id: z.string().uuid(),
|
|
52
|
+
source_artifact_id: z.string().uuid(),
|
|
53
|
+
kind: z.enum(["image", "video"]),
|
|
54
|
+
source_start_seconds: z.number().nonnegative().optional(),
|
|
55
|
+
source_duration_seconds: z.number().positive().optional(),
|
|
56
|
+
},
|
|
57
|
+
handler: async (args) => {
|
|
58
|
+
try {
|
|
59
|
+
await books.ready();
|
|
60
|
+
const sourceBookId = String(args.source_book_id);
|
|
61
|
+
const source = books.get(sourceBookId);
|
|
62
|
+
if (!source.ok)
|
|
63
|
+
throw new Error(source.error.message);
|
|
64
|
+
const artifact = source.value.engine.artifacts.get(String(args.source_artifact_id));
|
|
65
|
+
if (!artifact.ok)
|
|
66
|
+
throw new Error(artifact.error.message);
|
|
67
|
+
const result = await queue.enqueueAndWait(`prepare-search-reference-${uuidv7()}`, {
|
|
68
|
+
type: "prepare_search_reference",
|
|
69
|
+
bookId: sourceBookId,
|
|
70
|
+
mediaKind: String(args.kind),
|
|
71
|
+
name: artifact.value.label ?? artifact.value.artifactId,
|
|
72
|
+
assetId: artifact.value.artifactId,
|
|
73
|
+
...(typeof args.source_start_seconds === "number"
|
|
74
|
+
? { sourceStartMs: args.source_start_seconds * 1_000 }
|
|
75
|
+
: {}),
|
|
76
|
+
...(typeof args.source_duration_seconds === "number"
|
|
77
|
+
? { sourceDurationMs: args.source_duration_seconds * 1_000 }
|
|
78
|
+
: {}),
|
|
79
|
+
}, 15 * 60_000);
|
|
80
|
+
return json(result);
|
|
81
|
+
}
|
|
82
|
+
catch (cause) {
|
|
83
|
+
return failure(cause);
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: "release_search_reference",
|
|
89
|
+
description: "Release a temporary reverse-search reference",
|
|
90
|
+
scope: "global",
|
|
91
|
+
schema: {
|
|
92
|
+
reference_id: z.string().uuid(),
|
|
93
|
+
},
|
|
94
|
+
handler: async (args) => json({
|
|
95
|
+
released: searchReferenceStore.release(String(args.reference_id)),
|
|
96
|
+
}),
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: "set_notebook_media_slot",
|
|
100
|
+
description: "Attach or clear a passive notebook media slot",
|
|
101
|
+
scope: "global",
|
|
102
|
+
schema: {
|
|
103
|
+
book_id: z.string().uuid(),
|
|
104
|
+
notebook_id: z.string().uuid(),
|
|
105
|
+
cell_id: z.string().uuid(),
|
|
106
|
+
artifact_ref: z.string().min(1).nullable(),
|
|
107
|
+
expected_revision: z.string().min(1).optional(),
|
|
108
|
+
},
|
|
109
|
+
handler: async (args) => {
|
|
110
|
+
try {
|
|
111
|
+
await books.ready();
|
|
112
|
+
const book = books.get(String(args.book_id));
|
|
113
|
+
if (!book.ok)
|
|
114
|
+
throw new Error(book.error.message);
|
|
115
|
+
const result = await setNotebookMediaSlot(book.value.engine, {
|
|
116
|
+
notebookId: String(args.notebook_id),
|
|
117
|
+
cellId: String(args.cell_id),
|
|
118
|
+
artifactRef: typeof args.artifact_ref === "string"
|
|
119
|
+
? args.artifact_ref
|
|
120
|
+
: null,
|
|
121
|
+
...(typeof args.expected_revision === "string"
|
|
122
|
+
? { expectedRevision: args.expected_revision }
|
|
123
|
+
: {}),
|
|
124
|
+
});
|
|
125
|
+
return json({
|
|
126
|
+
cell_id: result.cellId,
|
|
127
|
+
output_artifact_id: result.outputArtifactId,
|
|
128
|
+
revision: result.revision,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
catch (cause) {
|
|
132
|
+
return failure(cause);
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
name: "search_library_assets",
|
|
138
|
+
description: "Search ready audio, image, and video assets across all books",
|
|
139
|
+
scope: "global",
|
|
140
|
+
schema: {
|
|
141
|
+
query: z.string().optional(),
|
|
142
|
+
reference_id: z.string().uuid().optional(),
|
|
143
|
+
reference_range: z.object({
|
|
144
|
+
start_seconds: z.number().nonnegative(),
|
|
145
|
+
duration_seconds: z.number().positive().optional(),
|
|
146
|
+
}).optional(),
|
|
147
|
+
kinds: z.array(z.enum(mediaKinds)).min(1),
|
|
148
|
+
tags_all: z.array(tagSelectorSchema).optional()
|
|
149
|
+
.describe("Only assets carrying every one of these tags"),
|
|
150
|
+
tags_any: z.array(tagSelectorSchema).optional()
|
|
151
|
+
.describe("Only assets carrying at least one of these tags"),
|
|
152
|
+
browse_status: z.enum(LIBRARY_BROWSE_STATUSES).optional()
|
|
153
|
+
.describe("Browse ready, processing, error, or all media when there is no text or reverse-search query. Semantic and reverse search stay ready-only."),
|
|
154
|
+
limit: z.number().int().positive().max(100).optional(),
|
|
155
|
+
offset: z.number().int().nonnegative().optional(),
|
|
156
|
+
},
|
|
157
|
+
handler: async (args) => {
|
|
158
|
+
try {
|
|
159
|
+
const referenceRange = searchReferenceRange(args.reference_range);
|
|
160
|
+
const tags = {
|
|
161
|
+
...(args.tags_all ? { all: args.tags_all } : {}),
|
|
162
|
+
...(args.tags_any ? { any: args.tags_any } : {}),
|
|
163
|
+
};
|
|
164
|
+
const browseStatus = isLibraryBrowseStatus(args.browse_status)
|
|
165
|
+
? args.browse_status
|
|
166
|
+
: undefined;
|
|
167
|
+
return json(await searchLibraryAssets(books, {
|
|
168
|
+
...(typeof args.query === "string" ? { query: args.query } : {}),
|
|
169
|
+
...(typeof args.reference_id === "string"
|
|
170
|
+
? { referenceId: args.reference_id }
|
|
171
|
+
: {}),
|
|
172
|
+
...(referenceRange ? { referenceRange } : {}),
|
|
173
|
+
kinds: args.kinds,
|
|
174
|
+
tags,
|
|
175
|
+
...(browseStatus ? { browseStatus } : {}),
|
|
176
|
+
...(typeof args.limit === "number" ? { limit: args.limit } : {}),
|
|
177
|
+
...(typeof args.offset === "number" ? { offset: args.offset } : {}),
|
|
178
|
+
}));
|
|
179
|
+
}
|
|
180
|
+
catch (cause) {
|
|
181
|
+
return failure(cause);
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
name: "attach_library_asset",
|
|
187
|
+
description: "Copy a foreign Library asset into a book and attach it to a media slot",
|
|
188
|
+
scope: "global",
|
|
189
|
+
schema: {
|
|
190
|
+
source_book_id: z.string().uuid(),
|
|
191
|
+
source_artifact_id: z.string().uuid(),
|
|
192
|
+
target_book_id: z.string().uuid(),
|
|
193
|
+
notebook_id: z.string().uuid(),
|
|
194
|
+
cell_id: z.string().uuid(),
|
|
195
|
+
expected_output_artifact_id: z.string().uuid().nullable(),
|
|
196
|
+
new_name: z.string().min(1).optional(),
|
|
197
|
+
},
|
|
198
|
+
handler: async (args) => {
|
|
199
|
+
try {
|
|
200
|
+
const jobId = `copy-library-asset-${uuidv7()}`;
|
|
201
|
+
const result = await queue.enqueueAndWait(jobId, {
|
|
202
|
+
type: "copy_library_asset",
|
|
203
|
+
bookId: String(args.target_book_id),
|
|
204
|
+
sourceBookId: String(args.source_book_id),
|
|
205
|
+
sourceArtifactId: String(args.source_artifact_id),
|
|
206
|
+
targetBookId: String(args.target_book_id),
|
|
207
|
+
notebookId: String(args.notebook_id),
|
|
208
|
+
cellId: String(args.cell_id),
|
|
209
|
+
expectedOutputArtifactId: typeof args.expected_output_artifact_id === "string"
|
|
210
|
+
? args.expected_output_artifact_id
|
|
211
|
+
: null,
|
|
212
|
+
...(typeof args.new_name === "string"
|
|
213
|
+
? { newName: args.new_name }
|
|
214
|
+
: {}),
|
|
215
|
+
}, 15 * 60_000);
|
|
216
|
+
return json({
|
|
217
|
+
source_book_id: result.sourceBookId,
|
|
218
|
+
source_artifact_id: result.sourceArtifactId,
|
|
219
|
+
target_book_id: result.targetBookId,
|
|
220
|
+
target_artifact_id: result.targetArtifactId,
|
|
221
|
+
notebook_id: result.notebookId,
|
|
222
|
+
cell_id: result.cellId,
|
|
223
|
+
revision: result.revision,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
catch (cause) {
|
|
227
|
+
return failure(cause);
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
];
|
|
232
|
+
}
|
|
233
|
+
function searchReferenceRange(value) {
|
|
234
|
+
if (typeof value !== "object" || value === null)
|
|
235
|
+
return undefined;
|
|
236
|
+
const range = value;
|
|
237
|
+
if (typeof range.start_seconds !== "number")
|
|
238
|
+
return undefined;
|
|
239
|
+
return {
|
|
240
|
+
startMs: range.start_seconds * 1_000,
|
|
241
|
+
...(typeof range.duration_seconds === "number"
|
|
242
|
+
? { durationMs: range.duration_seconds * 1_000 }
|
|
243
|
+
: {}),
|
|
244
|
+
};
|
|
245
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { readTimeline, timelineFailure, timelineInput, timelinePayload, writeTimeline, } from "../timeline-helpers.js";
|
|
3
|
+
export function moveTimelineAudioDef(engine) {
|
|
4
|
+
return {
|
|
5
|
+
name: "move_timeline_audio",
|
|
6
|
+
description: "Move an audio overlay to a frame offset",
|
|
7
|
+
schema: {
|
|
8
|
+
book_id: z.string().uuid(),
|
|
9
|
+
audio_id: z.string().uuid(),
|
|
10
|
+
start_frame: z.number().int().min(0),
|
|
11
|
+
},
|
|
12
|
+
handler: async (args) => {
|
|
13
|
+
const timeline = readTimeline(engine);
|
|
14
|
+
const audioId = String(args.audio_id);
|
|
15
|
+
if (!timeline.audio.some((clip) => clip.id === audioId)) {
|
|
16
|
+
return timelineFailure(`Timeline audio clip not found: ${audioId}`);
|
|
17
|
+
}
|
|
18
|
+
const result = await writeTimeline(engine, {
|
|
19
|
+
...timelineInput(timeline),
|
|
20
|
+
audio: timeline.audio.map((clip) => ({
|
|
21
|
+
id: clip.id,
|
|
22
|
+
artifactId: clip.artifactId,
|
|
23
|
+
startFrame: clip.id === audioId ? args.start_frame : clip.startFrame,
|
|
24
|
+
durationFrames: clip.durationFrames,
|
|
25
|
+
...(clip.volume === undefined ? {} : { volume: clip.volume }),
|
|
26
|
+
...(clip.fadeIn === undefined ? {} : { fadeIn: clip.fadeIn }),
|
|
27
|
+
...(clip.fadeOut === undefined ? {} : { fadeOut: clip.fadeOut }),
|
|
28
|
+
})),
|
|
29
|
+
});
|
|
30
|
+
return result.ok ? {
|
|
31
|
+
content: [{ type: "text", text: JSON.stringify(timelinePayload(result.value)) }],
|
|
32
|
+
} : timelineFailure(result.error.message);
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { queue } from "../queue.js";
|
|
3
|
+
import { cachedMusicAnalysis, musicSource, readMusicAnalysis, readSectionLabels, sectionLabelsCurrent } from "../rhythm/music-analysis.js";
|
|
4
|
+
function json(value) {
|
|
5
|
+
return { content: [{ type: "text", text: JSON.stringify(value) }] };
|
|
6
|
+
}
|
|
7
|
+
export function musicAnalysisDefs(engine) {
|
|
8
|
+
const schema = { book_id: z.string().uuid(), asset_id: z.string().uuid(), stream_id: z.string().uuid().optional() };
|
|
9
|
+
return [
|
|
10
|
+
{
|
|
11
|
+
name: "analyze_music",
|
|
12
|
+
description: "Analyze an audio source's beats and waveform locally in a background job, reusing results for unchanged audio. sections adds an energy curve and suggested section boundaries and labels, which may use a paid model.",
|
|
13
|
+
scope: "book",
|
|
14
|
+
schema: { ...schema, force: z.boolean().optional(), sections: z.boolean().optional() },
|
|
15
|
+
handler: async (args) => {
|
|
16
|
+
try {
|
|
17
|
+
const source = await musicSource(engine, String(args.asset_id), args.stream_id);
|
|
18
|
+
if (!args.force && await cachedMusicAnalysis(engine, source)) {
|
|
19
|
+
if (args.sections === true && !await labelsReadyOrPending(engine, source)) {
|
|
20
|
+
await queue.enqueue(`music-sections-${source.artifactId}-${source.analysisId}`, {
|
|
21
|
+
type: "label_music_sections", bookId: String(args.book_id), artifactId: source.artifactId,
|
|
22
|
+
analysisId: source.analysisId, streamId: source.streamId,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
return json(await readMusicAnalysis(engine, source.artifactId, source.streamId));
|
|
26
|
+
}
|
|
27
|
+
await queue.enqueue(`music-${source.artifactId}-${source.analysisId}`, {
|
|
28
|
+
type: "analyze_music", bookId: String(args.book_id), musicArtifactId: source.artifactId,
|
|
29
|
+
analysisId: source.analysisId, streamId: source.streamId, sections: args.sections === true,
|
|
30
|
+
});
|
|
31
|
+
return json(await readMusicAnalysis(engine, source.artifactId, source.streamId));
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
return { isError: true, content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }] };
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: "get_music_analysis",
|
|
40
|
+
description: "Read cached music beats, waveform, analysis job status and optional section energy/label suggestions for the current source audio",
|
|
41
|
+
scope: "book", schema,
|
|
42
|
+
handler: async (args) => {
|
|
43
|
+
try {
|
|
44
|
+
return json(await readMusicAnalysis(engine, String(args.asset_id), args.stream_id));
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
return { isError: true, content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }] };
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
];
|
|
52
|
+
}
|
|
53
|
+
async function labelsReadyOrPending(engine, source) {
|
|
54
|
+
const analysis = await cachedMusicAnalysis(engine, source);
|
|
55
|
+
const stored = analysis ? await readSectionLabels(engine, source.artifactId, source.analysisId) : null;
|
|
56
|
+
if (stored && analysis && sectionLabelsCurrent(stored, analysis))
|
|
57
|
+
return true;
|
|
58
|
+
const jobs = engine.jobs.queue.list({ type: "label_music_sections", limit: 500 });
|
|
59
|
+
return jobs.some((item) => item.payload.analysisId === source.analysisId && ["queued", "running", "completing"].includes(item.state));
|
|
60
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { musicDraftOptionsSchema } from "../rhythm/draft-model.js";
|
|
3
|
+
import { planMusicVideo, commitMusicVideo } from "../rhythm/music-draft.js";
|
|
4
|
+
const schema = z.object({ book_id: z.string().uuid(), expected_revision: z.string().min(1), rhythm_revision: z.string().min(1),
|
|
5
|
+
actor: z.string().default("videobook-user"), source_surface: z.enum(["ui", "slash", "chat", "system"]).default("ui"), options: musicDraftOptionsSchema });
|
|
6
|
+
export function musicDraftDefs(engine) {
|
|
7
|
+
return [{ name: "plan_music_video", description: "Draft a beat-aligned music video from selected sources without saving it. Review the returned sequence, then apply it with commit_music_video using the returned intent, preview hash and revisions.", scope: "book", schema: schema.shape,
|
|
8
|
+
annotations: { readOnlyHint: true }, handler: async (input) => {
|
|
9
|
+
try {
|
|
10
|
+
const args = schema.parse(input);
|
|
11
|
+
return { content: [{ type: "text", text: JSON.stringify(await planMusicVideo(engine, args.options, args.expected_revision, args.rhythm_revision, args.actor, args.source_surface)) }] };
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
return { isError: true, content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }] };
|
|
15
|
+
}
|
|
16
|
+
} }, {
|
|
17
|
+
name: "commit_music_video", description: "Apply a reviewed music-video draft once, checking its timeline, source and beat-map revisions. Returns the previous action on a retry.", scope: "book",
|
|
18
|
+
schema: { book_id: z.string().uuid(), intent: z.record(z.string(), z.unknown()), preview_hash: z.string().min(1), rhythm_revision: z.string().min(1), sequence_content_revision: z.string().min(1) },
|
|
19
|
+
annotations: { idempotentHint: true }, handler: async (args) => {
|
|
20
|
+
try {
|
|
21
|
+
const result = await commitMusicVideo(engine, args.intent, String(args.preview_hash), String(args.rhythm_revision), String(args.sequence_content_revision));
|
|
22
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
return { isError: true, content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }] };
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
}];
|
|
29
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { queue } from "../queue.js";
|
|
4
|
+
import { findVideoFile } from "videobook-engine";
|
|
5
|
+
import { probeVideo } from "../ffmpeg.js";
|
|
6
|
+
import { parseTime } from "../time-parse.js";
|
|
7
|
+
export function padVideoDef(engine) {
|
|
8
|
+
return {
|
|
9
|
+
name: "pad_video",
|
|
10
|
+
description: "Append black video frames at the end to make a whole-second video tail. Retains original audio and makes the appended tail silent.",
|
|
11
|
+
schema: {
|
|
12
|
+
asset_id: z.string().describe("Source video asset ID"),
|
|
13
|
+
book_id: z.string().describe("Book id"),
|
|
14
|
+
padding: z.string().describe("Padding duration (e.g. 2s)"),
|
|
15
|
+
},
|
|
16
|
+
handler: async (args) => {
|
|
17
|
+
const { asset_id, book_id, padding } = args;
|
|
18
|
+
const paddingSecs = parseTime(padding);
|
|
19
|
+
if (paddingSecs === undefined || paddingSecs <= 0) {
|
|
20
|
+
return {
|
|
21
|
+
isError: true,
|
|
22
|
+
content: [{ type: "text", text: `Invalid padding duration: ${padding}` }],
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
const manifestResult = await engine.files.manifest(asset_id);
|
|
26
|
+
if (!manifestResult.ok) {
|
|
27
|
+
return {
|
|
28
|
+
isError: true,
|
|
29
|
+
content: [{ type: "text", text: manifestResult.error.message }],
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
const manifest = manifestResult.value;
|
|
33
|
+
const videoFile = findVideoFile(manifest.files);
|
|
34
|
+
if (!videoFile) {
|
|
35
|
+
return {
|
|
36
|
+
isError: true,
|
|
37
|
+
content: [{ type: "text", text: `No video file found in asset ${asset_id}` }],
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
const sourcePath = join(manifest.path, videoFile.name);
|
|
41
|
+
let sourceDurationSec;
|
|
42
|
+
let hasAudio;
|
|
43
|
+
try {
|
|
44
|
+
const probe = await probeVideo(sourcePath);
|
|
45
|
+
if (!probe.videoStream || probe.videoDuration <= 0)
|
|
46
|
+
throw new Error("Source has no playable video stream");
|
|
47
|
+
sourceDurationSec = probe.videoDuration;
|
|
48
|
+
hasAudio = probe.hasAudio;
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
return {
|
|
52
|
+
isError: true,
|
|
53
|
+
content: [{
|
|
54
|
+
type: "text",
|
|
55
|
+
text: `Failed to probe source duration: ${err instanceof Error ? err.message : String(err)}`,
|
|
56
|
+
}],
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const unroundedTarget = sourceDurationSec + paddingSecs;
|
|
60
|
+
const EPSILON = 1e-4;
|
|
61
|
+
const targetSecs = Math.ceil(unroundedTarget - EPSILON);
|
|
62
|
+
const jobId = `pad-video-${asset_id}-${Date.now()}`;
|
|
63
|
+
await queue.enqueue(jobId, {
|
|
64
|
+
type: "pad_video",
|
|
65
|
+
sourcePath,
|
|
66
|
+
assetDir: manifest.path,
|
|
67
|
+
sourceAssetId: asset_id,
|
|
68
|
+
bookId: book_id,
|
|
69
|
+
targetSecs,
|
|
70
|
+
hasAudio,
|
|
71
|
+
});
|
|
72
|
+
return {
|
|
73
|
+
content: [{
|
|
74
|
+
type: "text",
|
|
75
|
+
text: JSON.stringify({
|
|
76
|
+
source_asset_id: asset_id,
|
|
77
|
+
source_duration_sec: sourceDurationSec,
|
|
78
|
+
padding_sec: paddingSecs,
|
|
79
|
+
target_sec: targetSecs,
|
|
80
|
+
job_id: jobId,
|
|
81
|
+
status: "enqueued",
|
|
82
|
+
}),
|
|
83
|
+
}],
|
|
84
|
+
};
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { queue } from "../queue.js";
|
|
4
|
+
import { getPost, listPosts, migrateLegacyPosts } from "../posts/post-store.js";
|
|
5
|
+
import { POST_METRIC_NAMES, POST_PLATFORMS } from "../posts/post-contract.js";
|
|
6
|
+
import { recordPostMetrics } from "../posts/post-metrics.js";
|
|
7
|
+
import { postIcloudSyncStatus } from "../posts/icloud-drive.js";
|
|
8
|
+
import { postAnalysisTarget } from "../posts/post-copy.js";
|
|
9
|
+
import { freshPostLanguage } from "../lightning-defaults.js";
|
|
10
|
+
import { POST_COPY_LANGUAGES } from "../posts/post-copy-format.js";
|
|
11
|
+
import { getApiKey } from "../api-key.js";
|
|
12
|
+
import { analyzeVideoJobPayload } from "./analyze-video.js";
|
|
13
|
+
const book = { book_id: z.string() };
|
|
14
|
+
const target = { ...book, post_cell_id: z.string().uuid() };
|
|
15
|
+
const fields = { platform: z.enum(POST_PLATFORMS).optional(), account: z.string().max(200).optional(), caption: z.string().max(25_000).optional(), credits: z.string().max(10_000).optional(), notes: z.string().max(20_000).optional() };
|
|
16
|
+
function define(name, description, schema, handler, scope = "book") {
|
|
17
|
+
return { name, description, schema, scope, handler: async (args) => {
|
|
18
|
+
try {
|
|
19
|
+
return { content: [{ type: "text", text: JSON.stringify(await handler(args)) }] };
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
return { isError: true, content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }] };
|
|
23
|
+
}
|
|
24
|
+
} };
|
|
25
|
+
}
|
|
26
|
+
async function draftPostCopyTool(engine, args) {
|
|
27
|
+
if (!getApiKey("OPENAI_API_KEY"))
|
|
28
|
+
throw new Error("OPENAI_API_KEY is not set");
|
|
29
|
+
const bookId = String(args.book_id);
|
|
30
|
+
const postCellId = String(args.post_cell_id);
|
|
31
|
+
const post = getPost(engine, postCellId);
|
|
32
|
+
if (!post.definition.source)
|
|
33
|
+
throw new Error(`${post.address} has no saved source clip. Choose one in the Post panel.`);
|
|
34
|
+
const target = await postAnalysisTarget(engine, post);
|
|
35
|
+
if (target?.analyzable && !target.analyzed) {
|
|
36
|
+
await queue.enqueueAndWait(`post-copy-analysis-${target.assetId}-${randomUUID()}`, await analyzeVideoJobPayload(engine, target.assetId, bookId, "tagging"));
|
|
37
|
+
}
|
|
38
|
+
const platform = args.platform ?? post.definition.platform;
|
|
39
|
+
const suppliedTags = Array.isArray(args.tags)
|
|
40
|
+
? args.tags.filter((tag) => typeof tag === "string")
|
|
41
|
+
: undefined;
|
|
42
|
+
const request = {
|
|
43
|
+
platform,
|
|
44
|
+
language: freshPostLanguage(platform, args.language),
|
|
45
|
+
instruction: typeof args.instruction === "string" ? args.instruction : undefined,
|
|
46
|
+
caption: typeof args.caption === "string" ? args.caption : undefined,
|
|
47
|
+
tags: suppliedTags,
|
|
48
|
+
};
|
|
49
|
+
return queue.enqueueAndWait(`post-copy-${postCellId}-${randomUUID()}`, { type: "post_copy", bookId, postCellId, request });
|
|
50
|
+
}
|
|
51
|
+
export function postRecordDefs(engine) {
|
|
52
|
+
return [
|
|
53
|
+
define("create_post", "Create a Post cell to track one publication. Freezes the selected source clip and its upstream idea/prompt lineage; place the cell beside the source.", { ...book, notebook_id: z.string().uuid(), source_cell_id: z.string().uuid().optional(), ...fields }, async (args) => queue.enqueueAndWait(`post-create-${randomUUID()}`, { type: "post_create", bookId: String(args.book_id), args: { notebookId: String(args.notebook_id), sourceCellId: args.source_cell_id, platform: args.platform, caption: args.caption, credits: args.credits, account: args.account, notes: args.notes } })),
|
|
54
|
+
define("update_post", "Edit a Post draft or its notes. Delivered media, caption and source versions are immutable; create another Post for a repost.", { ...target, ...fields, source_cell_id: z.string().uuid().optional() }, async (args) => {
|
|
55
|
+
const patch = {};
|
|
56
|
+
for (const name of ["account", "caption", "credits", "notes"])
|
|
57
|
+
if (typeof args[name] === "string")
|
|
58
|
+
patch[name] = args[name];
|
|
59
|
+
if (args.platform)
|
|
60
|
+
patch.platform = args.platform;
|
|
61
|
+
if (args.source_cell_id)
|
|
62
|
+
patch.sourceCellId = String(args.source_cell_id);
|
|
63
|
+
return queue.enqueueAndWait(`post-update-${args.post_cell_id}-${randomUUID()}`, { type: "post_update", bookId: String(args.book_id), postCellId: String(args.post_cell_id), patch });
|
|
64
|
+
}),
|
|
65
|
+
define("get_post", "Read a Post with its frozen source, lineage, publication receipt and timestamped results.", target, async (args) => { await migrateLegacyPosts(engine); return getPost(engine, String(args.post_cell_id)); }),
|
|
66
|
+
define("list_posts", "List this book's publications and performance observations for comparison.", book, async () => { await migrateLegacyPosts(engine); return { posts: listPosts(engine) }; }),
|
|
67
|
+
define("link_post", "Attach the actual URL and publication time after manually posting. Freezes media and caption; duplicate publication URLs are rejected.", { ...target, url: z.string().url(), posted_at: z.string().datetime() }, async (args) => {
|
|
68
|
+
const post = await queue.enqueueAndWait(`post-link-${args.post_cell_id}-${randomUUID()}`, { type: "post_link", bookId: String(args.book_id), postCellId: String(args.post_cell_id), url: String(args.url), postedAt: String(args.posted_at) });
|
|
69
|
+
if (post.definition.platform === "x")
|
|
70
|
+
await queue.enqueue(`post-metrics-${post.id}-${randomUUID()}`, { type: "post_metrics", bookId: String(args.book_id), postCellId: post.id });
|
|
71
|
+
return post;
|
|
72
|
+
}),
|
|
73
|
+
define("upload_post_to_dropbox", "Upload the frozen Post media and caption.txt to a private Dropbox folder. A caption saves to the Post first and freezes with the first delivery. Interrupted uploads resume without replacing unrelated files.", { ...target, folder: z.string().optional(), caption: fields.caption }, async (args) => queue.enqueueAndWait(`post-dropbox-${args.post_cell_id}-${randomUUID()}`, { type: "post_dropbox", bookId: String(args.book_id), postCellId: String(args.post_cell_id), folder: args.folder, caption: args.caption })),
|
|
74
|
+
define("save_post_to_icloud", "Deliver a Post's frozen media and caption.txt to the shared iCloud Drive Videobook folder (folder is relative to it) so they appear in Files on the phones. A caption saves to the Post first. Repeats are no-ops and never replace different files.", { ...target, folder: z.string().max(500).optional(), caption: fields.caption }, async (args) => queue.enqueueAndWait(`post-icloud-${args.post_cell_id}-${randomUUID()}`, { type: "post_icloud", bookId: String(args.book_id), postCellId: String(args.post_cell_id), folder: args.folder, caption: args.caption })),
|
|
75
|
+
define("draft_post_copy", "Draft a Post's caption and hashtags from its clip's analysis, transcript and prompts (analyzing the clip first when needed) without saving. language defaults to Thai for TikTok and English otherwise; instruction, caption and tags rewrite an existing draft.", { ...target, language: z.enum(POST_COPY_LANGUAGES).optional(), platform: z.enum(POST_PLATFORMS).optional(), instruction: z.string().max(1000).optional(), caption: fields.caption, tags: z.array(z.string().max(100)).max(50).optional() }, async (args) => draftPostCopyTool(engine, args)),
|
|
76
|
+
define("refresh_post_metrics", "Fetch a current X metrics observation. Non-X publications use record_post_metrics. Unknown values remain unknown.", target, async (args) => queue.enqueueAndWait(`post-metrics-${args.post_cell_id}-${randomUUID()}`, { type: "post_metrics", bookId: String(args.book_id), postCellId: String(args.post_cell_id) })),
|
|
77
|
+
define("record_post_metrics", "Append a manual performance observation with its actual timestamp. Omit unavailable metrics; zero means an observed zero.", { ...target, observed_at: z.string().datetime(), values: z.partialRecord(z.enum(POST_METRIC_NAMES), z.number().int().nonnegative().nullable()).refine((value) => Object.keys(value).length > 0), note: z.string().optional() }, async (args) => {
|
|
78
|
+
recordPostMetrics(engine, String(args.post_cell_id), { source: "manual", observedAt: String(args.observed_at), values: args.values, note: args.note });
|
|
79
|
+
return getPost(engine, String(args.post_cell_id));
|
|
80
|
+
}),
|
|
81
|
+
define("post_icloud_sync_status", "Read whether a saved Post's clip and caption.txt have finished uploading to iCloud. A folder outside iCloud Drive reports that it is not an iCloud folder.", target, async (args) => postIcloudSyncStatus(engine, String(args.post_cell_id))),
|
|
82
|
+
];
|
|
83
|
+
}
|