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,200 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
export const OP_VAULT_SETTING = "VIDEOBOOK_OP_VAULT";
|
|
7
|
+
export const OP_TOKEN_SETTING = "VIDEOBOOK_OP_SERVICE_TOKEN";
|
|
8
|
+
const OP_TIMEOUT_MS = 10_000;
|
|
9
|
+
const TEMPLATE_UNSAFE = /[\r\n]|\{\{|\}\}/u;
|
|
10
|
+
const ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/u;
|
|
11
|
+
export function isOpReference(value) {
|
|
12
|
+
return typeof value === "string" && value.trim().startsWith("op://");
|
|
13
|
+
}
|
|
14
|
+
export function vaultReference(vault, key) {
|
|
15
|
+
return `op://${vault}/${key}/password`;
|
|
16
|
+
}
|
|
17
|
+
export function resolveServiceToken(raw, env) {
|
|
18
|
+
const value = raw?.trim();
|
|
19
|
+
if (!value)
|
|
20
|
+
return { value: null, source: null };
|
|
21
|
+
const name = value.startsWith("env:") ? value.slice(4) : value.startsWith("$") ? value.slice(1) : value;
|
|
22
|
+
if (value !== name || /^[A-Z_][A-Z0-9_]*$/u.test(value)) {
|
|
23
|
+
if (!ENV_NAME.test(name))
|
|
24
|
+
return { value: null, source: `env:${name}` };
|
|
25
|
+
return { value: env[name]?.trim() || null, source: `env:${name}` };
|
|
26
|
+
}
|
|
27
|
+
return { value, source: "literal" };
|
|
28
|
+
}
|
|
29
|
+
export function readOnePasswordSettings(env) {
|
|
30
|
+
return {
|
|
31
|
+
vault: env[OP_VAULT_SETTING]?.trim() || null,
|
|
32
|
+
token: resolveServiceToken(env[OP_TOKEN_SETTING], env),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export function redactToken(text, token) {
|
|
36
|
+
return token ? text.split(token).join("***") : text;
|
|
37
|
+
}
|
|
38
|
+
export function planReferences(env, managedKeys, vault, vaultTitles) {
|
|
39
|
+
const explicit = Object.entries(env)
|
|
40
|
+
.filter((entry) => isOpReference(entry[1]))
|
|
41
|
+
.map(([key, reference]) => [key, reference.trim()]);
|
|
42
|
+
const covered = new Set(explicit.map(([key]) => key));
|
|
43
|
+
const convention = vault
|
|
44
|
+
? managedKeys
|
|
45
|
+
.filter((key) => !covered.has(key) && !env[key]?.trim() && vaultTitles.has(key))
|
|
46
|
+
.map((key) => [key, vaultReference(vault, key)])
|
|
47
|
+
: [];
|
|
48
|
+
return { explicit, convention };
|
|
49
|
+
}
|
|
50
|
+
function startMarker(nonce, key) {
|
|
51
|
+
return `<<<${nonce}:${key}>>>`;
|
|
52
|
+
}
|
|
53
|
+
function endMarker(nonce, key) {
|
|
54
|
+
return `<<<${nonce}/${key}>>>`;
|
|
55
|
+
}
|
|
56
|
+
export function injectionTemplate(entries, nonce) {
|
|
57
|
+
return entries.map(([key, reference]) => `${startMarker(nonce, key)}{{ ${reference} }}${endMarker(nonce, key)}`).join("\n");
|
|
58
|
+
}
|
|
59
|
+
export function parseInjection(output, keys, nonce) {
|
|
60
|
+
const values = new Map();
|
|
61
|
+
for (const key of keys) {
|
|
62
|
+
const start = output.indexOf(startMarker(nonce, key));
|
|
63
|
+
if (start < 0)
|
|
64
|
+
continue;
|
|
65
|
+
const from = start + startMarker(nonce, key).length;
|
|
66
|
+
const end = output.indexOf(endMarker(nonce, key), from);
|
|
67
|
+
if (end < 0)
|
|
68
|
+
continue;
|
|
69
|
+
const value = output.slice(from, end);
|
|
70
|
+
if (value !== "")
|
|
71
|
+
values.set(key, value);
|
|
72
|
+
}
|
|
73
|
+
return values;
|
|
74
|
+
}
|
|
75
|
+
function opEnvironment(env, token) {
|
|
76
|
+
return token ? { ...env, OP_SERVICE_ACCOUNT_TOKEN: token } : env;
|
|
77
|
+
}
|
|
78
|
+
export function opErrorMessage(error, token) {
|
|
79
|
+
const failure = error;
|
|
80
|
+
if (failure.code === "ENOENT")
|
|
81
|
+
return "the 1Password CLI (op) is not on PATH";
|
|
82
|
+
if (failure.code === "ETIMEDOUT" || failure.signal === "SIGTERM")
|
|
83
|
+
return "op timed out";
|
|
84
|
+
const line = String(failure.stderr ?? "").trim().split("\n")[0]?.trim().replace(/^\[ERROR\]\s+\S+\s+\S+\s+/u, "");
|
|
85
|
+
return redactToken(line || failure.message || "op failed", token);
|
|
86
|
+
}
|
|
87
|
+
function runOp(args, env, token) {
|
|
88
|
+
return execFileSync("op", args, {
|
|
89
|
+
env: opEnvironment(env, token),
|
|
90
|
+
encoding: "utf8",
|
|
91
|
+
timeout: OP_TIMEOUT_MS,
|
|
92
|
+
maxBuffer: 8 * 1024 * 1024,
|
|
93
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
export function listVaultItemTitles(vault, env, token) {
|
|
97
|
+
const parsed = JSON.parse(runOp(["item", "list", "--vault", vault, "--format", "json"], env, token));
|
|
98
|
+
if (!Array.isArray(parsed))
|
|
99
|
+
throw new Error("op item list did not return a list");
|
|
100
|
+
return new Set(parsed.flatMap((item) => {
|
|
101
|
+
const title = item && typeof item === "object" ? item.title : undefined;
|
|
102
|
+
return typeof title === "string" ? [title] : [];
|
|
103
|
+
}));
|
|
104
|
+
}
|
|
105
|
+
function injectBatch(entries, env, token) {
|
|
106
|
+
const nonce = randomUUID().replaceAll("-", "");
|
|
107
|
+
const directory = mkdtempSync(join(tmpdir(), "videobook-op-"));
|
|
108
|
+
try {
|
|
109
|
+
const template = join(directory, "template");
|
|
110
|
+
writeFileSync(template, injectionTemplate(entries, nonce), { mode: 0o600 });
|
|
111
|
+
const output = runOp(["inject", "--in-file", template], env, token);
|
|
112
|
+
return parseInjection(output, entries.map(([key]) => key), nonce);
|
|
113
|
+
}
|
|
114
|
+
finally {
|
|
115
|
+
rmSync(directory, { recursive: true, force: true });
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function readOne(key, reference, env, token) {
|
|
119
|
+
try {
|
|
120
|
+
const value = runOp(["read", "--no-newline", reference], env, token);
|
|
121
|
+
return value === ""
|
|
122
|
+
? { values: new Map(), failures: [{ key, reference, reason: "the item has no value" }] }
|
|
123
|
+
: { values: new Map([[key, value]]), failures: [] };
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
return { values: new Map(), failures: [{ key, reference, reason: opErrorMessage(error, token) }] };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function mergeResults(results) {
|
|
130
|
+
return {
|
|
131
|
+
values: new Map(results.flatMap((result) => [...result.values])),
|
|
132
|
+
failures: results.flatMap((result) => result.failures),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function resolveEntries(entries, env, token) {
|
|
136
|
+
const unsafe = entries.filter(([, reference]) => TEMPLATE_UNSAFE.test(reference));
|
|
137
|
+
const safe = entries.filter(([, reference]) => !TEMPLATE_UNSAFE.test(reference));
|
|
138
|
+
const refused = unsafe.map(([key, reference]) => ({ key, reference, reason: "malformed op:// reference" }));
|
|
139
|
+
if (safe.length === 0)
|
|
140
|
+
return { values: new Map(), failures: refused };
|
|
141
|
+
try {
|
|
142
|
+
const values = injectBatch(safe, env, token);
|
|
143
|
+
const missing = safe.filter(([key]) => !values.has(key)).map(([key, reference]) => ({ key, reference, reason: "op inject returned no value" }));
|
|
144
|
+
return { values, failures: [...refused, ...missing] };
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
const individual = mergeResults(safe.map(([key, reference]) => readOne(key, reference, env, token)));
|
|
148
|
+
return { values: individual.values, failures: [...refused, ...individual.failures] };
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
function vaultTitles(settings, env) {
|
|
152
|
+
if (!settings.vault)
|
|
153
|
+
return { titles: new Set(), error: null };
|
|
154
|
+
try {
|
|
155
|
+
return { titles: listVaultItemTitles(settings.vault, env, settings.token.value), error: null };
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
return { titles: new Set(), error: `could not list vault ${settings.vault}: ${opErrorMessage(error, settings.token.value)}` };
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function emptyResolution(settings) {
|
|
162
|
+
return {
|
|
163
|
+
attempted: false,
|
|
164
|
+
vault: settings.vault,
|
|
165
|
+
tokenSource: settings.token.source,
|
|
166
|
+
fromReferences: [],
|
|
167
|
+
fromVault: [],
|
|
168
|
+
unresolved: [],
|
|
169
|
+
error: null,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
export function resolveOnePasswordEnv(env, managedKeys) {
|
|
173
|
+
const settings = readOnePasswordSettings(env);
|
|
174
|
+
const hasReferences = Object.values(env).some((value) => isOpReference(value));
|
|
175
|
+
if (!hasReferences && !settings.vault)
|
|
176
|
+
return emptyResolution(settings);
|
|
177
|
+
const listing = vaultTitles(settings, env);
|
|
178
|
+
const plan = planReferences(env, managedKeys, settings.vault, listing.titles);
|
|
179
|
+
const result = resolveEntries([...plan.explicit, ...plan.convention], env, settings.token.value);
|
|
180
|
+
for (const [key] of plan.explicit) {
|
|
181
|
+
const value = result.values.get(key);
|
|
182
|
+
if (value === undefined)
|
|
183
|
+
delete env[key];
|
|
184
|
+
else
|
|
185
|
+
env[key] = value;
|
|
186
|
+
}
|
|
187
|
+
for (const [key] of plan.convention) {
|
|
188
|
+
const value = result.values.get(key);
|
|
189
|
+
if (value !== undefined)
|
|
190
|
+
env[key] = value;
|
|
191
|
+
}
|
|
192
|
+
return {
|
|
193
|
+
...emptyResolution(settings),
|
|
194
|
+
attempted: true,
|
|
195
|
+
fromReferences: plan.explicit.map(([key]) => key).filter((key) => result.values.has(key)),
|
|
196
|
+
fromVault: plan.convention.map(([key]) => key).filter((key) => result.values.has(key)),
|
|
197
|
+
unresolved: result.failures,
|
|
198
|
+
error: listing.error,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { createOpenAi } from "./usage-providers.js";
|
|
2
|
+
import { withRetry } from "@apicity/openai";
|
|
3
|
+
export const INTELLIGENCE_MODEL = "gpt-5.6-sol";
|
|
4
|
+
export const INTELLIGENCE_REASONING_EFFORT = "medium";
|
|
5
|
+
export const OPENAI_ANALYSIS_TOOL = "openai_responses_analysis";
|
|
6
|
+
export const ANALYSIS_REASONING_EFFORT = "high";
|
|
7
|
+
export function intelligenceRequest(request, options = {}) {
|
|
8
|
+
return {
|
|
9
|
+
...request,
|
|
10
|
+
model: options.model ?? INTELLIGENCE_MODEL,
|
|
11
|
+
reasoning: {
|
|
12
|
+
effort: options.reasoningEffort ?? INTELLIGENCE_REASONING_EFFORT,
|
|
13
|
+
},
|
|
14
|
+
store: false,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function normalizeRequest(request, options) {
|
|
18
|
+
const normalized = intelligenceRequest(request, {
|
|
19
|
+
model: options.model,
|
|
20
|
+
reasoningEffort: request.reasoning?.effort
|
|
21
|
+
?? options.reasoningEffort
|
|
22
|
+
?? INTELLIGENCE_REASONING_EFFORT,
|
|
23
|
+
});
|
|
24
|
+
delete normalized.temperature;
|
|
25
|
+
delete normalized.top_p;
|
|
26
|
+
return normalized;
|
|
27
|
+
}
|
|
28
|
+
export function buildIntelligenceResponses(apiKey, options = {}) {
|
|
29
|
+
if (!apiKey) {
|
|
30
|
+
throw new Error("OPENAI_API_KEY is not set");
|
|
31
|
+
}
|
|
32
|
+
const timeout = options.timeout ?? 180_000;
|
|
33
|
+
const provider = createOpenAi({
|
|
34
|
+
apiKey,
|
|
35
|
+
timeout,
|
|
36
|
+
...(options.fetch ? { fetch: options.fetch } : {}),
|
|
37
|
+
...(options.baseURL ? { baseURL: options.baseURL } : {}),
|
|
38
|
+
});
|
|
39
|
+
const call = withRetry(provider.post.v1.responses, { retries: options.retries ?? 2, baseMs: 500 });
|
|
40
|
+
return async (request, signal) => {
|
|
41
|
+
const deadline = new AbortController();
|
|
42
|
+
const timer = setTimeout(() => deadline.abort(new Error(`OpenAI Responses request exceeded ${timeout}ms`)), timeout);
|
|
43
|
+
const combinedSignal = signal
|
|
44
|
+
? AbortSignal.any([signal, deadline.signal])
|
|
45
|
+
: deadline.signal;
|
|
46
|
+
try {
|
|
47
|
+
return await call(normalizeRequest(request, {
|
|
48
|
+
model: options.model,
|
|
49
|
+
reasoningEffort: options.reasoningEffort,
|
|
50
|
+
}), combinedSignal);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
if (signal?.aborted) {
|
|
54
|
+
throw signal.reason instanceof Error
|
|
55
|
+
? signal.reason
|
|
56
|
+
: new Error("OpenAI Responses request was cancelled");
|
|
57
|
+
}
|
|
58
|
+
if (deadline.signal.aborted) {
|
|
59
|
+
throw deadline.signal.reason instanceof Error
|
|
60
|
+
? deadline.signal.reason
|
|
61
|
+
: new Error(`OpenAI Responses request exceeded ${timeout}ms`);
|
|
62
|
+
}
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
clearTimeout(timer);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
export function firstResponseText(response) {
|
|
71
|
+
if (response.error) {
|
|
72
|
+
throw new Error(`OpenAI Responses error ${response.error.code}: ${response.error.message}`);
|
|
73
|
+
}
|
|
74
|
+
if (response.status === "incomplete") {
|
|
75
|
+
const reason = response.incomplete_details?.reason ?? "unknown reason";
|
|
76
|
+
throw new Error(`OpenAI Responses output was incomplete: ${reason}`);
|
|
77
|
+
}
|
|
78
|
+
if (response.status !== "completed") {
|
|
79
|
+
throw new Error(`OpenAI Responses request did not complete (status=${response.status})`);
|
|
80
|
+
}
|
|
81
|
+
const texts = [];
|
|
82
|
+
for (const item of response.output) {
|
|
83
|
+
if (item.type !== "message")
|
|
84
|
+
continue;
|
|
85
|
+
for (const content of item.content) {
|
|
86
|
+
if (content.type === "refusal") {
|
|
87
|
+
throw new Error(`OpenAI Responses refused the request: ${content.refusal}`);
|
|
88
|
+
}
|
|
89
|
+
if (content.type === "output_text") {
|
|
90
|
+
texts.push(content.text);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const text = texts.join("").trim();
|
|
95
|
+
if (!text) {
|
|
96
|
+
throw new Error("OpenAI Responses returned no text content");
|
|
97
|
+
}
|
|
98
|
+
return text;
|
|
99
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { findAudioFile, findPrimaryMediaFile, findVideoFile } from "videobook-engine";
|
|
2
|
+
export function findOriginalMediaFile(files, kind) {
|
|
3
|
+
const originals = files.filter((file) => /^original\./i.test(file.name));
|
|
4
|
+
if (kind === "video")
|
|
5
|
+
return findVideoFile(originals);
|
|
6
|
+
if (kind === "audio") {
|
|
7
|
+
return findAudioFile(originals, kind)
|
|
8
|
+
?? originals.find((file) => /^original\.webm$/i.test(file.name))
|
|
9
|
+
?? null;
|
|
10
|
+
}
|
|
11
|
+
return findPrimaryMediaFile(files, kind);
|
|
12
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { join, extname } from "node:path";
|
|
2
|
+
const OSG_POSITIONS = [
|
|
3
|
+
"top-left", "top-center", "top-right",
|
|
4
|
+
"middle-left", "middle-center", "middle-right",
|
|
5
|
+
"bottom-left", "bottom-center", "bottom-right",
|
|
6
|
+
];
|
|
7
|
+
export function isOsgPosition(value) {
|
|
8
|
+
return OSG_POSITIONS.includes(value);
|
|
9
|
+
}
|
|
10
|
+
export function isOsgSize(value) {
|
|
11
|
+
return value === "small" || value === "medium" || value === "large";
|
|
12
|
+
}
|
|
13
|
+
// Wider than _image-inputs' set: OSG accepts svg since Remotion renders it in
|
|
14
|
+
// a browser, while provider uploads (the _image-inputs consumers) cannot.
|
|
15
|
+
const OSG_IMAGE_EXTENSIONS = new Set(["png", "jpg", "jpeg", "webp", "gif", "svg"]);
|
|
16
|
+
function findOsgImageFile(files) {
|
|
17
|
+
const images = files.filter((file) => {
|
|
18
|
+
const ext = (file.extension ?? extname(file.name)).replace(/^\./, "").toLowerCase();
|
|
19
|
+
return OSG_IMAGE_EXTENSIONS.has(ext);
|
|
20
|
+
});
|
|
21
|
+
// Manifests are alphabetically sorted, so a bare images[0] would pick a
|
|
22
|
+
// cover-crop preview variant (landscape.jpg) over the transparent source.
|
|
23
|
+
return images.find((file) => file.name.startsWith("original")) ?? images[0] ?? null;
|
|
24
|
+
}
|
|
25
|
+
/** Resolve an OSG image asset id to the on-disk path of its source image. */
|
|
26
|
+
export async function resolveOsgImagePath(engine, osgAssetId) {
|
|
27
|
+
const manifest = await engine.files.manifest(osgAssetId);
|
|
28
|
+
if (!manifest.ok) {
|
|
29
|
+
return { ok: false, error: `OSG asset error: ${manifest.error.message}` };
|
|
30
|
+
}
|
|
31
|
+
const imgFile = findOsgImageFile(manifest.value.files);
|
|
32
|
+
if (!imgFile) {
|
|
33
|
+
return { ok: false, error: `No image file found in OSG asset ${osgAssetId}` };
|
|
34
|
+
}
|
|
35
|
+
return { ok: true, path: join(manifest.value.path, imgFile.name) };
|
|
36
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
export const OSG_PLACEMENT_VERSION = 1;
|
|
4
|
+
const OSG_PLACEMENT_METADATA_KEY = "osg.placement";
|
|
5
|
+
const regionSchema = z.object({
|
|
6
|
+
kind: z.enum(["face", "text", "graphic"]),
|
|
7
|
+
confidence: z.number().min(0).max(1),
|
|
8
|
+
x: z.number().min(0).max(1),
|
|
9
|
+
y: z.number().min(0).max(1),
|
|
10
|
+
width: z.number().positive().max(1),
|
|
11
|
+
height: z.number().positive().max(1),
|
|
12
|
+
}).strict().refine((region) => region.x + region.width <= 1.001 && region.y + region.height <= 1.001, { message: "occupied region extends beyond the source frame" });
|
|
13
|
+
const positionSchema = z.enum([
|
|
14
|
+
"top-left", "top-center", "top-right",
|
|
15
|
+
"middle-left", "middle-center", "middle-right",
|
|
16
|
+
"bottom-left", "bottom-center", "bottom-right",
|
|
17
|
+
]);
|
|
18
|
+
const analysisSchema = z.object({
|
|
19
|
+
version: z.literal(OSG_PLACEMENT_VERSION),
|
|
20
|
+
fingerprint: z.string().min(1),
|
|
21
|
+
sourceWidth: z.number().positive(),
|
|
22
|
+
sourceHeight: z.number().positive(),
|
|
23
|
+
frameTimes: z.array(z.number().nonnegative()).min(1),
|
|
24
|
+
regions: z.array(regionSchema),
|
|
25
|
+
positions: z.object({
|
|
26
|
+
landscape: positionSchema,
|
|
27
|
+
portrait: positionSchema,
|
|
28
|
+
square: positionSchema,
|
|
29
|
+
}).strict(),
|
|
30
|
+
analyzedAt: z.string().min(1),
|
|
31
|
+
}).strict();
|
|
32
|
+
export function osgPlacementFingerprint(videoFile, frameNames, box) {
|
|
33
|
+
const payload = {
|
|
34
|
+
version: OSG_PLACEMENT_VERSION,
|
|
35
|
+
video: {
|
|
36
|
+
name: videoFile.name,
|
|
37
|
+
size: videoFile.sizeBytes,
|
|
38
|
+
objectHash: videoFile.objectHash,
|
|
39
|
+
},
|
|
40
|
+
frames: frameNames,
|
|
41
|
+
box: {
|
|
42
|
+
aspect: Number(box.aspect.toFixed(4)),
|
|
43
|
+
minDimFraction: box.minDimFraction,
|
|
44
|
+
userScale: Number(box.userScale.toFixed(2)),
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
return createHash("sha256").update(JSON.stringify(payload)).digest("hex");
|
|
48
|
+
}
|
|
49
|
+
export function parseOsgPlacementAnalysis(raw) {
|
|
50
|
+
return analysisSchema.parse(raw);
|
|
51
|
+
}
|
|
52
|
+
export async function readOsgPlacementAnalysis(engine, assetId) {
|
|
53
|
+
const result = await engine.metadata.artifacts.read(assetId, OSG_PLACEMENT_METADATA_KEY);
|
|
54
|
+
if (!result.ok)
|
|
55
|
+
return null;
|
|
56
|
+
const parsed = analysisSchema.safeParse(result.value);
|
|
57
|
+
return parsed.success ? parsed.data : null;
|
|
58
|
+
}
|
|
59
|
+
export async function writeOsgPlacementAnalysis(engine, assetId, analysis) {
|
|
60
|
+
const parsed = parseOsgPlacementAnalysis(analysis);
|
|
61
|
+
const result = await engine.metadata.artifacts.write(assetId, OSG_PLACEMENT_METADATA_KEY, parsed);
|
|
62
|
+
if (!result.ok)
|
|
63
|
+
throw new Error(`Failed to persist OSG placement analysis: ${result.error.message}`);
|
|
64
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const ORIENTATIONS = ["landscape", "portrait", "square"];
|
|
2
|
+
/**
|
|
3
|
+
* Merge OSG fields into every orientation's `.render` metadata without
|
|
4
|
+
* triggering a render. `patch` receives the orientation and its existing
|
|
5
|
+
* metadata so callers can vary fields per orientation (the frame-aware
|
|
6
|
+
* watermark stores a different corner for each) or derive defaults from the
|
|
7
|
+
* stored values.
|
|
8
|
+
*/
|
|
9
|
+
export async function writeOsgSettings(engine, assetId, patch) {
|
|
10
|
+
const settings = {};
|
|
11
|
+
try {
|
|
12
|
+
for (const orientation of ORIENTATIONS) {
|
|
13
|
+
const existing = await engine.metadata.artifacts.read(assetId, `${orientation}.render`);
|
|
14
|
+
const base = existing.ok ? existing.value : {};
|
|
15
|
+
settings[orientation] = { ...base, ...patch(orientation, base) };
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
return { ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
20
|
+
}
|
|
21
|
+
for (const orientation of ORIENTATIONS) {
|
|
22
|
+
const written = await engine.metadata.artifacts.write(assetId, `${orientation}.render`, settings[orientation]);
|
|
23
|
+
if (!written.ok)
|
|
24
|
+
return { ok: false, error: written.error.message };
|
|
25
|
+
}
|
|
26
|
+
return { ok: true, settings };
|
|
27
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tools that spend provider money: generation, analysis, transcription,
|
|
3
|
+
* tagging, and the notebook run entry points that dispatch paid cells.
|
|
4
|
+
*/
|
|
5
|
+
export const BILLABLE_TOOL_NAMES = new Set([
|
|
6
|
+
"prepare_cold_open", "analyze_video", "analyze_jokes", "suggest_cuts", "suggest_sfx", "apply_sfx",
|
|
7
|
+
"transcribe", "translate_subtitles", "isolate_vocals", "clean_video_audio",
|
|
8
|
+
"make_cover_song", "extend_audio_asset", "rewrite_prompt", "rewrite_script",
|
|
9
|
+
"describe_image", "analyze_image_subjects", "edit_image_subjects", "rename_all",
|
|
10
|
+
"retag_asset", "verify_x_mentions",
|
|
11
|
+
"backfill_asset_tags", "retry_generation", "retry_job",
|
|
12
|
+
"run_notebook", "run_notebook_cell", "enqueue_notebook_run", "continue_notebook_generation",
|
|
13
|
+
"generate_image", "generate_video", "generate_audio",
|
|
14
|
+
]);
|
|
15
|
+
/**
|
|
16
|
+
* One answer to "does running this cost the user money", shared by the workspace
|
|
17
|
+
* action catalog's confirmation policy and the chain's per-step approval gate so
|
|
18
|
+
* the two can never drift apart.
|
|
19
|
+
*/
|
|
20
|
+
export function toolSpendsMoney(name) {
|
|
21
|
+
return BILLABLE_TOOL_NAMES.has(name);
|
|
22
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function platformGuidance(p) {
|
|
2
|
+
switch (p) {
|
|
3
|
+
case "ig":
|
|
4
|
+
return ("PLATFORM: Instagram. Target 4:5 portrait or 1:1 square framing. " +
|
|
5
|
+
"Aesthetic-candid register — curated but unstaged: natural daylight or soft indoor light, " +
|
|
6
|
+
"warm/golden cast acceptable, light film grain tolerated. Mild VSCO-style lift in shadows; " +
|
|
7
|
+
"skin kept natural with no heavy beautify. Avoid HDR crunch, over-saturation, and harsh " +
|
|
8
|
+
"on-camera flash unless the image is clearly a flash-aesthetic shot. Default device read: " +
|
|
9
|
+
"iPhone main camera. Never use editorial/film-stock vocabulary unless the image is clearly " +
|
|
10
|
+
"a professional editorial photo.");
|
|
11
|
+
case "tiktok":
|
|
12
|
+
return ("PLATFORM: TikTok. Target 9:16 vertical framing. Raw phone snapshot energy — candid framing, " +
|
|
13
|
+
"slight motion softness, mild compression artifacts, auto-HDR lifted blacks. Indoor scenes: " +
|
|
14
|
+
"ring-light rim or warm tungsten acceptable; at night, harsh on-camera flash is on-brand. " +
|
|
15
|
+
"Front-camera selfies (with visible mirror/arm) are native to the platform. Skin unretouched, " +
|
|
16
|
+
"tiny chromatic aberration on high-contrast edges. Never apply editorial polish, cinematic " +
|
|
17
|
+
"color grading, or named film-stock vocabulary (Portra, Cinestill, etc.). No 'cinematic', " +
|
|
18
|
+
"'masterpiece', 'editorial' — this is a phone snapshot.");
|
|
19
|
+
case "youtube":
|
|
20
|
+
return ("PLATFORM: YouTube. Default to 16:9 landscape unless the image is clearly a Short (then 9:16). " +
|
|
21
|
+
"Slightly elevated production versus TikTok: punchier contrast, cleaner key light, faces " +
|
|
22
|
+
"expressive and readable at small thumbnail sizes. Colour can sit slightly saturated for " +
|
|
23
|
+
"thumbnail legibility but avoid over-stylised filters. Keep the result photographic rather " +
|
|
24
|
+
"than graphic-design; no logo overlays, no title text baked in.");
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
2
|
+
import { createServer } from "node:http";
|
|
3
|
+
import { createDropbox } from "@apicity/dropbox";
|
|
4
|
+
import { appState } from "../app-state.js";
|
|
5
|
+
const KEY = "dropbox.oauth.tokens";
|
|
6
|
+
const REDIRECT = "http://127.0.0.1:8766/callback";
|
|
7
|
+
let pending = false;
|
|
8
|
+
let lastError = null;
|
|
9
|
+
let closeFlow = null;
|
|
10
|
+
let refreshing = null;
|
|
11
|
+
function oauthClient() {
|
|
12
|
+
const clientId = process.env.DROPBOX_CLIENT_ID;
|
|
13
|
+
if (!clientId)
|
|
14
|
+
throw new Error("Set DROPBOX_CLIENT_ID to your Dropbox app key before connecting");
|
|
15
|
+
return createDropbox({ clientId, clientSecret: process.env.DROPBOX_CLIENT_SECRET });
|
|
16
|
+
}
|
|
17
|
+
export async function dropboxClient() {
|
|
18
|
+
if (refreshing)
|
|
19
|
+
return refreshing;
|
|
20
|
+
refreshing = (async () => {
|
|
21
|
+
const tokens = await appState.get(KEY);
|
|
22
|
+
if (!tokens)
|
|
23
|
+
throw new Error("Connect Dropbox in Settings before uploading");
|
|
24
|
+
if (new Date(tokens.expiresAt).getTime() > Date.now() + 60_000)
|
|
25
|
+
return createDropbox({ oauthToken: tokens.accessToken });
|
|
26
|
+
const refreshed = await oauthClient().oauth2.token({ grant_type: "refresh_token", refresh_token: tokens.refreshToken });
|
|
27
|
+
await appState.set(KEY, { accessToken: refreshed.access_token, refreshToken: refreshed.refresh_token ?? tokens.refreshToken, expiresAt: new Date(Date.now() + refreshed.expires_in * 1000).toISOString() });
|
|
28
|
+
return createDropbox({ oauthToken: refreshed.access_token });
|
|
29
|
+
})();
|
|
30
|
+
try {
|
|
31
|
+
return await refreshing;
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
refreshing = null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export async function dropboxStatus() {
|
|
38
|
+
const tokens = await appState.get(KEY);
|
|
39
|
+
return { connected: Boolean(tokens), pending, has_credentials: Boolean(process.env.DROPBOX_CLIENT_ID), error: lastError, redirect_uri: REDIRECT };
|
|
40
|
+
}
|
|
41
|
+
export async function startDropboxConnect() {
|
|
42
|
+
const client = oauthClient();
|
|
43
|
+
if (closeFlow)
|
|
44
|
+
throw new Error("Dropbox connection is already in progress. Complete it in the browser first.");
|
|
45
|
+
const verifier = randomBytes(32).toString("base64url");
|
|
46
|
+
const state = randomBytes(24).toString("base64url");
|
|
47
|
+
const url = new URL("https://www.dropbox.com/oauth2/authorize");
|
|
48
|
+
url.search = new URLSearchParams({ client_id: process.env.DROPBOX_CLIENT_ID, response_type: "code", redirect_uri: REDIRECT, token_access_type: "offline", code_challenge: createHash("sha256").update(verifier).digest("base64url"), code_challenge_method: "S256", state }).toString();
|
|
49
|
+
const server = createServer((req, res) => {
|
|
50
|
+
const callback = new URL(req.url ?? "/", REDIRECT);
|
|
51
|
+
if (callback.pathname !== "/callback") {
|
|
52
|
+
res.writeHead(404).end();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (callback.searchParams.get("state") !== state || !callback.searchParams.get("code")) {
|
|
56
|
+
res.writeHead(400).end("Invalid authorization callback");
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
void (async () => {
|
|
60
|
+
try {
|
|
61
|
+
const result = await client.oauth2.token({ grant_type: "authorization_code", code: callback.searchParams.get("code"), redirect_uri: REDIRECT, code_verifier: verifier });
|
|
62
|
+
if (closeFlow !== closeCurrent) {
|
|
63
|
+
res.writeHead(400).end("Authorization expired. Connect again from Videobook.");
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (!result.refresh_token)
|
|
67
|
+
throw new Error("Dropbox did not grant offline access");
|
|
68
|
+
await appState.set(KEY, { accessToken: result.access_token, refreshToken: result.refresh_token, expiresAt: new Date(Date.now() + result.expires_in * 1000).toISOString() });
|
|
69
|
+
res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" }).end("Dropbox connected. Return to Videobook.");
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
lastError = "Dropbox authorization failed. Try connecting again.";
|
|
73
|
+
res.writeHead(400).end(lastError);
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
closeCurrent();
|
|
77
|
+
}
|
|
78
|
+
})();
|
|
79
|
+
});
|
|
80
|
+
const timeout = setTimeout(() => { lastError = "Dropbox connection timed out"; closeCurrent(); }, 5 * 60_000);
|
|
81
|
+
timeout.unref();
|
|
82
|
+
const closeCurrent = () => { clearTimeout(timeout); server.close(); if (closeFlow === closeCurrent) {
|
|
83
|
+
pending = false;
|
|
84
|
+
closeFlow = null;
|
|
85
|
+
} };
|
|
86
|
+
closeFlow = closeCurrent;
|
|
87
|
+
await new Promise((resolve, reject) => { server.once("error", reject); server.listen(8766, "127.0.0.1", resolve); }).catch((error) => { closeCurrent(); throw error; });
|
|
88
|
+
pending = true;
|
|
89
|
+
lastError = null;
|
|
90
|
+
return { authorize_url: url.toString() };
|
|
91
|
+
}
|