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,136 @@
|
|
|
1
|
+
---
|
|
2
|
+
source: "https://blog.fal.ai/kling-3-0-prompting-guide/ , https://www.ambienceai.com/tutorials/kling-prompting-guide , https://filmora.wondershare.com/ai-prompt/kling-ai-negative-prompt.html"
|
|
3
|
+
type: reference
|
|
4
|
+
date_added: 2026-02-13
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Kling 3.0 Prompting Guide
|
|
8
|
+
|
|
9
|
+
Compiled from multiple sources covering Kling 3.0 prompting techniques, camera controls, negative prompts, and multi-shot capabilities.
|
|
10
|
+
|
|
11
|
+
## Summary
|
|
12
|
+
|
|
13
|
+
Kling 3.0 is a major update to the Kling AI video generation model. Its biggest advancement is understanding **cinematic intent** — it processes prompts more like a director's notes than a list of objects. Key new features include 15-second generation, multi-shot sequences (up to 6 shots), native audio with dialogue, and improved character consistency.
|
|
14
|
+
|
|
15
|
+
## Prompt Structure
|
|
16
|
+
|
|
17
|
+
The recommended prompt template follows this order:
|
|
18
|
+
|
|
19
|
+
**Scene > Characters > Action > Camera > Audio & Style**
|
|
20
|
+
|
|
21
|
+
Full template: `[Shot type] of [subject] [action/movement], [environment/setting], [camera movement], [lighting/mood], [style/aesthetic]`
|
|
22
|
+
|
|
23
|
+
Example: "Medium shot of a professional chef expertly flipping vegetables in a wok with flames rising, modern restaurant kitchen with stainless steel surfaces, camera slowly dollies forward, dramatic side lighting creating strong shadows, cinematic food photography style, shallow depth of field"
|
|
24
|
+
|
|
25
|
+
## Cinematic Intent
|
|
26
|
+
|
|
27
|
+
Prompts that reference filmmaking concepts (scene coverage, composition, pacing, continuity) consistently outperform prompts that focus only on visual attributes. The model excels when prompts read like directions to a scene rather than a list of objects.
|
|
28
|
+
|
|
29
|
+
## Multi-Shot Sequences
|
|
30
|
+
|
|
31
|
+
- Supports up to 6 shots in a single output
|
|
32
|
+
- Clearly label each shot with framing, subject, and motion
|
|
33
|
+
- Don't compress multi-shot narratives into one paragraph
|
|
34
|
+
- Use explicit shot names: profile shots, macro close-ups, tracking shots, POV
|
|
35
|
+
|
|
36
|
+
Example structure:
|
|
37
|
+
```
|
|
38
|
+
[Scene Setup with atmosphere]
|
|
39
|
+
[Shot 1 description with framing and motion]
|
|
40
|
+
[Character A, tone]: "Dialogue"
|
|
41
|
+
[Immediate action]
|
|
42
|
+
[Character B, tone]: "Response"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Camera Movement Types
|
|
46
|
+
|
|
47
|
+
- **Dolly** — forward/backward for reveal shots
|
|
48
|
+
- **Tracking** — following subjects laterally for action
|
|
49
|
+
- **Crane/Jib** — vertical movement with arc for establishing shots
|
|
50
|
+
- **Pan** — horizontal rotation across landscapes
|
|
51
|
+
- **Tilt** — vertical rotation for height emphasis
|
|
52
|
+
- **Orbit** — 360-degree rotation around subjects
|
|
53
|
+
- **Handheld** — natural shake for documentary feel
|
|
54
|
+
- **Steadicam** — smooth floating movement
|
|
55
|
+
|
|
56
|
+
Advanced: dolly zoom (perspective shift), arc tracking (dynamic orbits), crane pans (vertical reveals)
|
|
57
|
+
|
|
58
|
+
Tips: Specify speed ("slow dolly," "rapid pan"), define start/end points, layer movements, match movement to mood, use focal length terms.
|
|
59
|
+
|
|
60
|
+
## Motion and Temporal Control
|
|
61
|
+
|
|
62
|
+
- Use temporal markers: "initially... then... finally" for progression
|
|
63
|
+
- Specify motion paths: "arc from left to right," "spiral upward"
|
|
64
|
+
- Add motion endpoints: "then settles back into place" to prevent drift
|
|
65
|
+
- Describe camera behavior precisely: stays, freezes, resumes
|
|
66
|
+
|
|
67
|
+
Motion descriptors: gracefully, swiftly, gradually, explosively, smoothly, rhythmically, dynamically
|
|
68
|
+
|
|
69
|
+
## Native Audio and Dialogue
|
|
70
|
+
|
|
71
|
+
- Specify who speaks and when, especially in multi-character scenes
|
|
72
|
+
- Use consistent character labels: `[Character A: Description]`
|
|
73
|
+
- Bind dialogue to actions: describe the action first, then dialogue
|
|
74
|
+
- Add tone descriptors: "raspy deep voice," "fearful voice"
|
|
75
|
+
- Use temporal markers: "Immediately," "Pause," "Softly speaking"
|
|
76
|
+
- Supports multiple languages, accents, and code-switching
|
|
77
|
+
|
|
78
|
+
## Elements (Visual Reference Binding)
|
|
79
|
+
|
|
80
|
+
- Elements inject reference images for specific characters, objects, or styles
|
|
81
|
+
- Each element has: `name` (label), `description` (what it is), `element_input_urls` (2-4 reference images)
|
|
82
|
+
- Reference in prompts with `@element_name` syntax — e.g., `@element_dog`
|
|
83
|
+
- The `name` must match the `@` reference in the prompt exactly (without the `@`)
|
|
84
|
+
- Naming convention: lowercase with underscores (`element_dog`, `element_cat`)
|
|
85
|
+
- Minimum 2 reference images per element, maximum 4 (JPG/PNG, max 10MB each)
|
|
86
|
+
- Video elements also supported: MP4/MOV, max 50MB
|
|
87
|
+
- Tracks up to 3 people independently in one scene
|
|
88
|
+
- `@element_name` binds visual identity; `[Character, tone]:` binds dialogue — use both together
|
|
89
|
+
- API field: `kling_elements` array in the request body
|
|
90
|
+
|
|
91
|
+
## Negative Prompts
|
|
92
|
+
|
|
93
|
+
### Best Practices
|
|
94
|
+
- Limit to 3-7 items — don't overload
|
|
95
|
+
- Keep language simple and direct
|
|
96
|
+
- Match prompts to the specific scene type
|
|
97
|
+
- Type what you want to avoid directly (e.g., "robots" not "no robots")
|
|
98
|
+
- Be specific rather than vague
|
|
99
|
+
|
|
100
|
+
### By Scene Type
|
|
101
|
+
- **Cinematic**: grain, lens distortion, flicker, low resolution
|
|
102
|
+
- **Character animation**: extra fingers, deformed limbs, face blur, glitching
|
|
103
|
+
- **Product marketing**: shadows, reflections, motion blur, unnecessary objects
|
|
104
|
+
- **Fashion/runway**: awkward arm movement, double limbs, stutter, poor lighting
|
|
105
|
+
- **Fantasy**: clutter, pixelation, overlapping structures, unnatural lighting
|
|
106
|
+
- **Nature**: blurry motion, incorrect animal anatomy, unnatural colors
|
|
107
|
+
- **Action**: camera shake, motion blur, vehicle clipping, unrealistic physics
|
|
108
|
+
|
|
109
|
+
### Common Mistakes
|
|
110
|
+
- Using vague terminology instead of specific technical terms
|
|
111
|
+
- Applying generic prompts without considering scene type
|
|
112
|
+
- Including excessive negative prompts that confuse the AI
|
|
113
|
+
- Forgetting to balance positive and negative instructions
|
|
114
|
+
|
|
115
|
+
## Style Keywords
|
|
116
|
+
|
|
117
|
+
Quality enhancers: cinematic, professional, ultra-detailed, photorealistic, studio-quality, broadcast-ready
|
|
118
|
+
|
|
119
|
+
Atmosphere terms: ethereal, dramatic, moody, vibrant, serene, intense, mysterious, energetic
|
|
120
|
+
|
|
121
|
+
## Technical Settings
|
|
122
|
+
|
|
123
|
+
- Resolution: 1080p (1920x1080) for professional output
|
|
124
|
+
- Frame rate: 30fps industry standard
|
|
125
|
+
- Duration: 5 seconds (clips) or 10-15 seconds (extended)
|
|
126
|
+
- Quality modes: Standard (faster) vs Cinematic (highest quality)
|
|
127
|
+
|
|
128
|
+
## Key Takeaways
|
|
129
|
+
|
|
130
|
+
- Write prompts like a director, not a describer
|
|
131
|
+
- Structure: Scene > Characters > Action > Camera > Style
|
|
132
|
+
- Use film terminology for camera and framing
|
|
133
|
+
- Multi-shot: label each shot explicitly, don't compress
|
|
134
|
+
- Negative prompts: targeted and limited (3-7 items)
|
|
135
|
+
- Specify motion endpoints to prevent generation drift
|
|
136
|
+
- Iterate by changing one variable at a time
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
source: "blog.google/products/gemini/prompting-tips-nano-banana-pro/, wavespeed.ai, minimaxir.com, imagine.art, github.com/ZeroLu/awesome-nanobanana-pro"
|
|
3
|
+
type: reference
|
|
4
|
+
date_added: 2026-02-13
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Nano Banana Pro Prompting Guide
|
|
8
|
+
|
|
9
|
+
## Summary
|
|
10
|
+
|
|
11
|
+
Nano Banana Pro is Google DeepMind's image generation model built on Gemini 3 Pro, launched November 2025. It introduces thinking mode (the model reasons through prompts before generating), search grounding (real-time Google Search integration for factual accuracy), text rendering (legible multi-language text directly in images), multi-image input (up to 14 reference images), and high-resolution output (up to 4K).
|
|
12
|
+
|
|
13
|
+
## Key Takeaways
|
|
14
|
+
|
|
15
|
+
- Prompts should follow the structure: Subject/Action + Style/Medium + Setting/Atmosphere + Technical Specs + Mood/Emotion
|
|
16
|
+
- Six essential prompt components: subject, composition, action, setting/location, style, and editing instructions
|
|
17
|
+
- Text rendering excels with specific placement, font, and size instructions — keep to 1-3 text elements per image
|
|
18
|
+
- Thinking mode reasons through the prompt and generates interim "thought images" before the final output
|
|
19
|
+
- Search grounding connects to Google Search for real-time data — trigger with keywords like "search for," "latest," "today," "current"
|
|
20
|
+
- Up to 14 reference images can be provided for character/brand consistency, style direction, and composition guidance
|
|
21
|
+
- Supports 1K, 2K, and 4K resolution output
|
|
22
|
+
- The model pushes toward realism due to RLHF training — surreal or absurd prompts get normalized
|
|
23
|
+
- Explicit negations work reliably: "NEVER include any text, watermarks, or line overlays"
|
|
24
|
+
- Photographic specifications (camera model, lens, lighting) significantly improve professional output quality
|
|
25
|
+
- JSON-structured prompts can help organize complex multi-element compositions
|
|
26
|
+
- Era/aesthetic anchoring through film stocks ("Kodak Portra 400") and time periods triggers consistent visual language
|
|
27
|
+
- Grid generations (2x2, 4x4) work well because autoregressive generation understands previously-rendered sections
|
|
28
|
+
- Cost: base ~$0.039/image, Pro 2K ~$0.134/image; generation time 20s to 1min+
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
---
|
|
2
|
+
source: "fal.ai/models/bytedance/seedance-2.0/reference-to-video, seedance2api.app/blog/seedance-2-prompt-guide, seed.bytedance.com/en/blog/official-launch-of-seedance-2-0"
|
|
3
|
+
type: reference
|
|
4
|
+
date_added: 2026-05-03
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Seedance 2.0 Prompting Guide
|
|
8
|
+
|
|
9
|
+
## Summary
|
|
10
|
+
|
|
11
|
+
Seedance 2.0 is ByteDance's multimodal video generation model. It uses a dual-branch Diffusion Transformer — one branch for spatial information (what things look like), one for temporal (how they move). Well-structured prompts feed both branches cleanly. The model exposes director-level camera control via prompt (dolly, tracking, POV switches, handheld) and generates synchronized native audio by default.
|
|
12
|
+
|
|
13
|
+
Three task variants share the same prompt grammar:
|
|
14
|
+
- **t2v** (text-to-video) — prompt only
|
|
15
|
+
- **i2v** (image-to-video) — prompt + a single first-frame image (and optional `end_image_url` last frame)
|
|
16
|
+
- **r2v** (reference-to-video) — prompt + up to 9 images, 3 videos, 3 audio clips (12 files max), referenced by @-tag in the prompt
|
|
17
|
+
|
|
18
|
+
A "fast" mode exists for each variant — same schema, lower quality/latency tradeoff.
|
|
19
|
+
|
|
20
|
+
## Prompt Structure
|
|
21
|
+
|
|
22
|
+
Five-component formula:
|
|
23
|
+
|
|
24
|
+
**Subject + Action + Camera + Style + Constraints**
|
|
25
|
+
|
|
26
|
+
- **Subject** — who/what appears
|
|
27
|
+
- **Action** — movement and events
|
|
28
|
+
- **Camera** — angle, movement, lens
|
|
29
|
+
- **Style** — aesthetic anchor (specific film/director, not "cinematic")
|
|
30
|
+
- **Constraints** — duration, resolution, physics requirements
|
|
31
|
+
|
|
32
|
+
Keep prompts under ~150 words. Place subject and action first.
|
|
33
|
+
|
|
34
|
+
## @ Tag Reference Syntax (r2v)
|
|
35
|
+
|
|
36
|
+
Reference uploaded files inline using `@Image1`, `@Video1`, `@Audio1`. Numbers correspond to upload order. `generate_video` in `r2v` mode accepts `refs.image` / `refs.video` / `refs.audio` arrays of artifact ids — index 0 is `@Image1`, index 1 is `@Image2`, etc.
|
|
37
|
+
|
|
38
|
+
Limits per request:
|
|
39
|
+
- Images: `@Image1` … `@Image9` (max 9)
|
|
40
|
+
- Videos: `@Video1` … `@Video3` (max 3, combined 2–15s)
|
|
41
|
+
- Audio: `@Audio1` … `@Audio3` (max 3, combined ≤15s)
|
|
42
|
+
- Total files across modalities: 12
|
|
43
|
+
|
|
44
|
+
### Image reference roles
|
|
45
|
+
|
|
46
|
+
State the role explicitly so the model knows what to do with each image:
|
|
47
|
+
|
|
48
|
+
- `@Image1 as the first frame` — start the video from this image
|
|
49
|
+
- `@Image1 as the last frame` — end the video at this image
|
|
50
|
+
- `@Image1 as character reference` — preserve appearance throughout
|
|
51
|
+
- `@Image1 as background environment` — use as scene setting
|
|
52
|
+
- `@Image1 as style reference` — match color/texture/mood
|
|
53
|
+
|
|
54
|
+
### Video reference roles
|
|
55
|
+
|
|
56
|
+
- `Refer to the camera movement of @Video1`
|
|
57
|
+
- `Character moves like @Video1` — transfer choreography
|
|
58
|
+
- `Apply the transition effects from @Video1`
|
|
59
|
+
- `Match the pacing and cuts of @Video1`
|
|
60
|
+
|
|
61
|
+
### Audio reference roles
|
|
62
|
+
|
|
63
|
+
- `@Audio1 as background soundtrack`
|
|
64
|
+
- `@Audio1 as ambient sound`
|
|
65
|
+
- `@Audio1 as voice style reference`
|
|
66
|
+
|
|
67
|
+
### Worked example (multi-modal r2v)
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
Replace the character in @Video1 with @Image1, with @Image1 as the first frame.
|
|
71
|
+
The character should wear virtual sci-fi glasses. Refer to the camera movement
|
|
72
|
+
and close-up surround shots of @Video1, changing from a third-person perspective
|
|
73
|
+
to the character's subjective perspective. Travel through AI virtual glasses and
|
|
74
|
+
arrive at the deep blue universe of @Image2 where several spaceships are seen
|
|
75
|
+
traveling into distance.
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
This prompt works because every @-tag has an explicit role (`replace … with`, `as the first frame`, `Refer to … of`, `arrive at … of`), the perspective shift is named (third-person → subjective), and the destination scene is anchored visually (`deep blue universe … spaceships`).
|
|
79
|
+
|
|
80
|
+
### Actor replacement (video source + still identity refs)
|
|
81
|
+
|
|
82
|
+
Use Seedance 2.0 reference-to-video when replacing people in a clip. Pass the source clip in `refs.video` (`@Video1`) and one still per actor in `refs.image` (`@Image1`…), then map each still onto an on-screen person:
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
Replace the on-screen person matching @Image1 with @Image1 as character reference. Replace the on-screen person matching @Image2 with @Image2 as character reference. Replace the on-screen person matching @Image3 with @Image3 as character reference. Keep camera movement, pacing, and framing of @Video1.
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Notebook wiring: connect the source video plus the identity stills to a Seedance generation cell (model `kie:bytedance/seedance-2-5`, mode `r2v`). The runner fills `refs.video` from connected video cells and `refs.image` from connected image cells. The result is a normal generated video artifact (`original.mp4` / `proxy.mp4`), not an orientation render.
|
|
89
|
+
|
|
90
|
+
## Camera Movement Vocabulary
|
|
91
|
+
|
|
92
|
+
Explicit camera direction is the single highest-leverage prompt component.
|
|
93
|
+
|
|
94
|
+
- Dolly: "slow dolly-in," "push forward"
|
|
95
|
+
- Pan / tilt: "pan right," "tilt upward"
|
|
96
|
+
- Zoom: "slow zoom to close-up," "dolly zoom"
|
|
97
|
+
- Tracking: "tracking shot," "follow from behind," "close-up surround shots"
|
|
98
|
+
- Static: "static wide shot," "camera holds steady"
|
|
99
|
+
- Angle: "extreme wide shot," "low angle," "overhead"
|
|
100
|
+
- Lens: "anamorphic lens flare," "bokeh background"
|
|
101
|
+
- Perspective: "third-person to subjective POV," "first-person handheld"
|
|
102
|
+
|
|
103
|
+
## Multi-Shot, Scene Cuts, Timestamps
|
|
104
|
+
|
|
105
|
+
A single generation can contain natural cuts (up to 15s). Use scene-cut and timecode language to control pacing:
|
|
106
|
+
|
|
107
|
+
- `Cut scene to …`
|
|
108
|
+
- `At 5 seconds, …`
|
|
109
|
+
- `[00:00-00:04] Shot 1: …`
|
|
110
|
+
|
|
111
|
+
Shot-script template for multi-shot output:
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
【Style】Denis Villeneuve sci-fi epic
|
|
115
|
+
【Duration】10s
|
|
116
|
+
[00:00-00:04] Shot 1: Establishing wide (low-angle dolly-in). Description with physical details.
|
|
117
|
+
[00:04-00:07] Shot 2: Close-up surround (tracking arc). Description.
|
|
118
|
+
[00:07-00:10] Shot 3: POV (handheld). Description.
|
|
119
|
+
Consistency: same wardrobe and lighting across all shots.
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Variants and Parameters
|
|
123
|
+
|
|
124
|
+
All variants share these output parameters:
|
|
125
|
+
|
|
126
|
+
| Parameter | Values |
|
|
127
|
+
|---|---|
|
|
128
|
+
| `resolution` | `480p`, `720p` (fal.ai also lists `1080p` for r2v) — default `720p` |
|
|
129
|
+
| `duration` | `auto`, `4`–`15` seconds (integer) — default `auto` |
|
|
130
|
+
| `aspect_ratio` | `auto`, `21:9`, `16:9`, `4:3`, `1:1`, `3:4`, `9:16` |
|
|
131
|
+
| `generate_audio` | bool, default `true` — synchronized music/SFX/dialogue |
|
|
132
|
+
| `seed` | int, optional |
|
|
133
|
+
|
|
134
|
+
Variant-specific inputs:
|
|
135
|
+
|
|
136
|
+
- **t2v** — `prompt` only
|
|
137
|
+
- **i2v** — `prompt` + `image_url` (first frame) + optional `end_image_url` (last frame)
|
|
138
|
+
- **r2v** — `prompt` + `image_urls[≤9]` + `video_urls[≤3]` + `audio_urls[≤3]`, total ≤12
|
|
139
|
+
|
|
140
|
+
## Style Anchoring
|
|
141
|
+
|
|
142
|
+
Generic terms ("cinematic," "epic") underperform specific anchors. Reference an actual director, film, or art movement: "Denis Villeneuve sci-fi epic," "Wong Kar-wai neon street photography," "Studio Ghibli watercolor backgrounds." Anchor early in the prompt.
|
|
143
|
+
|
|
144
|
+
## Physics and Body Language
|
|
145
|
+
|
|
146
|
+
Seedance 2.0 simulates fluid dynamics, cloth, and character motion. Prompts that describe physical reactions ("hair lifts in the wind," "fabric ripples as she turns") consistently outperform prompts that describe abstract emotion ("she feels free"). Describe the body, not the feeling.
|
|
147
|
+
|
|
148
|
+
## Common Mistakes
|
|
149
|
+
|
|
150
|
+
| Mistake | Fix |
|
|
151
|
+
|---|---|
|
|
152
|
+
| Vague subject ("a cool car video") | Add specific descriptors and camera direction |
|
|
153
|
+
| @-tag references a non-existent file (e.g., `@Image3` with 2 uploaded) | Verify upload order; tags start at 1 |
|
|
154
|
+
| @-tag with no role | State what the file is for: "as first frame," "as background" |
|
|
155
|
+
| No camera direction | Add explicit movement (dolly, tracking, POV) |
|
|
156
|
+
| Generic style ("cinematic") | Anchor to a specific director/film/movement |
|
|
157
|
+
| Conflicting text vs. reference | Align prompt with the actual content of @-tagged files |
|
|
158
|
+
| >150 words | Trim — long prompts dilute the load-bearing instructions |
|
|
159
|
+
| No duration in prompt OR API | Set both — prompt language plus the `duration` parameter |
|
|
160
|
+
| Multiple actions, no timecodes | Use `[00:00-00:05]` format to bind events to time |
|
|
161
|
+
|
|
162
|
+
## Key Takeaways
|
|
163
|
+
|
|
164
|
+
- Five-part formula: Subject + Action + Camera + Style + Constraints
|
|
165
|
+
- Every @-tag needs an explicit role ("as first frame," "as character reference," "Refer to camera movement of")
|
|
166
|
+
- Camera direction is mandatory — the model defaults to bland motion without it
|
|
167
|
+
- Style anchors should name a director or film, not a genre
|
|
168
|
+
- For multi-shot output, use timecodes (`[00:00-00:04]`) and named shots
|
|
169
|
+
- Describe physics and body language, not abstract emotion
|
|
170
|
+
- Keep prompts under ~150 words
|
|
171
|
+
- `generate_audio: true` (default) — say "no audio" in the prompt isn't enough, set the flag
|
|
172
|
+
- Match reference files to prompt text; conflicting signals degrade the output
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export const EXTENSION_ORIGIN = "chrome-extension://cebmihgcglhadjckbebmfahaiiljhgdj";
|
|
2
|
+
function localHost(host) {
|
|
3
|
+
const match = /^(localhost|127\.0\.0\.1|\[::1\])(?::([0-9]{1,5}))?$/i.exec(host);
|
|
4
|
+
return match !== null && (match[2] === undefined || Number(match[2]) <= 65535);
|
|
5
|
+
}
|
|
6
|
+
function localOrigin(origin) {
|
|
7
|
+
if (origin === EXTENSION_ORIGIN)
|
|
8
|
+
return true;
|
|
9
|
+
const match = /^https?:\/\/(.+)$/.exec(origin);
|
|
10
|
+
return match !== null && localHost(match[1]);
|
|
11
|
+
}
|
|
12
|
+
export function allowsHttpRequest(host, origin, fetchSite) {
|
|
13
|
+
if (!localHost(host))
|
|
14
|
+
return false;
|
|
15
|
+
if (origin !== null)
|
|
16
|
+
return localOrigin(origin);
|
|
17
|
+
return fetchSite !== "cross-site";
|
|
18
|
+
}
|
|
19
|
+
export const restrictHttpAccess = (req, res, next) => {
|
|
20
|
+
if (!allowsHttpRequest(req.get("host") ?? "", req.get("origin") ?? null, req.get("sec-fetch-site") ?? null)) {
|
|
21
|
+
res.status(403).json({ error: "HTTP request origin or host is not allowed" });
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
next();
|
|
25
|
+
};
|
|
26
|
+
function isNativeMediaPath(path) {
|
|
27
|
+
let decodedPath;
|
|
28
|
+
try {
|
|
29
|
+
decodedPath = decodeURIComponent(path);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
return /\.(?:mp4|m4v|mov|webm|ogv|mp3|m4a|aac|wav|flac|ogg|oga|opus|weba)$/iu.test(decodedPath);
|
|
35
|
+
}
|
|
36
|
+
export const restrictMediaContent = (req, res, next) => {
|
|
37
|
+
const nativeMedia = isNativeMediaPath(req.path);
|
|
38
|
+
res.setHeader("Content-Security-Policy", [
|
|
39
|
+
nativeMedia ? "sandbox allow-same-origin" : "sandbox",
|
|
40
|
+
"default-src 'none'",
|
|
41
|
+
"img-src 'self' data: blob:",
|
|
42
|
+
"media-src 'self' data: blob:",
|
|
43
|
+
"style-src 'unsafe-inline'",
|
|
44
|
+
"base-uri 'none'",
|
|
45
|
+
"form-action 'none'",
|
|
46
|
+
].join("; "));
|
|
47
|
+
res.setHeader("X-Content-Type-Options", "nosniff");
|
|
48
|
+
next();
|
|
49
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export function parseHttpByteRange(value, size) {
|
|
2
|
+
if (!Number.isSafeInteger(size) || size < 1)
|
|
3
|
+
return { ok: false };
|
|
4
|
+
const match = value.trim().match(/^bytes=(\d*)-(\d*)$/u);
|
|
5
|
+
if (!match || (!match[1] && !match[2]))
|
|
6
|
+
return { ok: false };
|
|
7
|
+
if (!match[1]) {
|
|
8
|
+
const suffixLength = Number(match[2]);
|
|
9
|
+
if (!Number.isSafeInteger(suffixLength) || suffixLength < 1) {
|
|
10
|
+
return { ok: false };
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
ok: true,
|
|
14
|
+
end: size - 1,
|
|
15
|
+
start: Math.max(0, size - suffixLength),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
const start = Number(match[1]);
|
|
19
|
+
const requestedEnd = match[2] ? Number(match[2]) : size - 1;
|
|
20
|
+
if (!Number.isSafeInteger(start)
|
|
21
|
+
|| !Number.isSafeInteger(requestedEnd)
|
|
22
|
+
|| start >= size
|
|
23
|
+
|| requestedEnd < start) {
|
|
24
|
+
return { ok: false };
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
ok: true,
|
|
28
|
+
end: Math.min(requestedEnd, size - 1),
|
|
29
|
+
start,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { startPostMetricsScheduler } from "./posts/post-metrics.js";
|
|
2
|
+
import { queue } from "./queue.js";
|
|
3
|
+
import { loadAndArmAll } from "./scheduled-posts.js";
|
|
4
|
+
const runDirectly = async (step) => await step.run();
|
|
5
|
+
function openBooks(books) {
|
|
6
|
+
return books.list().flatMap((book) => {
|
|
7
|
+
const selected = books.get(book.bookId);
|
|
8
|
+
return selected.ok ? [selected.value] : [];
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
function bookTag(book) {
|
|
12
|
+
return `${book.name} [${book.bookId.slice(0, 8)}]`;
|
|
13
|
+
}
|
|
14
|
+
async function loadReconcilers(run) {
|
|
15
|
+
const imageSubjects = await run({ phase: "import", label: "jobs/image-subjects (hydrate)", run: () => import("./jobs/image-subjects.js") });
|
|
16
|
+
const runNotebook = await run({ phase: "import", label: "jobs/run-notebook (hydrate)", run: () => import("./jobs/run-notebook.js") });
|
|
17
|
+
return { imageSubjects, runNotebook };
|
|
18
|
+
}
|
|
19
|
+
async function ensureDevFixtures(books, run) {
|
|
20
|
+
const dancing = await run({ phase: "import", label: "dancing-workflow (fixtures)", run: () => import("./dancing-workflow.js") });
|
|
21
|
+
await run({ phase: "reconcile", label: "ensureDancingWorkflow()", run: () => dancing.ensureDancingWorkflow(books) });
|
|
22
|
+
}
|
|
23
|
+
async function reconcileBeforeQueue(books, run, reconcilers) {
|
|
24
|
+
const { imageSubjects, runNotebook } = reconcilers;
|
|
25
|
+
for (const book of openBooks(books)) {
|
|
26
|
+
const tag = bookTag(book);
|
|
27
|
+
await run({ phase: "reconcile", label: `${tag} jobs.queue.reap()`, run: () => book.engine.jobs.queue.reap() });
|
|
28
|
+
await run({ phase: "reconcile", label: `${tag} reconcileImageSubjectJobFailures`, run: () => imageSubjects.reconcileImageSubjectJobFailures(book.engine, book.bookId) });
|
|
29
|
+
await run({ phase: "reconcile", label: `${tag} abortLeasedNotebookRunsOnStartup`, run: () => runNotebook.abortLeasedNotebookRunsOnStartup(book.engine) });
|
|
30
|
+
await run({ phase: "reconcile", label: `${tag} reconcileGenerationMediaOutputs`, run: () => runNotebook.reconcileGenerationMediaOutputs(book.engine) });
|
|
31
|
+
await run({ phase: "reconcile", label: `${tag} reconcileQueuedNotebookCells`, run: () => runNotebook.reconcileQueuedNotebookCells(book.engine) });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async function reconcileAfterQueue(books, run, reconcilers) {
|
|
35
|
+
for (const book of openBooks(books)) {
|
|
36
|
+
await run({
|
|
37
|
+
phase: "reconcile",
|
|
38
|
+
label: `${bookTag(book)} reconcileAwaitingNotebookOutputs`,
|
|
39
|
+
run: () => reconcilers.runNotebook.reconcileAwaitingNotebookOutputs(book.engine, book.bookId),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export async function hydrateLibrary(books, options) {
|
|
44
|
+
const run = options.runStep ?? runDirectly;
|
|
45
|
+
await run({ phase: "open", label: "bookManager.ready()", run: () => books.ready() });
|
|
46
|
+
if (options.devFixtures)
|
|
47
|
+
await ensureDevFixtures(books, run);
|
|
48
|
+
const reconcilers = await loadReconcilers(run);
|
|
49
|
+
await reconcileBeforeQueue(books, run, reconcilers);
|
|
50
|
+
if (options.startQueue !== false)
|
|
51
|
+
await run({ phase: "reconcile", label: "queue.start()", run: () => queue.start() });
|
|
52
|
+
await reconcileAfterQueue(books, run, reconcilers);
|
|
53
|
+
await run({ phase: "reconcile", label: "loadAndArmAll()", run: () => loadAndArmAll() });
|
|
54
|
+
if (options.startPostMetrics !== false)
|
|
55
|
+
startPostMetricsScheduler();
|
|
56
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import sharp from "sharp";
|
|
2
|
+
const JPEG_REENCODE_THRESHOLD_BYTES = 3 * 1024 * 1024;
|
|
3
|
+
// External image-gen APIs reject or silently degrade sub-300px inputs. Keep both
|
|
4
|
+
// dimensions at or above this floor by upscaling small images to JPEG.
|
|
5
|
+
const MIN_IMAGE_DIMENSION = 300;
|
|
6
|
+
const REENCODABLE_FORMATS = new Set(["jpeg", "png", "webp", "bmp", "tiff", "avif"]);
|
|
7
|
+
function replaceExtension(filename, newExt) {
|
|
8
|
+
const dot = filename.lastIndexOf(".");
|
|
9
|
+
return dot === -1 ? `${filename}.${newExt}` : `${filename.slice(0, dot)}.${newExt}`;
|
|
10
|
+
}
|
|
11
|
+
// Re-encode raster images larger than the threshold to high-quality JPEG at
|
|
12
|
+
// the source resolution. Uploads to free hosts and Alibaba/Kie worker fetches
|
|
13
|
+
// are more reliable with smaller payloads, and wan i2v has a ~10 MB base-64
|
|
14
|
+
// cap (≈7 MB raw). Returns the input unchanged for non-images, small files,
|
|
15
|
+
// or cases where re-encoding would not shrink the payload.
|
|
16
|
+
export async function maybeReencodeImage(data, filename) {
|
|
17
|
+
if (data.byteLength <= JPEG_REENCODE_THRESHOLD_BYTES) {
|
|
18
|
+
return { data, filename };
|
|
19
|
+
}
|
|
20
|
+
let format;
|
|
21
|
+
try {
|
|
22
|
+
const meta = await sharp(data).metadata();
|
|
23
|
+
format = meta.format;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return { data, filename };
|
|
27
|
+
}
|
|
28
|
+
if (!format || !REENCODABLE_FORMATS.has(format)) {
|
|
29
|
+
return { data, filename };
|
|
30
|
+
}
|
|
31
|
+
const reencoded = await sharp(data)
|
|
32
|
+
.rotate()
|
|
33
|
+
.flatten({ background: "#ffffff" })
|
|
34
|
+
.jpeg({ quality: 92, mozjpeg: true, progressive: true, chromaSubsampling: "4:2:0" })
|
|
35
|
+
.toBuffer();
|
|
36
|
+
if (reencoded.byteLength >= data.byteLength) {
|
|
37
|
+
return { data, filename };
|
|
38
|
+
}
|
|
39
|
+
return { data: new Uint8Array(reencoded), filename: replaceExtension(filename, "jpg") };
|
|
40
|
+
}
|
|
41
|
+
// Upscale raster images whose shortest side is below MIN_IMAGE_DIMENSION so that
|
|
42
|
+
// both dimensions are at least MIN_IMAGE_DIMENSION, preserving aspect ratio.
|
|
43
|
+
// Output is always JPEG. Returns the input unchanged for non-raster formats or
|
|
44
|
+
// images that already meet the floor.
|
|
45
|
+
export async function maybeUpscaleSmallImage(data, filename) {
|
|
46
|
+
let format;
|
|
47
|
+
let width;
|
|
48
|
+
let height;
|
|
49
|
+
try {
|
|
50
|
+
const meta = await sharp(data).metadata();
|
|
51
|
+
format = meta.format;
|
|
52
|
+
width = meta.width;
|
|
53
|
+
height = meta.height;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return { data, filename };
|
|
57
|
+
}
|
|
58
|
+
if (!format || !REENCODABLE_FORMATS.has(format))
|
|
59
|
+
return { data, filename };
|
|
60
|
+
if (!width || !height)
|
|
61
|
+
return { data, filename };
|
|
62
|
+
if (width >= MIN_IMAGE_DIMENSION && height >= MIN_IMAGE_DIMENSION) {
|
|
63
|
+
return { data, filename };
|
|
64
|
+
}
|
|
65
|
+
const resized = await sharp(data)
|
|
66
|
+
.rotate()
|
|
67
|
+
.flatten({ background: "#ffffff" })
|
|
68
|
+
.resize({
|
|
69
|
+
width: MIN_IMAGE_DIMENSION,
|
|
70
|
+
height: MIN_IMAGE_DIMENSION,
|
|
71
|
+
fit: "outside",
|
|
72
|
+
withoutEnlargement: false,
|
|
73
|
+
})
|
|
74
|
+
.jpeg({ quality: 92, mozjpeg: true, progressive: true, chromaSubsampling: "4:2:0" })
|
|
75
|
+
.toBuffer();
|
|
76
|
+
return { data: new Uint8Array(resized), filename: replaceExtension(filename, "jpg") };
|
|
77
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const DEFAULT_IMAGE_EDIT_MODEL_ID = "nano-banana-2-1";
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { resolveModel } from "./media/descriptor.js";
|
|
2
|
+
import { DEFAULT_IMAGE_EDIT_MODEL_ID } from "./image-edit-default.js";
|
|
3
|
+
export { DEFAULT_IMAGE_EDIT_MODEL_ID } from "./image-edit-default.js";
|
|
4
|
+
const STANDARD_IMAGE_EDIT_CAPABILITIES = {
|
|
5
|
+
semantic: true,
|
|
6
|
+
regionReplace: true,
|
|
7
|
+
wholeImage: true,
|
|
8
|
+
exactMask: false,
|
|
9
|
+
};
|
|
10
|
+
const EDIT_MODELS = [
|
|
11
|
+
{ catalogModel: `kie:${DEFAULT_IMAGE_EDIT_MODEL_ID}`, label: "Nano Banana 2.1 Edit" },
|
|
12
|
+
{ catalogModel: "kie:seedream/5-pro-image-to-image", label: "Seedream 5 Pro" },
|
|
13
|
+
{ catalogModel: "kie:gpt-image-2-5-flare-image-to-image", label: "GPT Image 2.5 Flare" },
|
|
14
|
+
{ catalogModel: "kie:gpt-image-2-5-sunburst-image-to-image", label: "GPT Image 2.5 Sunburst" },
|
|
15
|
+
{ catalogModel: "kie:gpt-image-2-image-to-image", label: "GPT Image 2" },
|
|
16
|
+
{ catalogModel: "fal:fal-ai/nano-banana-pro/edit", label: "Nano Banana Pro" },
|
|
17
|
+
{ catalogModel: "fal:fal-ai/nano-banana-2/edit", label: "Nano Banana 2" },
|
|
18
|
+
{ catalogModel: "fal:alibaba/qwen-image-3/edit", label: "Qwen 3.0 Edit" },
|
|
19
|
+
{ catalogModel: "fal:fal-ai/wan/v2.7/edit", label: "Wan 2.7 Edit" },
|
|
20
|
+
];
|
|
21
|
+
function imageSlotMax(model) {
|
|
22
|
+
return model.slots.find((slot) => slot.role === "image")?.max ?? null;
|
|
23
|
+
}
|
|
24
|
+
function editModelInfo(entry) {
|
|
25
|
+
const model = resolveModel(entry.catalogModel);
|
|
26
|
+
const provider = model.provider === "fal" ? "fal" : "kie";
|
|
27
|
+
return {
|
|
28
|
+
id: model.apiId,
|
|
29
|
+
catalogModel: model.id,
|
|
30
|
+
label: entry.label,
|
|
31
|
+
provider,
|
|
32
|
+
envKey: provider === "fal" ? "FAL_API_KEY" : "KIE_API_KEY",
|
|
33
|
+
maxInputImages: imageSlotMax(model),
|
|
34
|
+
capabilities: STANDARD_IMAGE_EDIT_CAPABILITIES,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export const IMAGE_EDIT_MODELS = EDIT_MODELS.map(editModelInfo);
|
|
38
|
+
export const IMAGE_EDIT_MODEL_IDS = IMAGE_EDIT_MODELS.map((model) => model.id);
|
|
39
|
+
export function findImageEditModel(id) {
|
|
40
|
+
return IMAGE_EDIT_MODELS.find((model) => model.id === id || model.catalogModel === id);
|
|
41
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const SETTINGS_KEY = "image-edit-model-selection";
|
|
2
|
+
export function readLastImageEditModel(engine) {
|
|
3
|
+
const stored = engine.settings.get(SETTINGS_KEY);
|
|
4
|
+
const modelId = stored?.modelId;
|
|
5
|
+
return typeof modelId === "string" && modelId.trim() ? modelId.trim() : null;
|
|
6
|
+
}
|
|
7
|
+
export function writeLastImageEditModel(engine, modelId) {
|
|
8
|
+
const written = engine.settings.set(SETTINGS_KEY, { modelId });
|
|
9
|
+
if (!written.ok)
|
|
10
|
+
throw new Error(written.error.message);
|
|
11
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import sharp from "sharp";
|
|
2
|
+
export function imageRegionEditPrompt(regions, operation, prompt) {
|
|
3
|
+
const targets = regions.map((box, index) => `Region ${index + 1}: normalized box x=${box.x.toFixed(6)}, y=${box.y.toFixed(6)}, width=${box.width.toFixed(6)}, height=${box.height.toFixed(6)}`);
|
|
4
|
+
const instruction = operation === "remove"
|
|
5
|
+
? "Remove the contents of the selected regions and fill them naturally from the surrounding scene."
|
|
6
|
+
: prompt.trim();
|
|
7
|
+
return [
|
|
8
|
+
instruction,
|
|
9
|
+
"Apply the change only inside the following regions of the input image. Coordinates run from 0 to 1, starting at the top-left corner.",
|
|
10
|
+
...targets,
|
|
11
|
+
"Keep the same image dimensions, composition and camera framing. Preserve everything outside the selected regions exactly.",
|
|
12
|
+
].join("\n");
|
|
13
|
+
}
|
|
14
|
+
export async function compositeImageRegionEdit(original, generated, regions) {
|
|
15
|
+
const source = await sharp(original).rotate().png().toBuffer();
|
|
16
|
+
const { width, height } = await sharp(source).metadata();
|
|
17
|
+
if (!width || !height)
|
|
18
|
+
throw new Error("Could not determine source image dimensions");
|
|
19
|
+
const edited = await sharp(generated).rotate().resize(width, height, { fit: "fill" })
|
|
20
|
+
.png().toBuffer();
|
|
21
|
+
const overlays = await Promise.all(regions.map(async (box) => {
|
|
22
|
+
const left = Math.min(width - 1, Math.floor(box.x * width));
|
|
23
|
+
const top = Math.min(height - 1, Math.floor(box.y * height));
|
|
24
|
+
const right = Math.min(width, Math.max(left + 1, Math.ceil((box.x + box.width) * width)));
|
|
25
|
+
const bottom = Math.min(height, Math.max(top + 1, Math.ceil((box.y + box.height) * height)));
|
|
26
|
+
const input = await sharp(edited).extract({
|
|
27
|
+
left, top, width: right - left, height: bottom - top,
|
|
28
|
+
}).toBuffer();
|
|
29
|
+
return { input, left, top };
|
|
30
|
+
}));
|
|
31
|
+
return sharp(source).composite(overlays).png().toBuffer();
|
|
32
|
+
}
|