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,318 @@
|
|
|
1
|
+
import { normalizeFilePath } from "videobook-engine";
|
|
2
|
+
import { createWriteStream } from "node:fs";
|
|
3
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import * as path from "node:path";
|
|
6
|
+
import { pipeline } from "node:stream/promises";
|
|
7
|
+
import { queue } from "./queue.js";
|
|
8
|
+
const extensionToKind = {
|
|
9
|
+
".mp4": "video",
|
|
10
|
+
".mov": "video",
|
|
11
|
+
".webm": "video",
|
|
12
|
+
".avi": "video",
|
|
13
|
+
".mkv": "video",
|
|
14
|
+
".jpg": "image",
|
|
15
|
+
".jpeg": "image",
|
|
16
|
+
".png": "image",
|
|
17
|
+
".gif": "image",
|
|
18
|
+
".heic": "image",
|
|
19
|
+
".heif": "image",
|
|
20
|
+
".webp": "image",
|
|
21
|
+
".svg": "image",
|
|
22
|
+
".mp3": "audio",
|
|
23
|
+
".wav": "audio",
|
|
24
|
+
".aac": "audio",
|
|
25
|
+
".flac": "audio",
|
|
26
|
+
".ogg": "audio",
|
|
27
|
+
".m4a": "audio",
|
|
28
|
+
".weba": "audio",
|
|
29
|
+
".md": "script",
|
|
30
|
+
};
|
|
31
|
+
function statusCodeForFsError(error) {
|
|
32
|
+
if (error.code === "NOT_FOUND")
|
|
33
|
+
return 404;
|
|
34
|
+
if (error.code === "INVALID_INPUT" || error.code === "ALREADY_EXISTS")
|
|
35
|
+
return 400;
|
|
36
|
+
if (error.code === "LOCKED")
|
|
37
|
+
return 423;
|
|
38
|
+
return 500;
|
|
39
|
+
}
|
|
40
|
+
function availableUploadFilename(filename, existing) {
|
|
41
|
+
const files = new Set(existing.map((name) => name.normalize("NFC").toLowerCase()));
|
|
42
|
+
const directories = new Set();
|
|
43
|
+
for (const file of files) {
|
|
44
|
+
const parts = file.split("/");
|
|
45
|
+
for (let index = 1; index < parts.length; index += 1) {
|
|
46
|
+
directories.add(parts.slice(0, index).join("/"));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const parts = filename.split("/");
|
|
50
|
+
const selected = [];
|
|
51
|
+
for (const [index, part] of parts.entries()) {
|
|
52
|
+
const { name, ext } = path.posix.parse(part);
|
|
53
|
+
let candidate = part;
|
|
54
|
+
let counter = 1;
|
|
55
|
+
while (true) {
|
|
56
|
+
const candidatePath = [...selected, candidate].join("/").normalize("NFC").toLowerCase();
|
|
57
|
+
if (!files.has(candidatePath) && (index < parts.length - 1 || !directories.has(candidatePath)))
|
|
58
|
+
break;
|
|
59
|
+
candidate = `${name}-${counter}${ext}`;
|
|
60
|
+
counter += 1;
|
|
61
|
+
}
|
|
62
|
+
selected.push(candidate);
|
|
63
|
+
}
|
|
64
|
+
return selected.join("/");
|
|
65
|
+
}
|
|
66
|
+
export async function uploadLocalBytes(engine, args) {
|
|
67
|
+
return uploadLocal(engine, {
|
|
68
|
+
bookId: args.bookId,
|
|
69
|
+
filename: args.filename,
|
|
70
|
+
origin: args.origin,
|
|
71
|
+
waitForProcessing: args.waitForProcessing,
|
|
72
|
+
targetAssetId: args.targetAssetId,
|
|
73
|
+
write: async (assetId, writtenFilename) => {
|
|
74
|
+
const writeResult = await engine.files.write(assetId, writtenFilename, args.data);
|
|
75
|
+
if (!writeResult.ok) {
|
|
76
|
+
return {
|
|
77
|
+
ok: false,
|
|
78
|
+
statusCode: statusCodeForFsError(writeResult.error),
|
|
79
|
+
message: writeResult.error.message,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
return { ok: true };
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
export async function uploadLocalStream(engine, args) {
|
|
87
|
+
return uploadLocal(engine, {
|
|
88
|
+
bookId: args.bookId,
|
|
89
|
+
filename: args.filename,
|
|
90
|
+
origin: args.origin,
|
|
91
|
+
waitForProcessing: args.waitForProcessing,
|
|
92
|
+
targetAssetId: args.targetAssetId,
|
|
93
|
+
write: async (assetId, writtenFilename) => {
|
|
94
|
+
let uploadRoot;
|
|
95
|
+
try {
|
|
96
|
+
uploadRoot = await mkdtemp(path.join(tmpdir(), "videobook-media-upload-"));
|
|
97
|
+
const uploadPath = path.join(uploadRoot, "input");
|
|
98
|
+
await pipeline(args.stream, createWriteStream(uploadPath, { flags: "wx" }));
|
|
99
|
+
const written = await engine.files.writeFromPath(assetId, writtenFilename, uploadPath);
|
|
100
|
+
if (!written.ok) {
|
|
101
|
+
return {
|
|
102
|
+
ok: false,
|
|
103
|
+
statusCode: statusCodeForFsError(written.error),
|
|
104
|
+
message: written.error.message,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
return { ok: true };
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
return {
|
|
111
|
+
ok: false,
|
|
112
|
+
statusCode: 500,
|
|
113
|
+
message: error instanceof Error ? error.message : String(error),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
finally {
|
|
117
|
+
if (uploadRoot)
|
|
118
|
+
await rm(uploadRoot, { recursive: true, force: true, maxRetries: 3 });
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
// Long enough to cover slow streaming writes for large videos plus the
|
|
124
|
+
// runner's 250ms poll. process_video writes native metadata a few seconds in,
|
|
125
|
+
// so even if this lock expires mid-job, the sweep falls back to the metadata
|
|
126
|
+
// presence check and still skips the asset.
|
|
127
|
+
const UPLOAD_LOCK_MS = 5 * 60_000;
|
|
128
|
+
async function runProcessingJob(jobId, payload, waitForProcessing) {
|
|
129
|
+
if (!waitForProcessing) {
|
|
130
|
+
await queue.enqueue(jobId, payload);
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
await queue.enqueueAndWait(jobId, payload);
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
return error instanceof Error ? error.message : String(error);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async function uploadLocal(engine, args) {
|
|
142
|
+
const ext = path.extname(args.filename).toLowerCase();
|
|
143
|
+
const kind = extensionToKind[ext];
|
|
144
|
+
if (!kind && !args.targetAssetId) {
|
|
145
|
+
return {
|
|
146
|
+
ok: false,
|
|
147
|
+
statusCode: 400,
|
|
148
|
+
message: `Unsupported file extension: ${ext}`,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
let filename = args.filename;
|
|
152
|
+
if (args.targetAssetId) {
|
|
153
|
+
try {
|
|
154
|
+
filename = normalizeFilePath(filename);
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
return {
|
|
158
|
+
ok: false,
|
|
159
|
+
statusCode: 400,
|
|
160
|
+
message: error instanceof Error ? error.message : String(error),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
let assetId;
|
|
165
|
+
let artifactId;
|
|
166
|
+
let assetPath;
|
|
167
|
+
let assetKind;
|
|
168
|
+
let isNewAsset = false;
|
|
169
|
+
if (args.targetAssetId) {
|
|
170
|
+
const artifact = engine.artifacts.get(args.targetAssetId);
|
|
171
|
+
if (!artifact.ok) {
|
|
172
|
+
return {
|
|
173
|
+
ok: false,
|
|
174
|
+
statusCode: statusCodeForFsError(artifact.error),
|
|
175
|
+
message: artifact.error.message,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
if (artifact.value.kind !== "character") {
|
|
179
|
+
return { ok: false, statusCode: 400, message: "Target asset must be a character folder" };
|
|
180
|
+
}
|
|
181
|
+
const resolveResult = await engine.workspaces.resolveArtifact(args.targetAssetId);
|
|
182
|
+
if (!resolveResult.ok) {
|
|
183
|
+
return { ok: false, statusCode: statusCodeForFsError(resolveResult.error), message: resolveResult.error.message };
|
|
184
|
+
}
|
|
185
|
+
assetId = args.targetAssetId;
|
|
186
|
+
assetPath = resolveResult.value;
|
|
187
|
+
artifactId = artifact.value.artifactId;
|
|
188
|
+
assetKind = artifact.value.kind;
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
const assetName = path.basename(args.filename, ext);
|
|
192
|
+
const assetResult = await engine.artifacts.create(kind, assetName);
|
|
193
|
+
if (!assetResult.ok) {
|
|
194
|
+
return {
|
|
195
|
+
ok: false,
|
|
196
|
+
statusCode: statusCodeForFsError(assetResult.error),
|
|
197
|
+
message: assetResult.error.message,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
assetId = assetResult.value.artifactId;
|
|
201
|
+
artifactId = assetResult.value.artifactId;
|
|
202
|
+
assetPath = assetResult.value.path;
|
|
203
|
+
assetKind = assetResult.value.kind;
|
|
204
|
+
isNewAsset = true;
|
|
205
|
+
}
|
|
206
|
+
// Hold an fs lock from before the bytes hit disk through the process_*
|
|
207
|
+
// enqueue. Without it, sync's findUnprocessedUploads sweep races the
|
|
208
|
+
// streaming write — sees `original.{ext}` with no metadata and no lock —
|
|
209
|
+
// and queues a duplicate process_video that re-extracts every frame after
|
|
210
|
+
// the real job finishes.
|
|
211
|
+
const lockResult = await engine.jobs.locks.acquire(assetPath, {
|
|
212
|
+
durationMs: UPLOAD_LOCK_MS,
|
|
213
|
+
data: { state: "uploading" },
|
|
214
|
+
});
|
|
215
|
+
if (!lockResult.ok) {
|
|
216
|
+
if (isNewAsset)
|
|
217
|
+
await engine.artifacts.delete(assetId).catch(() => { });
|
|
218
|
+
return {
|
|
219
|
+
ok: false,
|
|
220
|
+
statusCode: statusCodeForFsError(lockResult.error),
|
|
221
|
+
message: lockResult.error.message,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
let outcome;
|
|
225
|
+
try {
|
|
226
|
+
let writtenFilename = isNewAsset ? `original${ext}` : filename;
|
|
227
|
+
if (!isNewAsset) {
|
|
228
|
+
const manifest = await engine.files.manifest(assetId);
|
|
229
|
+
if (!manifest.ok) {
|
|
230
|
+
return {
|
|
231
|
+
ok: false,
|
|
232
|
+
statusCode: statusCodeForFsError(manifest.error),
|
|
233
|
+
message: manifest.error.message,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
writtenFilename = availableUploadFilename(filename, manifest.value.files.map((file) => file.name));
|
|
237
|
+
}
|
|
238
|
+
const writeResult = await args.write(assetId, writtenFilename);
|
|
239
|
+
if (!writeResult.ok) {
|
|
240
|
+
outcome = {
|
|
241
|
+
ok: false,
|
|
242
|
+
statusCode: writeResult.statusCode,
|
|
243
|
+
message: writeResult.message,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
const provenance = { origin: args.origin ?? "upload", processed_at: new Date().toISOString() };
|
|
248
|
+
let processingError = null;
|
|
249
|
+
if (assetKind === "video") {
|
|
250
|
+
const videoPath = path.join(assetPath, writtenFilename);
|
|
251
|
+
const jobId = `process-video-${assetId}-${Date.now()}`;
|
|
252
|
+
// Downstream speech-to-text stays enqueued even when the caller waits:
|
|
253
|
+
// it is fire-and-forget (separate job id), so enqueueAndWait still
|
|
254
|
+
// returns as soon as processing finishes. Skipping it here left MCP
|
|
255
|
+
// upload_file videos permanently transcript-less, which wedged the
|
|
256
|
+
// slot auto-render pipeline for audio videos (hasAudio + no
|
|
257
|
+
// .analysis.gpt4otrans.json blocks findEligibleRenderActions forever).
|
|
258
|
+
// Tests stay deterministic via process-video's NODE_ENV gate.
|
|
259
|
+
const payload = {
|
|
260
|
+
type: "process_video",
|
|
261
|
+
videoPath,
|
|
262
|
+
assetId,
|
|
263
|
+
assetDir: assetPath,
|
|
264
|
+
bookId: args.bookId,
|
|
265
|
+
provenance,
|
|
266
|
+
};
|
|
267
|
+
processingError = await runProcessingJob(jobId, payload, args.waitForProcessing);
|
|
268
|
+
}
|
|
269
|
+
else if (assetKind === "image") {
|
|
270
|
+
const jobId = `process-image-${assetId}-${Date.now()}`;
|
|
271
|
+
const payload = {
|
|
272
|
+
type: "process_image",
|
|
273
|
+
assetId,
|
|
274
|
+
bookId: args.bookId,
|
|
275
|
+
extension: ext,
|
|
276
|
+
provenance,
|
|
277
|
+
};
|
|
278
|
+
processingError = await runProcessingJob(jobId, payload, args.waitForProcessing);
|
|
279
|
+
}
|
|
280
|
+
else if (assetKind === "audio") {
|
|
281
|
+
const audioPath = path.join(assetPath, writtenFilename);
|
|
282
|
+
const jobId = `process-audio-${assetId}-${Date.now()}`;
|
|
283
|
+
const payload = {
|
|
284
|
+
type: "process_audio",
|
|
285
|
+
audioPath,
|
|
286
|
+
sourceFilename: writtenFilename,
|
|
287
|
+
assetId,
|
|
288
|
+
assetDir: assetPath,
|
|
289
|
+
bookId: args.bookId,
|
|
290
|
+
provenance,
|
|
291
|
+
};
|
|
292
|
+
processingError = await runProcessingJob(jobId, payload, args.waitForProcessing);
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
await engine.metadata.artifacts.write(assetId, "original", provenance);
|
|
296
|
+
}
|
|
297
|
+
outcome = {
|
|
298
|
+
ok: true,
|
|
299
|
+
value: {
|
|
300
|
+
artifactId,
|
|
301
|
+
assetId,
|
|
302
|
+
path: assetPath,
|
|
303
|
+
filename: writtenFilename,
|
|
304
|
+
...(processingError ? { processingError } : {}),
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
finally {
|
|
310
|
+
// deleteAsset refuses while the lock is held, so always release before
|
|
311
|
+
// any cleanup branch below runs.
|
|
312
|
+
engine.jobs.locks.release(assetPath);
|
|
313
|
+
}
|
|
314
|
+
if (!outcome.ok && isNewAsset) {
|
|
315
|
+
await engine.artifacts.delete(assetId).catch(() => { });
|
|
316
|
+
}
|
|
317
|
+
return outcome;
|
|
318
|
+
}
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { diagnosticRecord } from "./diagnostics.js";
|
|
2
|
+
const LEVEL_RANK = {
|
|
3
|
+
silent: 0,
|
|
4
|
+
error: 1,
|
|
5
|
+
warn: 2,
|
|
6
|
+
info: 3,
|
|
7
|
+
debug: 4,
|
|
8
|
+
};
|
|
9
|
+
const consoleSink = {
|
|
10
|
+
error: (...args) => {
|
|
11
|
+
console.error(...args);
|
|
12
|
+
},
|
|
13
|
+
warn: (...args) => {
|
|
14
|
+
console.warn(...args);
|
|
15
|
+
},
|
|
16
|
+
info: (...args) => {
|
|
17
|
+
console.log(...args);
|
|
18
|
+
},
|
|
19
|
+
debug: (...args) => {
|
|
20
|
+
console.debug(...args);
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
function parseLevel(value) {
|
|
24
|
+
if (!value)
|
|
25
|
+
return null;
|
|
26
|
+
const normalized = value.trim().toLowerCase();
|
|
27
|
+
if (normalized in LEVEL_RANK)
|
|
28
|
+
return normalized;
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
export function resolveLogLevel(env = process.env) {
|
|
32
|
+
const explicit = parseLevel(env.VIDEOBOOK_LOG_LEVEL);
|
|
33
|
+
if (explicit)
|
|
34
|
+
return explicit;
|
|
35
|
+
if (env.VITEST && env.VIDEOBOOK_TEST_LOG !== "1")
|
|
36
|
+
return "error";
|
|
37
|
+
return "info";
|
|
38
|
+
}
|
|
39
|
+
let currentLevel = resolveLogLevel();
|
|
40
|
+
let currentSink = consoleSink;
|
|
41
|
+
export function configureLoggerForTests(options) {
|
|
42
|
+
if (options.level !== undefined)
|
|
43
|
+
currentLevel = options.level;
|
|
44
|
+
if (options.sink !== undefined)
|
|
45
|
+
currentSink = options.sink;
|
|
46
|
+
}
|
|
47
|
+
export function resetLoggerForTests() {
|
|
48
|
+
currentLevel = resolveLogLevel();
|
|
49
|
+
currentSink = consoleSink;
|
|
50
|
+
}
|
|
51
|
+
function enabled(level) {
|
|
52
|
+
return LEVEL_RANK[currentLevel] >= LEVEL_RANK[level];
|
|
53
|
+
}
|
|
54
|
+
function emit(level, event, fields) {
|
|
55
|
+
if (!enabled(level))
|
|
56
|
+
return;
|
|
57
|
+
const record = diagnosticRecord(event, fields);
|
|
58
|
+
if (record.event === "server-listening" && typeof record.port === "number") {
|
|
59
|
+
currentSink[level](`MCP server listening on http://127.0.0.1:${record.port}/mcp`);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
currentSink[level](`[${record.event}] ${JSON.stringify(record)}`);
|
|
63
|
+
}
|
|
64
|
+
export const logger = {
|
|
65
|
+
error: (event, fields) => emit("error", event, fields),
|
|
66
|
+
warn: (event, fields) => emit("warn", event, fields),
|
|
67
|
+
info: (event, fields) => emit("info", event, fields),
|
|
68
|
+
debug: (event, fields) => emit("debug", event, fields),
|
|
69
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { createAlibaba } from "../../usage-providers.js";
|
|
2
|
+
import { requireApiKey } from "../../api-key.js";
|
|
3
|
+
import { uploadAssetToAlibaba } from "../../jobs/_alibaba-upload.js";
|
|
4
|
+
async function submit(request, _context) {
|
|
5
|
+
const provider = createAlibaba({ apiKey: requireApiKey("DASHSCOPE_API_KEY"), timeout: 120_000 });
|
|
6
|
+
const response = await provider.post.api.v1.services.aigc.multimodalGeneration.generation(request);
|
|
7
|
+
const urls = response.output.choices.flatMap((choice) => choice.message.content.flatMap((part) => part.image ? [part.image] : []));
|
|
8
|
+
if (urls.length === 0)
|
|
9
|
+
throw new Error("Alibaba response contained no image URLs");
|
|
10
|
+
return { kind: "done", taskId: response.request_id, outputs: urls.map((url) => ({ url })) };
|
|
11
|
+
}
|
|
12
|
+
export function alibabaAdapterFor(apiId) {
|
|
13
|
+
return {
|
|
14
|
+
provider: "alibaba",
|
|
15
|
+
envKey: "DASHSCOPE_API_KEY",
|
|
16
|
+
upload: (engine, assetId, filename) => uploadAssetToAlibaba(engine, assetId, filename, apiId),
|
|
17
|
+
uploadUrl: async (url) => url,
|
|
18
|
+
submit,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { createElevenLabs } from "../../usage-providers.js";
|
|
4
|
+
import { requireApiKey } from "../../api-key.js";
|
|
5
|
+
import { providerEndpoints } from "../endpoints.js";
|
|
6
|
+
const ASSET_SCHEME = "asset://";
|
|
7
|
+
function elevenLabsProvider() {
|
|
8
|
+
const baseURL = providerEndpoints().elevenlabsBaseURL;
|
|
9
|
+
return createElevenLabs({
|
|
10
|
+
apiKey: requireApiKey("ELEVENLABS_API_KEY"),
|
|
11
|
+
timeout: 10 * 60_000,
|
|
12
|
+
...(baseURL ? { baseURL } : {}),
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
async function readAssetBlob(engine, ref) {
|
|
16
|
+
const [assetId, ...rest] = ref.slice(ASSET_SCHEME.length).split("/");
|
|
17
|
+
const dir = await engine.workspaces.resolveArtifact(assetId);
|
|
18
|
+
if (!dir.ok)
|
|
19
|
+
throw new Error(dir.error.message);
|
|
20
|
+
const bytes = await readFile(join(dir.value, rest.join("/")));
|
|
21
|
+
return new Blob([bytes], { type: "video/mp4" });
|
|
22
|
+
}
|
|
23
|
+
async function videoToMusic(request, context) {
|
|
24
|
+
const refs = Array.isArray(request.videos) ? request.videos.map(String) : [];
|
|
25
|
+
const videos = await Promise.all(refs.map((ref) => readAssetBlob(context.engine, ref)));
|
|
26
|
+
return elevenLabsProvider().v1.music.videoToMusic({ ...request, videos }, context.ctx.signal);
|
|
27
|
+
}
|
|
28
|
+
function generate(request, context) {
|
|
29
|
+
const provider = elevenLabsProvider();
|
|
30
|
+
const signal = context.ctx.signal;
|
|
31
|
+
switch (context.model.apiId) {
|
|
32
|
+
case "sound-generation":
|
|
33
|
+
return provider.v1.soundGeneration(request, signal);
|
|
34
|
+
case "music":
|
|
35
|
+
return provider.v1.music(request, signal);
|
|
36
|
+
case "video-to-music":
|
|
37
|
+
return videoToMusic(request, context);
|
|
38
|
+
case "text-to-speech": {
|
|
39
|
+
const { voice_id: voiceId, ...body } = request;
|
|
40
|
+
return provider.v1.textToSpeech(String(voiceId), body, signal);
|
|
41
|
+
}
|
|
42
|
+
default:
|
|
43
|
+
throw new Error(`Unsupported ElevenLabs model ${context.model.apiId}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async function submit(request, context) {
|
|
47
|
+
const audio = await generate(request, context);
|
|
48
|
+
return { kind: "done", taskId: `${context.model.apiId}-${Date.now()}`, outputs: [{ bytes: Buffer.from(audio), extension: "mp3" }] };
|
|
49
|
+
}
|
|
50
|
+
export const elevenLabsAdapter = {
|
|
51
|
+
provider: "elevenlabs",
|
|
52
|
+
envKey: "ELEVENLABS_API_KEY",
|
|
53
|
+
upload: async (_engine, assetId, filename) => `${ASSET_SCHEME}${assetId}/${filename}`,
|
|
54
|
+
uploadUrl: async (url) => {
|
|
55
|
+
throw new Error(`ElevenLabs needs a project asset, not a URL: ${url}`);
|
|
56
|
+
},
|
|
57
|
+
submit,
|
|
58
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { createFal } from "../../usage-providers.js";
|
|
2
|
+
import { requireApiKey } from "../../api-key.js";
|
|
3
|
+
import { uploadAssetToFal } from "../../jobs/_fal-upload.js";
|
|
4
|
+
import { describeFalError } from "../../jobs/_fal-shared.js";
|
|
5
|
+
import { withProviderHandoff } from "../../jobs/_lease.js";
|
|
6
|
+
import { providerEndpoints } from "../endpoints.js";
|
|
7
|
+
import { writePendingTask } from "./pending.js";
|
|
8
|
+
import { MEDIA_TASK_TYPES } from "./types.js";
|
|
9
|
+
function falProvider() {
|
|
10
|
+
const queueBaseURL = providerEndpoints().falQueueBaseURL;
|
|
11
|
+
return createFal({
|
|
12
|
+
apiKey: requireApiKey("FAL_API_KEY"),
|
|
13
|
+
timeout: 60_000,
|
|
14
|
+
...(queueBaseURL ? { queueBaseURL } : {}),
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
async function submitQueued(request, context, ownerId) {
|
|
18
|
+
const endpointId = context.model.apiId;
|
|
19
|
+
let submitted;
|
|
20
|
+
try {
|
|
21
|
+
submitted = await falProvider().post.v1.queue.submit({ endpoint_id: endpointId, input: request });
|
|
22
|
+
}
|
|
23
|
+
catch (cause) {
|
|
24
|
+
const message = describeFalError(cause);
|
|
25
|
+
if (cause instanceof Error && message === cause.message)
|
|
26
|
+
throw cause;
|
|
27
|
+
throw new Error(message, { cause });
|
|
28
|
+
}
|
|
29
|
+
await writePendingTask(context, submitted.request_id, MEDIA_TASK_TYPES.fal, { falEndpointId: endpointId }, ownerId);
|
|
30
|
+
return submitted.request_id;
|
|
31
|
+
}
|
|
32
|
+
async function submit(request, context) {
|
|
33
|
+
const taskId = await withProviderHandoff(context.engine, context.assetId, context.ctx, "generate", async (ownerId) => {
|
|
34
|
+
const id = await submitQueued(request, context, ownerId);
|
|
35
|
+
return { taskId: id, result: id };
|
|
36
|
+
});
|
|
37
|
+
return { kind: "pending", taskId };
|
|
38
|
+
}
|
|
39
|
+
export const falAdapter = {
|
|
40
|
+
provider: "fal",
|
|
41
|
+
envKey: "FAL_API_KEY",
|
|
42
|
+
upload: uploadAssetToFal,
|
|
43
|
+
uploadUrl: async (url) => url,
|
|
44
|
+
submit,
|
|
45
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { alibabaAdapterFor } from "./alibaba.js";
|
|
2
|
+
import { elevenLabsAdapter } from "./elevenlabs.js";
|
|
3
|
+
import { falAdapter } from "./fal.js";
|
|
4
|
+
import { kieAdapter } from "./kie.js";
|
|
5
|
+
import { xaiAdapter } from "./xai.js";
|
|
6
|
+
export function adapterFor(model) {
|
|
7
|
+
switch (model.provider) {
|
|
8
|
+
case "kie":
|
|
9
|
+
return kieAdapter;
|
|
10
|
+
case "fal":
|
|
11
|
+
return falAdapter;
|
|
12
|
+
case "xai":
|
|
13
|
+
return xaiAdapter;
|
|
14
|
+
case "alibaba":
|
|
15
|
+
return alibabaAdapterFor(model.apiId);
|
|
16
|
+
case "elevenlabs":
|
|
17
|
+
return elevenLabsAdapter;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export const PROVIDER_ENV_KEYS = {
|
|
21
|
+
kie: "KIE_API_KEY",
|
|
22
|
+
fal: "FAL_API_KEY",
|
|
23
|
+
xai: "XAI_API_KEY",
|
|
24
|
+
alibaba: "DASHSCOPE_API_KEY",
|
|
25
|
+
elevenlabs: "ELEVENLABS_API_KEY",
|
|
26
|
+
};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
2
|
+
import { KieError, submitMediaJob } from "@apicity/kie";
|
|
3
|
+
import { createKie } from "../../usage-providers.js";
|
|
4
|
+
import { requireApiKey } from "../../api-key.js";
|
|
5
|
+
import { authorizeKiePaygate, ensurePaygateSecret, mintKieOtp } from "../../jobs/kie-paygate.js";
|
|
6
|
+
import { uploadAssetToKie } from "../../jobs/_kie-upload.js";
|
|
7
|
+
import { providerEndpoints } from "../endpoints.js";
|
|
8
|
+
import { isKieSunoModel, isKieVeoModel } from "../wire.js";
|
|
9
|
+
import { nonEmpty, writePendingTask } from "./pending.js";
|
|
10
|
+
import { MEDIA_TASK_TYPES } from "./types.js";
|
|
11
|
+
const VEO_DOT_PATH = "api.v1.veo.generate";
|
|
12
|
+
const SUNO_DOT_PATH = "api.v1.generate";
|
|
13
|
+
const SUNO_FAILURES = new Set(["CREATE_TASK_FAILED", "GENERATE_AUDIO_FAILED", "SENSITIVE_WORD_ERROR"]);
|
|
14
|
+
const SUNO_DONE = new Set(["SUCCESS", "FIRST_SUCCESS", "CALLBACK_EXCEPTION"]);
|
|
15
|
+
function kieProvider() {
|
|
16
|
+
const baseURL = providerEndpoints().kieBaseURL;
|
|
17
|
+
return createKie({
|
|
18
|
+
apiKey: requireApiKey("KIE_API_KEY"),
|
|
19
|
+
timeout: 60_000,
|
|
20
|
+
paygate: { secret: ensurePaygateSecret() },
|
|
21
|
+
...(baseURL ? { baseURL } : {}),
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
function correlation(context, stage) {
|
|
25
|
+
return { bookId: context.bookId, artifactId: context.assetId, tool: context.model.id, stage };
|
|
26
|
+
}
|
|
27
|
+
async function recordTaskId(context, taskId) {
|
|
28
|
+
await context.engine.metadata.artifacts.write(context.assetId, "kie-task-id", { taskId });
|
|
29
|
+
}
|
|
30
|
+
async function submitCreateTask(request, context) {
|
|
31
|
+
const typed = request;
|
|
32
|
+
if (Object.keys(context.costHints).length > 0) {
|
|
33
|
+
await context.engine.metadata.artifacts.write(context.assetId, "generation-cost-hints", context.costHints);
|
|
34
|
+
}
|
|
35
|
+
const { otp } = await authorizeKiePaygate(typed, {
|
|
36
|
+
costHints: {
|
|
37
|
+
...(context.costHints.durationSeconds !== undefined ? { duration: context.costHints.durationSeconds } : {}),
|
|
38
|
+
...(context.costHints.inputDurationSeconds !== undefined ? { inputDuration: context.costHints.inputDurationSeconds } : {}),
|
|
39
|
+
},
|
|
40
|
+
engine: context.engine,
|
|
41
|
+
correlation: correlation(context, "submit-kie"),
|
|
42
|
+
});
|
|
43
|
+
const taskId = await submitMediaJob(kieProvider(), typed, { otp });
|
|
44
|
+
await writePendingTask(context, taskId, MEDIA_TASK_TYPES.kie);
|
|
45
|
+
await recordTaskId(context, taskId);
|
|
46
|
+
return { kind: "pending", taskId };
|
|
47
|
+
}
|
|
48
|
+
async function submitVeo(request, context) {
|
|
49
|
+
const typed = request;
|
|
50
|
+
const { otp } = await authorizeKiePaygate(typed, {
|
|
51
|
+
dotPath: VEO_DOT_PATH,
|
|
52
|
+
engine: context.engine,
|
|
53
|
+
correlation: correlation(context, "submit-veo"),
|
|
54
|
+
});
|
|
55
|
+
const result = await kieProvider().veo.post.api.v1.veo.generate(typed, { otp });
|
|
56
|
+
const taskId = nonEmpty(result.data?.taskId);
|
|
57
|
+
if (!taskId)
|
|
58
|
+
throw new KieError(`veo.generate failed: ${result.code}`, result.code);
|
|
59
|
+
await writePendingTask(context, taskId, MEDIA_TASK_TYPES.kie, { veoModel: context.model.apiId });
|
|
60
|
+
await recordTaskId(context, taskId);
|
|
61
|
+
return { kind: "pending", taskId };
|
|
62
|
+
}
|
|
63
|
+
function sunoTrackUrl(tracks) {
|
|
64
|
+
for (const track of tracks ?? []) {
|
|
65
|
+
const url = nonEmpty(track.audioUrl);
|
|
66
|
+
if (url)
|
|
67
|
+
return url;
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
async function submitSunoTask(provider, request, apiId) {
|
|
72
|
+
if (apiId === "suno/sounds-generate") {
|
|
73
|
+
const response = await provider.suno.post.api.v1.generate.sounds(request);
|
|
74
|
+
const taskId = nonEmpty(response.data?.taskId);
|
|
75
|
+
if (!taskId)
|
|
76
|
+
throw new Error(`Suno sounds submission failed: ${response.msg ?? response.code}`);
|
|
77
|
+
return taskId;
|
|
78
|
+
}
|
|
79
|
+
const otp = mintKieOtp(request, undefined, SUNO_DOT_PATH);
|
|
80
|
+
const response = await provider.suno.post.api.v1.generate(request, { otp });
|
|
81
|
+
const taskId = nonEmpty(response.data?.taskId);
|
|
82
|
+
if (!taskId)
|
|
83
|
+
throw new Error(`Suno submission failed: ${response.msg ?? response.code}`);
|
|
84
|
+
return taskId;
|
|
85
|
+
}
|
|
86
|
+
async function pollSuno(provider, taskId, signal) {
|
|
87
|
+
const settings = providerEndpoints();
|
|
88
|
+
const deadline = Date.now() + (settings.timeoutMs ?? 25 * 60_000);
|
|
89
|
+
while (Date.now() < deadline) {
|
|
90
|
+
const result = await provider.suno.get.api.v1.generate.recordInfo(taskId);
|
|
91
|
+
const status = result.data?.status;
|
|
92
|
+
const url = sunoTrackUrl(result.data?.response?.sunoData);
|
|
93
|
+
if (url && status && SUNO_DONE.has(status))
|
|
94
|
+
return url;
|
|
95
|
+
if (status && SUNO_FAILURES.has(status))
|
|
96
|
+
throw new Error(`Suno generation failed: ${result.data?.errorMessage || status}`);
|
|
97
|
+
await delay(settings.pollIntervalMs ?? 3_000, undefined, { signal });
|
|
98
|
+
}
|
|
99
|
+
throw new Error(`Suno task ${taskId} did not finish in time`);
|
|
100
|
+
}
|
|
101
|
+
async function submitSuno(request, context) {
|
|
102
|
+
const provider = kieProvider();
|
|
103
|
+
const taskId = await submitSunoTask(provider, request, context.model.apiId);
|
|
104
|
+
await recordTaskId(context, taskId);
|
|
105
|
+
const url = await pollSuno(provider, taskId, context.ctx.signal);
|
|
106
|
+
return { kind: "done", taskId, outputs: [{ url }] };
|
|
107
|
+
}
|
|
108
|
+
export const kieAdapter = {
|
|
109
|
+
provider: "kie",
|
|
110
|
+
envKey: "KIE_API_KEY",
|
|
111
|
+
upload: uploadAssetToKie,
|
|
112
|
+
uploadUrl: async (url) => url,
|
|
113
|
+
submit: (request, context) => {
|
|
114
|
+
if (isKieVeoModel(context.model.apiId))
|
|
115
|
+
return submitVeo(request, context);
|
|
116
|
+
if (isKieSunoModel(context.model.apiId))
|
|
117
|
+
return submitSuno(request, context);
|
|
118
|
+
return submitCreateTask(request, context);
|
|
119
|
+
},
|
|
120
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export async function writePendingTask(context, taskId, taskType, extraMeta = {}, ownerId = context.ctx.getOwnerId()) {
|
|
2
|
+
const written = await context.engine.jobs.pending.write({
|
|
3
|
+
taskId,
|
|
4
|
+
taskType,
|
|
5
|
+
artifactId: context.assetId,
|
|
6
|
+
meta: { ...context.meta, ...extraMeta },
|
|
7
|
+
}, ownerId);
|
|
8
|
+
if (!written.ok)
|
|
9
|
+
throw new Error(`Failed to write pending-task for ${context.assetId}: ${written.error.message}`);
|
|
10
|
+
if (written.value === null)
|
|
11
|
+
throw new Error(`Lost local lease for ${context.assetId} while writing pending-task`);
|
|
12
|
+
context.ctx.setLease({ kind: "task", taskId });
|
|
13
|
+
}
|
|
14
|
+
export function record(value) {
|
|
15
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
16
|
+
? value
|
|
17
|
+
: null;
|
|
18
|
+
}
|
|
19
|
+
export function nonEmpty(value) {
|
|
20
|
+
return typeof value === "string" && value.trim() !== "" ? value.trim() : null;
|
|
21
|
+
}
|