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,133 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { queue } from "../queue.js";
|
|
4
|
+
import { findVideoFile, findAudioFile } from "videobook-engine";
|
|
5
|
+
import { probeVideo } from "../ffmpeg.js";
|
|
6
|
+
export function replaceVideoAudioDef(engine) {
|
|
7
|
+
return {
|
|
8
|
+
name: "replace_video_audio",
|
|
9
|
+
description: "Replace a video's audio track in place with an audio asset, or with one file of another asset given as <artifactId>/<filename>. Existing renders become stale.",
|
|
10
|
+
schema: {
|
|
11
|
+
asset_id: z.string().describe("Video artifact whose audio is being replaced"),
|
|
12
|
+
audio_asset_id: z.string().describe("Audio source: an audio artifact id, or `<artifactId>/<filename>` to point at a specific file inside another artifact"),
|
|
13
|
+
book_id: z.string().describe("Book UUID"),
|
|
14
|
+
},
|
|
15
|
+
handler: async (args) => {
|
|
16
|
+
const { asset_id, audio_asset_id, book_id } = args;
|
|
17
|
+
const videoArtifact = engine.artifacts.get(asset_id);
|
|
18
|
+
if (!videoArtifact.ok) {
|
|
19
|
+
return {
|
|
20
|
+
isError: true,
|
|
21
|
+
content: [{ type: "text", text: videoArtifact.error.message }],
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
if (videoArtifact.value.kind !== "video") {
|
|
25
|
+
return {
|
|
26
|
+
isError: true,
|
|
27
|
+
content: [{ type: "text", text: `asset_id must be a video artifact, got ${videoArtifact.value.kind} artifact ${asset_id}` }],
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
// `<artifactId>/<filename>` addresses a specific sibling file. Used by
|
|
31
|
+
// PlanViewer to splice the per-scene `plan/sN.mp3` into a freshly-
|
|
32
|
+
// generated seedance video output. Bare ids must be audio artifacts.
|
|
33
|
+
const slashIdx = audio_asset_id.indexOf("/");
|
|
34
|
+
const audioAssetIdResolved = slashIdx >= 0 ? audio_asset_id.slice(0, slashIdx) : audio_asset_id;
|
|
35
|
+
const audioFilenameOverride = slashIdx >= 0 ? audio_asset_id.slice(slashIdx + 1) : null;
|
|
36
|
+
const audioArtifact = engine.artifacts.get(audioAssetIdResolved);
|
|
37
|
+
if (!audioArtifact.ok) {
|
|
38
|
+
return {
|
|
39
|
+
isError: true,
|
|
40
|
+
content: [{ type: "text", text: audioArtifact.error.message }],
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
if (slashIdx < 0 && audioArtifact.value.kind !== "audio") {
|
|
44
|
+
return {
|
|
45
|
+
isError: true,
|
|
46
|
+
content: [{ type: "text", text: `audio_asset_id must be an audio artifact or <artifactId>/<filename>, got ${audioArtifact.value.kind} artifact ${audio_asset_id}` }],
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const videoManifest = await engine.files.manifest(asset_id);
|
|
50
|
+
if (!videoManifest.ok) {
|
|
51
|
+
return {
|
|
52
|
+
isError: true,
|
|
53
|
+
content: [{ type: "text", text: videoManifest.error.message }],
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
const videoFile = findVideoFile(videoManifest.value.files);
|
|
57
|
+
if (!videoFile) {
|
|
58
|
+
return {
|
|
59
|
+
isError: true,
|
|
60
|
+
content: [{ type: "text", text: `No video file found in asset ${asset_id}` }],
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const audioManifest = await engine.files.manifest(audioAssetIdResolved);
|
|
64
|
+
if (!audioManifest.ok) {
|
|
65
|
+
return {
|
|
66
|
+
isError: true,
|
|
67
|
+
content: [{ type: "text", text: audioManifest.error.message }],
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
let audioFileName;
|
|
71
|
+
if (audioFilenameOverride !== null) {
|
|
72
|
+
const file = audioManifest.value.files.find((f) => f.name === audioFilenameOverride);
|
|
73
|
+
if (!file) {
|
|
74
|
+
return {
|
|
75
|
+
isError: true,
|
|
76
|
+
content: [{ type: "text", text: `File ${audioFilenameOverride} not found in asset ${audioAssetIdResolved}` }],
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
audioFileName = file.name;
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
const file = findAudioFile(audioManifest.value.files, audioArtifact.value.kind);
|
|
83
|
+
if (!file) {
|
|
84
|
+
return {
|
|
85
|
+
isError: true,
|
|
86
|
+
content: [{ type: "text", text: `No audio file found in asset ${audioAssetIdResolved}` }],
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
audioFileName = file.name;
|
|
90
|
+
}
|
|
91
|
+
const assetDir = videoManifest.value.path;
|
|
92
|
+
const videoPath = join(assetDir, videoFile.name);
|
|
93
|
+
const audioPath = join(audioManifest.value.path, audioFileName);
|
|
94
|
+
try {
|
|
95
|
+
const [video, audio] = await Promise.all([probeVideo(videoPath), probeVideo(audioPath)]);
|
|
96
|
+
if (!video.videoStream || video.videoDuration <= 0)
|
|
97
|
+
throw new Error("Video source has no playable video stream");
|
|
98
|
+
if (!audio.hasAudio || (audio.audioDuration ?? audio.duration) <= 0)
|
|
99
|
+
throw new Error("Replacement source has no playable audio stream");
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
return { isError: true, content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }] };
|
|
103
|
+
}
|
|
104
|
+
const tempPath = join(assetDir, "original_audio_replaced.mp4");
|
|
105
|
+
const jobId = `replace-audio-${asset_id}-${Date.now()}`;
|
|
106
|
+
try {
|
|
107
|
+
await queue.enqueueAndWait(jobId, {
|
|
108
|
+
type: "replace_video_audio",
|
|
109
|
+
inputPath: videoPath,
|
|
110
|
+
audioPath,
|
|
111
|
+
outputPath: tempPath,
|
|
112
|
+
assetId: asset_id,
|
|
113
|
+
assetDir,
|
|
114
|
+
audioAssetId: audio_asset_id,
|
|
115
|
+
bookId: book_id,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
120
|
+
return {
|
|
121
|
+
isError: true,
|
|
122
|
+
content: [{ type: "text", text: `Replace audio failed: ${msg}` }],
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
content: [{
|
|
127
|
+
type: "text",
|
|
128
|
+
text: JSON.stringify({ asset_id, audio_asset_id, job_id: jobId, status: "completed" }),
|
|
129
|
+
}],
|
|
130
|
+
};
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { extname } from "node:path";
|
|
3
|
+
import { indexKey } from "../archive-b2.js";
|
|
4
|
+
import { getArchiveStore } from "../archive-store.js";
|
|
5
|
+
import { queue } from "../queue.js";
|
|
6
|
+
const MAX_RESTORE_IDS = 25;
|
|
7
|
+
const ARCHIVE_ID_RE = /^[a-f0-9]{64}$/i;
|
|
8
|
+
const EXTENSION_TARGETS = new Map([
|
|
9
|
+
[".mp4", { kind: "video" }],
|
|
10
|
+
[".mov", { kind: "video" }],
|
|
11
|
+
[".webm", { kind: "video" }],
|
|
12
|
+
[".mkv", { kind: "video" }],
|
|
13
|
+
[".mp3", { kind: "audio" }],
|
|
14
|
+
[".wav", { kind: "audio" }],
|
|
15
|
+
[".m4a", { kind: "audio" }],
|
|
16
|
+
[".aac", { kind: "audio" }],
|
|
17
|
+
[".flac", { kind: "audio" }],
|
|
18
|
+
[".ogg", { kind: "audio" }],
|
|
19
|
+
[".weba", { kind: "audio" }],
|
|
20
|
+
]);
|
|
21
|
+
const CONTENT_TYPE_EXTENSIONS = new Map([
|
|
22
|
+
["video/mp4", ".mp4"],
|
|
23
|
+
["video/quicktime", ".mov"],
|
|
24
|
+
["video/webm", ".webm"],
|
|
25
|
+
["video/x-matroska", ".mkv"],
|
|
26
|
+
["audio/mpeg", ".mp3"],
|
|
27
|
+
["audio/wav", ".wav"],
|
|
28
|
+
["audio/mp4", ".m4a"],
|
|
29
|
+
["audio/aac", ".aac"],
|
|
30
|
+
["audio/flac", ".flac"],
|
|
31
|
+
["audio/ogg", ".ogg"],
|
|
32
|
+
["audio/webm", ".weba"],
|
|
33
|
+
]);
|
|
34
|
+
function toolError(message) {
|
|
35
|
+
return {
|
|
36
|
+
isError: true,
|
|
37
|
+
content: [{ type: "text", text: message }],
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function normalizeArchiveIds(values) {
|
|
41
|
+
const seen = new Set();
|
|
42
|
+
const out = [];
|
|
43
|
+
for (const value of values) {
|
|
44
|
+
const id = value.trim().toLowerCase();
|
|
45
|
+
if (!id || seen.has(id))
|
|
46
|
+
continue;
|
|
47
|
+
seen.add(id);
|
|
48
|
+
out.push(id);
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
function archiveMediaTarget(index) {
|
|
53
|
+
const filenameExtension = extname(index.media.filename).toLowerCase();
|
|
54
|
+
const contentType = index.media.content_type.toLowerCase().split(";")[0]?.trim() ?? "";
|
|
55
|
+
const contentTypeExtension = CONTENT_TYPE_EXTENSIONS.get(contentType) || "";
|
|
56
|
+
const extension = filenameExtension === ".webm" && contentTypeExtension === ".weba"
|
|
57
|
+
? ".weba"
|
|
58
|
+
: EXTENSION_TARGETS.has(filenameExtension) ? filenameExtension : contentTypeExtension;
|
|
59
|
+
const target = EXTENSION_TARGETS.get(extension);
|
|
60
|
+
if (!target)
|
|
61
|
+
return null;
|
|
62
|
+
return {
|
|
63
|
+
...target,
|
|
64
|
+
filename: `original${extension}`,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function archiveAssetName(index) {
|
|
68
|
+
const title = index.title?.trim();
|
|
69
|
+
if (title)
|
|
70
|
+
return title;
|
|
71
|
+
const base = index.media.filename.replace(/\.[^.]+$/, "").trim();
|
|
72
|
+
return base || `archive ${index.archive_id.slice(0, 8)}`;
|
|
73
|
+
}
|
|
74
|
+
function tagLabels(tags) {
|
|
75
|
+
return tags.map((tag) => tag.label);
|
|
76
|
+
}
|
|
77
|
+
function archiveMetadata(index) {
|
|
78
|
+
return {
|
|
79
|
+
archive_id: index.archive_id,
|
|
80
|
+
sha256: index.sha256,
|
|
81
|
+
title: index.title,
|
|
82
|
+
media_key: index.media.key,
|
|
83
|
+
media_filename: index.media.filename,
|
|
84
|
+
media_content_type: index.media.content_type,
|
|
85
|
+
media_size_bytes: index.media.size_bytes,
|
|
86
|
+
archive_created_at: index.created_at,
|
|
87
|
+
archive_updated_at: index.updated_at,
|
|
88
|
+
archived_at: index.created_at,
|
|
89
|
+
restored_at: new Date().toISOString(),
|
|
90
|
+
topics: tagLabels(index.topics),
|
|
91
|
+
people: tagLabels(index.people),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
async function existingArchiveAssets(engine, archiveIds) {
|
|
95
|
+
const wanted = new Set(archiveIds);
|
|
96
|
+
const out = new Map();
|
|
97
|
+
const assets = await engine.artifacts.list();
|
|
98
|
+
await Promise.all(assets.map(async (asset) => {
|
|
99
|
+
const meta = await engine.metadata.artifacts.read(asset.artifactId, "archive");
|
|
100
|
+
if (!meta.ok)
|
|
101
|
+
return;
|
|
102
|
+
const archiveId = meta.value.archive_id;
|
|
103
|
+
if (typeof archiveId === "string" && wanted.has(archiveId)) {
|
|
104
|
+
out.set(archiveId, asset.artifactId);
|
|
105
|
+
}
|
|
106
|
+
}));
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
109
|
+
export function restoreArchiveClipsDef(engine) {
|
|
110
|
+
return {
|
|
111
|
+
name: "restore_archive_clips",
|
|
112
|
+
description: "Restore selected archived video/audio clips into the current Videobook as new assets.",
|
|
113
|
+
schema: {
|
|
114
|
+
book_id: z.string().describe("Book id to restore clips into"),
|
|
115
|
+
archive_ids: z.array(z.string()).min(1).max(MAX_RESTORE_IDS).describe("Archive IDs to restore"),
|
|
116
|
+
skip_existing: z.boolean().optional().default(true).describe("Skip archive IDs already restored into this project"),
|
|
117
|
+
},
|
|
118
|
+
handler: async (args) => {
|
|
119
|
+
const { book_id, archive_ids, skip_existing } = args;
|
|
120
|
+
const archiveIds = normalizeArchiveIds(archive_ids);
|
|
121
|
+
if (archiveIds.length === 0)
|
|
122
|
+
return toolError("restore_archive_clips requires at least one archive_id");
|
|
123
|
+
if (archiveIds.length > MAX_RESTORE_IDS)
|
|
124
|
+
return toolError(`restore_archive_clips supports at most ${MAX_RESTORE_IDS} archive_ids`);
|
|
125
|
+
const invalid = archiveIds.filter((id) => !ARCHIVE_ID_RE.test(id));
|
|
126
|
+
if (invalid.length > 0)
|
|
127
|
+
return toolError(`Invalid archive_id: ${invalid.join(", ")}`);
|
|
128
|
+
const store = getArchiveStore();
|
|
129
|
+
const indices = [];
|
|
130
|
+
for (const archiveId of archiveIds) {
|
|
131
|
+
const index = await store.getJson(indexKey(store, archiveId));
|
|
132
|
+
if (!index)
|
|
133
|
+
return toolError(`Archive clip not found: ${archiveId}`);
|
|
134
|
+
indices.push(index);
|
|
135
|
+
}
|
|
136
|
+
const existing = skip_existing
|
|
137
|
+
? await existingArchiveAssets(engine, indices.map((index) => index.archive_id))
|
|
138
|
+
: new Map();
|
|
139
|
+
const targets = new Map();
|
|
140
|
+
for (const index of indices) {
|
|
141
|
+
if (existing.has(index.archive_id))
|
|
142
|
+
continue;
|
|
143
|
+
const target = archiveMediaTarget(index);
|
|
144
|
+
if (!target)
|
|
145
|
+
return toolError(`Archive media type is not supported: ${index.media.filename}`);
|
|
146
|
+
targets.set(index.archive_id, target);
|
|
147
|
+
}
|
|
148
|
+
const restored = [];
|
|
149
|
+
const skipped = [];
|
|
150
|
+
for (const index of indices) {
|
|
151
|
+
const existingAssetId = existing.get(index.archive_id);
|
|
152
|
+
if (existingAssetId) {
|
|
153
|
+
skipped.push({ archive_id: index.archive_id, asset_id: existingAssetId, reason: "already_restored" });
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
const target = targets.get(index.archive_id);
|
|
157
|
+
const created = await engine.artifacts.create(target.kind, archiveAssetName(index));
|
|
158
|
+
if (!created.ok)
|
|
159
|
+
return toolError(created.error.message);
|
|
160
|
+
const writeArchive = await engine.metadata.artifacts.write(created.value.artifactId, "archive", archiveMetadata(index));
|
|
161
|
+
if (!writeArchive.ok) {
|
|
162
|
+
await engine.artifacts.delete(created.value.artifactId).catch(() => undefined);
|
|
163
|
+
return toolError(writeArchive.error.message);
|
|
164
|
+
}
|
|
165
|
+
const jobId = `restore-archive-${created.value.artifactId}-${Date.now()}`;
|
|
166
|
+
try {
|
|
167
|
+
await queue.enqueue(jobId, {
|
|
168
|
+
type: "restore_archive_clip",
|
|
169
|
+
bookId: book_id,
|
|
170
|
+
assetId: created.value.artifactId,
|
|
171
|
+
assetDir: created.value.path,
|
|
172
|
+
archiveId: index.archive_id,
|
|
173
|
+
mediaKey: index.media.key,
|
|
174
|
+
filename: target.filename,
|
|
175
|
+
contentType: index.media.content_type,
|
|
176
|
+
mediaKind: target.kind,
|
|
177
|
+
title: index.title,
|
|
178
|
+
duration: index.duration,
|
|
179
|
+
topics: tagLabels(index.topics),
|
|
180
|
+
people: tagLabels(index.people),
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
await engine.artifacts.delete(created.value.artifactId).catch(() => undefined);
|
|
185
|
+
return toolError(error instanceof Error ? error.message : String(error));
|
|
186
|
+
}
|
|
187
|
+
restored.push({
|
|
188
|
+
archive_id: index.archive_id,
|
|
189
|
+
asset_id: created.value.artifactId,
|
|
190
|
+
job_id: jobId,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
return {
|
|
194
|
+
content: [{
|
|
195
|
+
type: "text",
|
|
196
|
+
text: JSON.stringify({
|
|
197
|
+
status: restored.length > 0 ? "enqueued" : "noop",
|
|
198
|
+
restored,
|
|
199
|
+
skipped,
|
|
200
|
+
}),
|
|
201
|
+
}],
|
|
202
|
+
};
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
export const __restoreArchiveClipsTest = {
|
|
207
|
+
archiveMediaTarget,
|
|
208
|
+
normalizeArchiveIds,
|
|
209
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function restoreAssetVersionDef(engine) {
|
|
3
|
+
return {
|
|
4
|
+
name: "restore_asset_version",
|
|
5
|
+
description: "Restore an asset from an immutable revision as a new revision",
|
|
6
|
+
schema: {
|
|
7
|
+
asset_id: z
|
|
8
|
+
.string()
|
|
9
|
+
.describe("Stable artifact UUID, or the slug of an active artifact"),
|
|
10
|
+
commit_hash: z.string().describe("DoltLite revision hash to restore from"),
|
|
11
|
+
book_id: z.string().describe("Book id"),
|
|
12
|
+
},
|
|
13
|
+
handler: async (args) => {
|
|
14
|
+
const { asset_id, commit_hash } = args;
|
|
15
|
+
const artifact = engine.artifacts.get(asset_id);
|
|
16
|
+
const artifactId = artifact.ok ? artifact.value.artifactId : asset_id;
|
|
17
|
+
const restored = await engine.history.restoreArtifact(artifactId, commit_hash);
|
|
18
|
+
if (!restored.ok) {
|
|
19
|
+
return {
|
|
20
|
+
isError: true,
|
|
21
|
+
content: [{ type: "text", text: restored.error.message }],
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
content: [{
|
|
26
|
+
type: "text",
|
|
27
|
+
text: JSON.stringify({
|
|
28
|
+
status: "restored",
|
|
29
|
+
asset_id: artifactId,
|
|
30
|
+
from_commit: commit_hash,
|
|
31
|
+
new_commit: restored.value.hash,
|
|
32
|
+
}),
|
|
33
|
+
}],
|
|
34
|
+
};
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { queue } from "../queue.js";
|
|
3
|
+
function error(text) {
|
|
4
|
+
return { isError: true, content: [{ type: "text", text }] };
|
|
5
|
+
}
|
|
6
|
+
/** Replay a failed generation using its persisted `generation-request` payload.
|
|
7
|
+
* Clears the error row (reverting the asset to claimable `pending`) and
|
|
8
|
+
* re-enqueues the exact job, so the queue's generate_* handoff re-claims the
|
|
9
|
+
* SAME asset. Safe because generationErrors.clear is CAS-revertible. */
|
|
10
|
+
export function retryGenerationDef(engine) {
|
|
11
|
+
return {
|
|
12
|
+
name: "retry_generation",
|
|
13
|
+
description: "Re-run a failed generation with its original request payload and inputs, clearing the asset's error first.",
|
|
14
|
+
schema: {
|
|
15
|
+
asset_id: z.string().describe("Asset ID of the failed generation to retry (vid-*, or @-prefixed)"),
|
|
16
|
+
book_id: z.string().describe("Book id"),
|
|
17
|
+
},
|
|
18
|
+
handler: async (args) => {
|
|
19
|
+
const { asset_id, book_id: _book_id } = args;
|
|
20
|
+
const assetId = asset_id.replace(/^@/, "");
|
|
21
|
+
const metaResult = await engine.metadata.artifacts.read(assetId, "generation-request");
|
|
22
|
+
if (!metaResult.ok || metaResult.value === null) {
|
|
23
|
+
return error(`No generation-request metadata found for ${assetId} — cannot retry. Recover or regenerate instead.`);
|
|
24
|
+
}
|
|
25
|
+
const payload = metaResult.value;
|
|
26
|
+
const type = typeof payload.type === "string" ? payload.type : "";
|
|
27
|
+
if (type !== "generate_media") {
|
|
28
|
+
return error(`Stored generation-request for ${assetId} is not a replayable generation (type: ${type || "unknown"}); regenerate it with generate_image, generate_video or generate_audio`);
|
|
29
|
+
}
|
|
30
|
+
const embeddedId = typeof payload.assetId === "string" ? payload.assetId : null;
|
|
31
|
+
if (embeddedId !== assetId) {
|
|
32
|
+
return error(`generation-request payload targets a different asset (${embeddedId ?? "none"}) than ${assetId}`);
|
|
33
|
+
}
|
|
34
|
+
// Refuse if an active task is already in flight for this asset.
|
|
35
|
+
const taskResult = await engine.jobs.pending.read(assetId);
|
|
36
|
+
const activeTask = taskResult.ok ? taskResult.value : null;
|
|
37
|
+
if (activeTask !== null) {
|
|
38
|
+
return error(`${assetId} already has an active generation in progress (task ${activeTask.taskId})`);
|
|
39
|
+
}
|
|
40
|
+
const cleared = await engine.jobs.failures.clear(assetId);
|
|
41
|
+
if (!cleared.ok) {
|
|
42
|
+
return error(`Failed to clear error for ${assetId}: ${cleared.error.message}`);
|
|
43
|
+
}
|
|
44
|
+
const jobId = `retry-${assetId}-${Date.now()}`;
|
|
45
|
+
await queue.enqueue(jobId, payload);
|
|
46
|
+
return {
|
|
47
|
+
content: [{
|
|
48
|
+
type: "text",
|
|
49
|
+
text: JSON.stringify({ asset_id: assetId, retried: true, job_id: jobId }),
|
|
50
|
+
}],
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { queue } from "../queue.js";
|
|
4
|
+
import { getFFmpeggy } from "../ffmpeg.js";
|
|
5
|
+
import { findVideoFile } from "videobook-engine";
|
|
6
|
+
const MAX_DURATION_SECONDS = 300;
|
|
7
|
+
async function hasAudioStream(filePath) {
|
|
8
|
+
const FFmpeggy = await getFFmpeggy();
|
|
9
|
+
const probe = await FFmpeggy.probe(filePath);
|
|
10
|
+
return probe.streams.some((s) => s.codec_type === "audio");
|
|
11
|
+
}
|
|
12
|
+
async function getVideoDuration(filePath) {
|
|
13
|
+
const FFmpeggy = await getFFmpeggy();
|
|
14
|
+
const probe = await FFmpeggy.probe(filePath);
|
|
15
|
+
return parseFloat(probe.format.duration);
|
|
16
|
+
}
|
|
17
|
+
export function reverseVideoDef(engine) {
|
|
18
|
+
return {
|
|
19
|
+
name: "reverse_video",
|
|
20
|
+
description: "Reverse video playback (play backwards). Toggle operation — reversing twice restores original. Max 300s due to memory buffering.",
|
|
21
|
+
schema: {
|
|
22
|
+
asset_id: z.string().describe("Asset ID (e.g. vid-my-video)"),
|
|
23
|
+
book_id: z.string().describe("Book id"),
|
|
24
|
+
},
|
|
25
|
+
handler: async (args) => {
|
|
26
|
+
const { asset_id, book_id } = args;
|
|
27
|
+
const manifestResult = await engine.files.manifest(asset_id);
|
|
28
|
+
if (!manifestResult.ok) {
|
|
29
|
+
return {
|
|
30
|
+
isError: true,
|
|
31
|
+
content: [{ type: "text", text: manifestResult.error.message }],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const manifest = manifestResult.value;
|
|
35
|
+
const videoFile = findVideoFile(manifest.files);
|
|
36
|
+
if (!videoFile) {
|
|
37
|
+
return {
|
|
38
|
+
isError: true,
|
|
39
|
+
content: [{ type: "text", text: `No video file found in asset ${asset_id}` }],
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
const assetDir = manifest.path;
|
|
43
|
+
const videoPath = join(assetDir, videoFile.name);
|
|
44
|
+
const duration = await getVideoDuration(videoPath);
|
|
45
|
+
if (duration > MAX_DURATION_SECONDS) {
|
|
46
|
+
return {
|
|
47
|
+
isError: true,
|
|
48
|
+
content: [{
|
|
49
|
+
type: "text",
|
|
50
|
+
text: `Video is ${duration.toFixed(1)}s — reverse requires buffering all frames in memory. Maximum supported duration is ${MAX_DURATION_SECONDS}s (5 minutes). Trim the video first.`,
|
|
51
|
+
}],
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
const hasAudio = await hasAudioStream(videoPath);
|
|
55
|
+
const tempPath = join(assetDir, "original_reversed.mp4");
|
|
56
|
+
const jobId = `reverse-${asset_id}-${Date.now()}`;
|
|
57
|
+
await queue.enqueue(jobId, {
|
|
58
|
+
type: "reverse_video",
|
|
59
|
+
inputPath: videoPath,
|
|
60
|
+
outputPath: tempPath,
|
|
61
|
+
hasAudio,
|
|
62
|
+
assetId: asset_id,
|
|
63
|
+
assetDir,
|
|
64
|
+
bookId: book_id,
|
|
65
|
+
});
|
|
66
|
+
return {
|
|
67
|
+
content: [{
|
|
68
|
+
type: "text",
|
|
69
|
+
text: JSON.stringify({ asset_id, job_id: jobId, status: "enqueued" }),
|
|
70
|
+
}],
|
|
71
|
+
};
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
}
|