@gentbajko/slopify 2.5.0 → 3.0.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/LICENSE +201 -21
- package/NOTICE +12 -0
- package/README.md +21 -40
- package/SUBTITLES.md +1 -1
- package/dist/adapter-registry.js +3 -0
- package/dist/adapters/alignment/cache.js +4 -1
- package/dist/adapters/alignment/ctc.js +25 -23
- package/dist/adapters/alignment/index.js +37 -6
- package/dist/adapters/alignment/multilingual.js +267 -0
- package/dist/adapters/alignment/protocol.js +11 -0
- package/dist/adapters/alignment/quality.js +4 -3
- package/dist/adapters/alignment/sentences.js +95 -0
- package/dist/adapters/alignment/spec.js +24 -0
- package/dist/adapters/alignment/spell.js +769 -0
- package/dist/adapters/alignment/text.js +56 -9
- package/dist/adapters/alignment/window.js +34 -30
- package/dist/adapters/alignment/worker.js +23 -21
- package/dist/adapters/explain.js +11 -11
- package/dist/adapters/host-cli/index.js +44 -5
- package/dist/adapters/host-cli/transport.js +7 -5
- package/dist/adapters/image/bytes.js +2 -2
- package/dist/adapters/image/codex-output.js +1 -1
- package/dist/adapters/image/codex.js +87 -22
- package/dist/adapters/image/fal.js +13 -5
- package/dist/adapters/image/google.js +11 -8
- package/dist/adapters/image/openai.js +14 -7
- package/dist/adapters/image/reference.js +34 -0
- package/dist/adapters/image/replicate.js +7 -2
- package/dist/adapters/image/video.js +2 -2
- package/dist/adapters/key-probes.js +36 -0
- package/dist/adapters/llm/claude-code.js +168 -32
- package/dist/adapters/llm/cli-login-error.js +1 -1
- package/dist/adapters/llm/codex-limits.js +157 -0
- package/dist/adapters/llm/codex.js +99 -12
- package/dist/adapters/llm/document-workspace.js +2 -2
- package/dist/adapters/llm/gemini-workspace.js +1 -1
- package/dist/adapters/llm/gemini.js +16 -3
- package/dist/adapters/llm/image-workspace.js +58 -0
- package/dist/adapters/llm/openrouter.js +11 -3
- package/dist/adapters/llm/run-cli.js +12 -7
- package/dist/adapters/tts/cartesia.js +10 -0
- package/dist/adapters/tts/elevenlabs.js +31 -9
- package/dist/adapters/tts/inworld-async.js +9 -5
- package/dist/adapters/tts/inworld.js +17 -3
- package/dist/adapters/tts/openai.js +3 -0
- package/dist/adapters/tts/voice-languages.js +80 -0
- package/dist/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/NotoSansArabic-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/NotoSansDevanagari-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/NotoSansHebrew-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/NotoSansThai-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/OFL.txt +93 -0
- package/dist/assets/fonts/noto/SOURCE.txt +18 -0
- package/dist/assets/sample/sample-audiobook.tar +0 -0
- package/dist/assets/sample/sample-podcast.tar +0 -0
- package/dist/assets/sample/sample-project.tar +0 -0
- package/dist/catalog/merge.js +132 -0
- package/dist/catalog/registry.js +13 -13
- package/dist/catalog/schema.js +2 -0
- package/dist/catalog/store.js +147 -8
- package/dist/compose.yaml +71 -0
- package/dist/edge/autostart/docker-record.js +32 -0
- package/dist/edge/autostart/docker.js +50 -0
- package/dist/edge/autostart/index.js +78 -0
- package/dist/edge/autostart/launcher.js +176 -0
- package/dist/edge/autostart/model.js +3 -0
- package/dist/edge/autostart/native.js +210 -0
- package/dist/edge/autostart/prompt.js +66 -0
- package/dist/edge/autostart/service.js +81 -0
- package/dist/edge/cli.js +57 -51
- package/dist/edge/docker-install/activation.js +56 -0
- package/dist/edge/docker-install/apply.js +458 -0
- package/dist/edge/docker-install/engine.js +362 -0
- package/dist/edge/docker-install/run.js +154 -0
- package/dist/edge/docker-install/state.js +146 -0
- package/dist/edge/{docker-projects → docker-install}/volume.js +1 -5
- package/dist/edge/docker.js +14 -22
- package/dist/edge/host-cli.js +1 -1
- package/dist/edge/http/actions.js +32 -10
- package/dist/edge/http/app.js +58 -2
- package/dist/edge/http/auditions.js +70 -0
- package/dist/edge/http/autostart.js +37 -0
- package/dist/edge/http/channel-memory.js +92 -0
- package/dist/edge/http/channels.js +128 -0
- package/dist/edge/http/entries.js +22 -3
- package/dist/edge/http/files.js +1 -1
- package/dist/edge/http/folder-location.js +1 -1
- package/dist/edge/http/home.js +39 -0
- package/dist/edge/http/host-cli.js +29 -5
- package/dist/edge/http/narration-peaks.js +76 -0
- package/dist/edge/http/onboarding.js +152 -0
- package/dist/edge/http/open-folder.js +1 -1
- package/dist/edge/http/patch-notes.js +45 -0
- package/dist/edge/http/planning.js +4 -2
- package/dist/edge/http/project-create.js +87 -0
- package/dist/edge/http/projects.js +31 -99
- package/dist/edge/http/prompts.js +40 -3
- package/dist/edge/http/pronunciations.js +23 -4
- package/dist/edge/http/providers.js +86 -0
- package/dist/edge/http/reviews.js +121 -0
- package/dist/edge/http/revision-files.js +1 -1
- package/dist/edge/http/revisions.js +1 -1
- package/dist/edge/http/run-cost.js +25 -0
- package/dist/edge/http/schedules.js +111 -6
- package/dist/edge/http/settings.js +84 -6
- package/dist/edge/http/storage-files.js +50 -0
- package/dist/edge/http/storage.js +29 -2
- package/dist/edge/http/studio.js +207 -0
- package/dist/edge/http/style-preview.js +100 -0
- package/dist/edge/http/subtitles.js +1 -1
- package/dist/edge/http/trash.js +69 -0
- package/dist/edge/http/update.js +24 -3
- package/dist/edge/http/whats-new.js +15 -0
- package/dist/edge/http/youtube-edits.js +83 -0
- package/dist/edge/native-update.js +62 -0
- package/dist/host-cli/open-folder.js +10 -10
- package/dist/host-cli/runtime.js +9 -1
- package/dist/kernel/db/migrations/0025-automatic-reviews.sql +40 -0
- package/dist/kernel/db/migrations/0026-schedule-topic-generation.sql +23 -0
- package/dist/kernel/db/migrations/0027-run-cost.sql +58 -0
- package/dist/kernel/db/migrations/0030-automatic-retries.sql +18 -0
- package/dist/kernel/db/migrations/0031-prompt-history-and-description-edits.sql +33 -0
- package/dist/kernel/db/migrations/0032-channels.sql +65 -0
- package/dist/kernel/db/migrations/0034-script-prompts.sql +23 -0
- package/dist/kernel/db/migrations/0035-narration-aliases.sql +13 -0
- package/dist/kernel/db/migrations/0037-project-uploads.sql +8 -0
- package/dist/kernel/db/migrations/0038-voice-languages.sql +7 -0
- package/dist/kernel/db/migrations/0039-channel-essentials.sql +64 -0
- package/dist/kernel/db/migrations/0040-standalone-usage.sql +35 -0
- package/dist/kernel/paths.js +27 -6
- package/dist/kernel/pipeline.js +4 -1
- package/dist/kernel/ports/host-cli.js +122 -14
- package/dist/kernel/ports/key-probe.js +1 -0
- package/dist/kernel/ports/languages.js +157 -0
- package/dist/kernel/ports/model.js +5 -2
- package/dist/kernel/ports/narration-aliases.js +60 -0
- package/dist/kernel/ports/plan-limits.js +36 -0
- package/dist/kernel/runner/attempt.js +17 -8
- package/dist/kernel/runner/audition.js +36 -0
- package/dist/kernel/runner/graph.js +24 -5
- package/dist/kernel/runner/index.js +35 -5
- package/dist/kernel/runner/meter.js +1 -0
- package/dist/kernel/runner/providers.js +142 -7
- package/dist/kernel/runner/retry-policy.js +43 -0
- package/dist/kernel/runner/standalone.js +134 -0
- package/dist/kernel/runner/work-authority.js +28 -2
- package/dist/main.js +272 -16
- package/dist/patch-notes/0.4.2-to-1.0.md +93 -0
- package/dist/patch-notes/1.0-to-2.0.md +74 -0
- package/dist/patch-notes/2.0-to-3.0.md +257 -0
- package/dist/patch-notes/3.0.0.md +70 -0
- package/dist/patch-notes/index.json +26 -0
- package/dist/slices/admission/model.js +27 -0
- package/dist/slices/admission/repo.js +24 -4
- package/dist/slices/admission/rules.js +79 -1
- package/dist/slices/admission/schema.js +58 -1
- package/dist/slices/admission/short-mode.js +47 -0
- package/dist/slices/admission/start.js +41 -5
- package/dist/slices/article/continuation.js +2 -2
- package/dist/slices/article/provided-entries.js +2 -2
- package/dist/slices/article/run.js +6 -2
- package/dist/slices/article/segments.js +3 -3
- package/dist/slices/backups/folder.js +14 -6
- package/dist/slices/batch/index.js +7 -2
- package/dist/slices/cancel/index.js +1 -1
- package/dist/slices/channels/cast-images.js +110 -0
- package/dist/slices/channels/cast-match.js +32 -0
- package/dist/slices/channels/images.js +19 -0
- package/dist/slices/channels/model.js +5 -0
- package/dist/slices/channels/rebrand.js +100 -0
- package/dist/slices/channels/repo.js +218 -0
- package/dist/slices/channels/runs.js +129 -0
- package/dist/slices/channels/schema.js +129 -0
- package/dist/slices/channels/service.js +180 -0
- package/dist/slices/channels/studio-csv.js +110 -0
- package/dist/slices/channels/videos.js +120 -0
- package/dist/slices/checkpoints/recovery.js +10 -0
- package/dist/slices/control/index.js +2 -1
- package/dist/slices/document/fonts.js +1 -1
- package/dist/slices/document/render.js +1 -1
- package/dist/slices/episodes/related.js +90 -0
- package/dist/slices/episodes/repo.js +60 -0
- package/dist/slices/episodes/service.js +58 -0
- package/dist/slices/episodes/summarize.js +170 -0
- package/dist/slices/estimate/index.js +109 -28
- package/dist/slices/estimate/requests.js +60 -2
- package/dist/slices/fixes/rules.js +73 -0
- package/dist/slices/fonts/coverage.js +82 -0
- package/dist/slices/fonts/sfnt.js +74 -0
- package/dist/slices/images/run.js +8 -5
- package/dist/slices/images/scale.js +84 -0
- package/dist/slices/library/diff.js +122 -0
- package/dist/slices/library/history.js +100 -0
- package/dist/slices/library/model.js +3 -0
- package/dist/slices/library/photorealistic.js +49 -0
- package/dist/slices/library/repo.js +19 -14
- package/dist/slices/library/save.js +62 -12
- package/dist/slices/library/slots.js +7 -1
- package/dist/slices/library/used-by.js +153 -0
- package/dist/slices/loudness/level-pieces.js +41 -0
- package/dist/slices/loudness/loudnorm.js +164 -0
- package/dist/slices/loudness/model.js +153 -0
- package/dist/slices/model-upkeep/model.js +10 -0
- package/dist/slices/model-upkeep/switch.js +217 -0
- package/dist/slices/model-upkeep/usage.js +183 -0
- package/dist/slices/narration/aliases-library.js +31 -0
- package/dist/slices/narration/aliases-schema.js +53 -0
- package/dist/slices/narration/aliases.js +49 -0
- package/dist/slices/narration/blocks.js +352 -0
- package/dist/slices/narration/chunk.js +19 -7
- package/dist/slices/narration/concat.js +2 -2
- package/dist/slices/narration/describe.js +45 -0
- package/dist/slices/narration/pauses-model.js +63 -0
- package/dist/slices/narration/pauses.js +269 -0
- package/dist/slices/narration/peaks-model.js +1 -0
- package/dist/slices/narration/peaks.js +94 -0
- package/dist/slices/narration/preparation.js +21 -3
- package/dist/slices/narration/pronunciation-chunks.js +3 -3
- package/dist/slices/narration/pronunciation.js +72 -25
- package/dist/slices/narration/run.js +6 -6
- package/dist/slices/narration/shared-glossary.js +1 -1
- package/dist/slices/narration/steering.js +15 -4
- package/dist/slices/notifications/notifier.js +21 -3
- package/dist/slices/notifications/rules.js +22 -1
- package/dist/slices/onboarding/copy.js +143 -0
- package/dist/slices/onboarding/first-run.js +46 -0
- package/dist/slices/onboarding/install.js +119 -0
- package/dist/slices/onboarding/model.js +64 -0
- package/dist/slices/onboarding/packs.js +491 -0
- package/dist/slices/onboarding/quick-short.js +129 -0
- package/dist/slices/onboarding/sample.js +132 -0
- package/dist/slices/onboarding/state.js +68 -0
- package/dist/slices/onboarding/template.js +73 -0
- package/dist/slices/patch-notes/library.js +73 -0
- package/dist/slices/patch-notes/seen.js +77 -0
- package/dist/slices/play-drafts/convert.js +125 -6
- package/dist/slices/play-drafts/readiness.js +3 -0
- package/dist/slices/play-drafts/repo.js +2 -0
- package/dist/slices/play-drafts/review-inputs.js +20 -3
- package/dist/slices/play-drafts/schema.js +89 -0
- package/dist/slices/play-drafts/service.js +4 -1
- package/dist/slices/play-drafts/start.js +43 -18
- package/dist/slices/project-templates/from-project.js +50 -4
- package/dist/slices/project-templates/one-off.js +26 -0
- package/dist/slices/project-templates/repo.js +2 -1
- package/dist/slices/project-templates/service.js +15 -4
- package/dist/slices/project-templates/setup.js +18 -2
- package/dist/slices/rebuild/admission-repo.js +1 -1
- package/dist/slices/rebuild/model.js +6 -0
- package/dist/slices/rebuild/preview-details.js +46 -3
- package/dist/slices/rebuild/preview-plan.js +13 -6
- package/dist/slices/rebuild/recipe-audio-parts.js +40 -7
- package/dist/slices/rebuild/recipe-audio.js +66 -12
- package/dist/slices/rebuild/recipe-build.js +8 -4
- package/dist/slices/rebuild/recipe-cast.js +18 -0
- package/dist/slices/rebuild/recipe-checkpoints.js +11 -9
- package/dist/slices/rebuild/recipe-describe.js +93 -0
- package/dist/slices/rebuild/recipe-edit.js +13 -1
- package/dist/slices/rebuild/recipe-exports.js +62 -8
- package/dist/slices/rebuild/recipe-input-schema.js +35 -0
- package/dist/slices/rebuild/recipe-legacy.js +9 -0
- package/dist/slices/rebuild/recipe-loudness.js +45 -0
- package/dist/slices/rebuild/recipe-model.js +25 -6
- package/dist/slices/rebuild/recipe-pauses.js +7 -0
- package/dist/slices/rebuild/recipe-preparation.js +3 -3
- package/dist/slices/rebuild/recipe-provider-choice.js +11 -1
- package/dist/slices/rebuild/recipe-reference.js +15 -1
- package/dist/slices/rebuild/recipe-reviews.js +117 -0
- package/dist/slices/rebuild/recipe-save.js +4 -1
- package/dist/slices/rebuild/recipe-shorts.js +23 -3
- package/dist/slices/rebuild/recipe-text.js +114 -19
- package/dist/slices/rebuild/recipe-validation.js +14 -2
- package/dist/slices/rebuild/recipe-visual.js +96 -33
- package/dist/slices/rebuild/recipe-voices.js +286 -0
- package/dist/slices/rebuild/recipe-work.js +77 -6
- package/dist/slices/rebuild/recipe-youtube.js +2 -0
- package/dist/slices/rebuild/recovery-conflict.js +7 -3
- package/dist/slices/rebuild/recovery-model.js +3 -0
- package/dist/slices/rebuild/recovery-selection.js +28 -2
- package/dist/slices/rebuild/recovery.js +35 -17
- package/dist/slices/rebuild/repo.js +3 -3
- package/dist/slices/rebuild/retry.js +18 -0
- package/dist/slices/rebuild/review-redo.js +110 -0
- package/dist/slices/rebuild/runtime-actions.js +5 -0
- package/dist/slices/rebuild/runtime-admission.js +4 -1
- package/dist/slices/rebuild/runtime-animate.js +4 -4
- package/dist/slices/rebuild/runtime-article.js +5 -5
- package/dist/slices/rebuild/runtime-document.js +4 -4
- package/dist/slices/rebuild/runtime-export-bed.js +43 -0
- package/dist/slices/rebuild/runtime-export-edit.js +70 -11
- package/dist/slices/rebuild/runtime-export-inputs.js +52 -10
- package/dist/slices/rebuild/runtime-export-short.js +85 -0
- package/dist/slices/rebuild/runtime-export.js +84 -19
- package/dist/slices/rebuild/runtime-figure-card.js +87 -0
- package/dist/slices/rebuild/runtime-image.js +21 -2
- package/dist/slices/rebuild/runtime-local.js +148 -24
- package/dist/slices/rebuild/runtime-materialize.js +8 -0
- package/dist/slices/rebuild/runtime-narration-reuse.js +2 -1
- package/dist/slices/rebuild/runtime-narration-text.js +7 -7
- package/dist/slices/rebuild/runtime-pictures.js +28 -0
- package/dist/slices/rebuild/runtime-piece-label.js +27 -0
- package/dist/slices/rebuild/runtime-provider.js +126 -18
- package/dist/slices/rebuild/runtime-publication.js +21 -5
- package/dist/slices/rebuild/runtime-review.js +237 -0
- package/dist/slices/rebuild/runtime-run.js +17 -11
- package/dist/slices/rebuild/runtime-shorts.js +44 -17
- package/dist/slices/rebuild/runtime-store.js +39 -10
- package/dist/slices/rebuild/runtime-subtitles.js +72 -19
- package/dist/slices/rebuild/runtime-voices.js +103 -0
- package/dist/slices/rebuild/runtime-youtube.js +7 -6
- package/dist/slices/rebuild/soften.js +49 -0
- package/dist/slices/rebuild/transition-repo.js +9 -2
- package/dist/slices/research/run.js +4 -4
- package/dist/slices/research/synthesis.js +2 -2
- package/dist/slices/reviews/model.js +105 -0
- package/dist/slices/reviews/outcome.js +22 -0
- package/dist/slices/reviews/repo.js +106 -0
- package/dist/slices/reviews/rules.js +63 -0
- package/dist/slices/reviews/verdict.js +71 -0
- package/dist/slices/reviews/view.js +30 -0
- package/dist/slices/revisions/adopt-content.js +5 -2
- package/dist/slices/revisions/adopt.js +3 -1
- package/dist/slices/revisions/downloads.js +7 -1
- package/dist/slices/revisions/image-plan.js +183 -0
- package/dist/slices/revisions/mutation-assets.js +10 -0
- package/dist/slices/revisions/mutation-cues.js +71 -0
- package/dist/slices/revisions/mutation-request.js +2 -1
- package/dist/slices/revisions/mutations.js +97 -3
- package/dist/slices/revisions/schema.js +11 -1
- package/dist/slices/run-cost/limits.js +128 -0
- package/dist/slices/run-cost/meter.js +47 -0
- package/dist/slices/run-cost/panel.js +234 -0
- package/dist/slices/run-cost/pricing.js +92 -0
- package/dist/slices/run-cost/week.js +108 -0
- package/dist/slices/schedules/agenda.js +158 -0
- package/dist/slices/schedules/repo.js +58 -18
- package/dist/slices/schedules/scheduler.js +67 -16
- package/dist/slices/schedules/schema.js +138 -1
- package/dist/slices/schedules/service.js +43 -4
- package/dist/slices/schedules/similar.js +90 -0
- package/dist/slices/schedules/topic-list.js +163 -0
- package/dist/slices/schedules/topics.js +422 -0
- package/dist/slices/settings/first-run.js +102 -0
- package/dist/slices/settings/health.js +190 -0
- package/dist/slices/settings/key-guides.js +128 -0
- package/dist/slices/settings/key-test.js +85 -0
- package/dist/slices/settings/model.js +6 -1
- package/dist/slices/settings/playback.js +15 -0
- package/dist/slices/settings/repo.js +17 -3
- package/dist/slices/settings/voices.js +64 -4
- package/dist/slices/settings/whats-new.js +64 -0
- package/dist/slices/shorts/captions.js +9 -0
- package/dist/slices/shorts/pick.js +3 -3
- package/dist/slices/shorts/prompts.js +1 -1
- package/dist/slices/shorts/render.js +54 -5
- package/dist/slices/storage/asset-name.js +3 -0
- package/dist/slices/storage/backup-export.js +48 -9
- package/dist/slices/storage/backup-format.js +38 -1
- package/dist/slices/storage/backup-import.js +191 -19
- package/dist/slices/storage/delete-project.js +7 -4
- package/dist/slices/storage/documents.js +79 -0
- package/dist/slices/storage/files-location.js +531 -0
- package/dist/slices/storage/layout.js +14 -4
- package/dist/slices/storage/model.js +11 -0
- package/dist/slices/storage/portable.js +92 -12
- package/dist/slices/storage/reconcile.js +14 -1
- package/dist/slices/storage/repo.js +4 -1
- package/dist/slices/storage/schema.js +11 -0
- package/dist/slices/storage/trim.js +146 -0
- package/dist/slices/studio/disclosure.js +74 -0
- package/dist/slices/studio/model.js +28 -0
- package/dist/slices/studio/pack.js +209 -0
- package/dist/slices/studio/settings.js +133 -0
- package/dist/slices/style-preview/images.js +61 -0
- package/dist/slices/style-preview/render.js +161 -0
- package/dist/slices/style-preview/schema.js +45 -0
- package/dist/slices/style-preview/service.js +93 -0
- package/dist/slices/style-preview/settings.js +89 -0
- package/dist/slices/subtitles/captions.js +42 -9
- package/dist/slices/subtitles/model.js +22 -0
- package/dist/slices/subtitles/prepare.js +27 -16
- package/dist/slices/subtitles/transcript.js +1 -1
- package/dist/slices/thumbnail/run.js +7 -7
- package/dist/slices/trash/model.js +25 -0
- package/dist/slices/trash/service.js +273 -0
- package/dist/slices/uploads/repo.js +20 -0
- package/dist/slices/video/ambient-bed-schema.js +15 -0
- package/dist/slices/video/ambient-bed.js +95 -0
- package/dist/slices/video/ambient-mix.js +72 -0
- package/dist/slices/video/audio-inputs.js +2 -2
- package/dist/slices/video/cards.js +5 -2
- package/dist/slices/video/edit-list.js +23 -2
- package/dist/slices/video/edit-settings.js +13 -3
- package/dist/slices/video/ffmpeg.js +63 -9
- package/dist/slices/video/figure-card.js +492 -0
- package/dist/slices/video/figure-spans.js +51 -0
- package/dist/slices/video/plan.js +125 -13
- package/dist/slices/video/run.js +14 -12
- package/dist/slices/video/slideshow.js +39 -4
- package/dist/slices/voices/attribution.js +69 -0
- package/dist/slices/voices/audio-files.js +82 -0
- package/dist/slices/voices/audition.js +25 -0
- package/dist/slices/voices/cast.js +56 -0
- package/dist/slices/voices/grouping.js +49 -0
- package/dist/slices/voices/join.js +35 -0
- package/dist/slices/voices/languages.js +25 -0
- package/dist/slices/voices/model.js +179 -0
- package/dist/slices/voices/palette.js +26 -0
- package/dist/slices/voices/panel.js +91 -0
- package/dist/slices/voices/portraits.js +55 -0
- package/dist/slices/voices/script.js +136 -0
- package/dist/slices/voices/timing.js +58 -0
- package/dist/slices/youtube/answer.js +1 -1
- package/dist/slices/youtube/chapters.js +126 -0
- package/dist/slices/youtube/edits-repo.js +69 -0
- package/dist/slices/youtube/edits.js +89 -0
- package/dist/slices/youtube/placeholders.js +98 -0
- package/dist/slices/youtube/transcript.js +11 -4
- package/dist/updater/service.js +66 -21
- package/dist/web/assets/index-CUA2sUxU.css +1 -0
- package/dist/web/assets/index-CX590kE-.js +303 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
- package/dist/web/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
- package/dist/web/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
- package/dist/web/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
- package/dist/web/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
- package/dist/web/assets/{pdf-BLF2i8c2.js → pdf-BG8n_uc5.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +4 -4
- package/dist/edge/docker-launch.js +0 -81
- package/dist/edge/docker-projects/activation.js +0 -35
- package/dist/edge/docker-projects/claims.js +0 -85
- package/dist/edge/docker-projects/committed.js +0 -40
- package/dist/edge/docker-projects/engine.js +0 -461
- package/dist/edge/docker-projects/install.js +0 -254
- package/dist/edge/docker-projects/prune.js +0 -66
- package/dist/edge/docker-projects/recover.js +0 -100
- package/dist/edge/docker-projects/state.js +0 -225
- package/dist/web/assets/index-CktpxlJd.js +0 -171
- package/dist/web/assets/index-DB-vOnGU.css +0 -1
- package/scripts/docker-run.sh +0 -3
- /package/dist/edge/{docker-projects → docker-install}/tree.js +0 -0
|
@@ -2,8 +2,14 @@ import { z } from "zod";
|
|
|
2
2
|
import { defaultEdgeSilenceSeconds, defaultImageSeconds, defaultZoomPercent, edgeSilenceSecondsProblem, imageSecondsProblem, numberPerPromptMax, zoomPercentProblem, } from "../admission/rules.js";
|
|
3
3
|
import { runDraftSchema } from "../admission/schema.js";
|
|
4
4
|
import { draftDocumentThemeOf } from "../document/model.js";
|
|
5
|
+
import { defaultExpectedWords, everyMinutesMax, everyMinutesMin, expectedWordsMax, imagesPerHourMax, imagesPerHourMin, perHourFromMinutes, wordCount, } from "../images/scale.js";
|
|
6
|
+
import { defaultLoudness, loudnessOfForm, } from "../loudness/model.js";
|
|
7
|
+
import { pausesOfForm } from "../narration/pauses-model.js";
|
|
8
|
+
import { reviewSettingsFromForm } from "../reviews/model.js";
|
|
9
|
+
import { stageMakesItems } from "../reviews/rules.js";
|
|
5
10
|
import { defaultShorts, shortsExtrasOf, shortsSettingsProblems, } from "../shorts/model.js";
|
|
6
11
|
import { defaultSubtitles } from "../subtitles/model.js";
|
|
12
|
+
import { ambientBedOfForm, ambientBedProblems } from "../video/ambient-bed.js";
|
|
7
13
|
// Said where the control is, since the music sits in a closed disclosure on the Export rail.
|
|
8
14
|
const musicMessages = {
|
|
9
15
|
uploading: "The shorts' background music is still uploading, so the run can't start yet. Wait for it to finish, or remove it under Outputs → Export → More shorts options → Background music.",
|
|
@@ -12,6 +18,14 @@ const musicMessages = {
|
|
|
12
18
|
function pickMusic(id) {
|
|
13
19
|
return id === undefined ? {} : { shortsMusic: id };
|
|
14
20
|
}
|
|
21
|
+
const bedWhere = "under Outputs → Export → Ambient sound";
|
|
22
|
+
const bedMessages = {
|
|
23
|
+
uploading: `The ambient sound's audio file is still uploading, so the run can't start yet. Wait for it to finish ${bedWhere}.`,
|
|
24
|
+
missing: `The ambient sound's audio file is missing (it was never chosen, was not uploaded again after the draft was copied, or the upload failed). Choose the file ${bedWhere}, or pick Rain, Fireplace or Wind instead.`,
|
|
25
|
+
};
|
|
26
|
+
function pickBed(id) {
|
|
27
|
+
return id === undefined ? {} : { ambientBed: id };
|
|
28
|
+
}
|
|
15
29
|
export function toAdmissionDraft(input) {
|
|
16
30
|
const { form } = input.document;
|
|
17
31
|
const fields = [];
|
|
@@ -108,7 +122,28 @@ export function toAdmissionDraft(input) {
|
|
|
108
122
|
: defaultShorts.maxSeconds,
|
|
109
123
|
};
|
|
110
124
|
};
|
|
125
|
+
// Only the reviews of stages that make something; none left is no reviews at all, which is
|
|
126
|
+
// what every draft saved before them was.
|
|
127
|
+
const reviewed = form.reviews === undefined
|
|
128
|
+
? undefined
|
|
129
|
+
: reviewSettingsFromForm(form.reviews, (stage) => stageMakesItems({ sources, shorts: shortsOn ? { enabled: true } : undefined }, stage));
|
|
130
|
+
if (reviewed?.retriesProblem !== undefined)
|
|
131
|
+
fields.push({ field: "reviews.retries", message: reviewed.retriesProblem });
|
|
132
|
+
const reviews = reviewed?.settings;
|
|
133
|
+
// The ambient bed lies under the long video's narration, so a bed left set with either Off
|
|
134
|
+
// asks for nothing. Refused in the rule's words, so Play and the run say the same sentence.
|
|
135
|
+
const bed = sources.video === "generate" && sources.audio !== "off"
|
|
136
|
+
? ambientBedOfForm(form.ambientBed)
|
|
137
|
+
: undefined;
|
|
138
|
+
const bedProblems = bed === undefined ? [] : ambientBedProblems(bed);
|
|
139
|
+
for (const problem of bedProblems)
|
|
140
|
+
fields.push({
|
|
141
|
+
field: `ambientBed.${problem.field}`,
|
|
142
|
+
message: `${problem.message} Change it ${bedWhere}.`,
|
|
143
|
+
});
|
|
144
|
+
const bedRefused = bedProblems.length;
|
|
111
145
|
const draft = {
|
|
146
|
+
...(reviews === undefined ? {} : { reviews }),
|
|
112
147
|
...(form.checkpoints === undefined ? {} : { checkpoints: form.checkpoints }),
|
|
113
148
|
title: form.title,
|
|
114
149
|
format: form.format,
|
|
@@ -123,11 +158,12 @@ export function toAdmissionDraft(input) {
|
|
|
123
158
|
: {}),
|
|
124
159
|
llm: form.llm,
|
|
125
160
|
audio: sources.audio === "generate" || form.audio.usePronunciationGlossary !== undefined
|
|
126
|
-
?
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
161
|
+
? onlyWhenOn(
|
|
162
|
+
// With the glossary on, sharing it is on unless the draft turned it off, as Play
|
|
163
|
+
// shows it for a draft saved before the switch existed.
|
|
164
|
+
form.audio.usePronunciationGlossary === true && form.audio.shareGlossary === undefined
|
|
165
|
+
? { ...form.audio, shareGlossary: true }
|
|
166
|
+
: form.audio)
|
|
131
167
|
: undefined,
|
|
132
168
|
images: form.images,
|
|
133
169
|
// The establishing image belongs to generated images; Off, or images not generated,
|
|
@@ -143,10 +179,25 @@ export function toAdmissionDraft(input) {
|
|
|
143
179
|
},
|
|
144
180
|
}
|
|
145
181
|
: {}),
|
|
182
|
+
// Three only for a thumbnail the image provider draws; one leaves it out, which is what
|
|
183
|
+
// every draft saved before it was.
|
|
184
|
+
...(form.thumbnailCount === 3 &&
|
|
185
|
+
(sources.thumbnail === "from_prompt" || sources.thumbnail === "prompt_by_llm")
|
|
186
|
+
? { thumbnailCount: 3 }
|
|
187
|
+
: {}),
|
|
146
188
|
articlePrompt: sources.article === "generate" ? form.articlePrompt : undefined,
|
|
147
189
|
...(sources.audio === "generate" && form.narrationPrompt?.trim()
|
|
148
190
|
? { narrationPrompt: form.narrationPrompt }
|
|
149
191
|
: {}),
|
|
192
|
+
// English is never stored on the run, so an English project is the run it always was.
|
|
193
|
+
...(form.language === undefined || form.language === "en" ? {} : { language: form.language }),
|
|
194
|
+
// Drawn for the blocks the narration describes, so only with a video and describing on.
|
|
195
|
+
...(sources.video === "generate" &&
|
|
196
|
+
sources.audio === "generate" &&
|
|
197
|
+
form.audio.describeFigures === true &&
|
|
198
|
+
form.showFigures === true
|
|
199
|
+
? { showFigures: true }
|
|
200
|
+
: {}),
|
|
150
201
|
// Timed from the narration, so a switch left on with narration Off asks for nothing.
|
|
151
202
|
...(sources.audio !== "off" && form.youtubeDescription === true
|
|
152
203
|
? {
|
|
@@ -163,6 +214,9 @@ export function toAdmissionDraft(input) {
|
|
|
163
214
|
number: number(prompt.number, `imagePrompts.${index}.number`, numberPerPromptMax),
|
|
164
215
|
}))
|
|
165
216
|
: [],
|
|
217
|
+
...(sources.images === "generate" && form.imageScale !== undefined
|
|
218
|
+
? { imageScale: imageScaleOf(input.document, fields) }
|
|
219
|
+
: {}),
|
|
166
220
|
thumbnailPrompt: ["from_prompt", "prompt_by_llm"].includes(sources.thumbnail)
|
|
167
221
|
? form.thumbnailPrompt
|
|
168
222
|
: undefined,
|
|
@@ -192,6 +246,10 @@ export function toAdmissionDraft(input) {
|
|
|
192
246
|
...(shortsOn && form.provided.shortsMusic
|
|
193
247
|
? pickMusic(file(form.provided.shortsMusic, "audio", "shorts.music", musicMessages))
|
|
194
248
|
: {}),
|
|
249
|
+
// Only while the bed plays the user's own file; kept on the draft otherwise.
|
|
250
|
+
...(bed?.source === "upload"
|
|
251
|
+
? pickBed(file(form.provided.ambientBed ?? null, "audio", "ambientBed.file", bedMessages))
|
|
252
|
+
: {}),
|
|
195
253
|
},
|
|
196
254
|
chunking: {
|
|
197
255
|
mode: chunkMode,
|
|
@@ -209,7 +267,11 @@ export function toAdmissionDraft(input) {
|
|
|
209
267
|
mode,
|
|
210
268
|
fontSize: number(form.subtitles.fontSize, "subtitles.fontSize", 120, 16),
|
|
211
269
|
},
|
|
212
|
-
|
|
270
|
+
// This run's own gap when one was typed, else the one Settings has. Text that is not a
|
|
271
|
+
// number is NaN, which admission refuses at silenceGapSeconds.
|
|
272
|
+
silenceGapSeconds: form.silenceGapSeconds === undefined || form.silenceGapSeconds.trim() === ""
|
|
273
|
+
? input.silenceGapSeconds
|
|
274
|
+
: Number(form.silenceGapSeconds),
|
|
213
275
|
imageSeconds: measure("imageSeconds", sources.video === "generate" || shortsOn, defaultImageSeconds, imageSecondsProblem),
|
|
214
276
|
zoomPercent: measure("zoomPercent", sources.video === "generate", defaultZoomPercent, zoomPercentProblem),
|
|
215
277
|
// A choice from a list, so there is nothing to refuse; a run without a video keeps it
|
|
@@ -217,6 +279,15 @@ export function toAdmissionDraft(input) {
|
|
|
217
279
|
motionStyle: form.motionStyle,
|
|
218
280
|
// Kept the same way; admission checks it only while the video renders.
|
|
219
281
|
...(form.videoEdit === undefined ? {} : { videoEdit: form.videoEdit }),
|
|
282
|
+
// Spoken by the speakers only while narration is generated.
|
|
283
|
+
...(form.voices !== undefined && sources.audio === "generate" ? { voices: form.voices } : {}),
|
|
284
|
+
// Under the long video's narration only; a number already refused above leaves it out
|
|
285
|
+
// rather than saved as NaN.
|
|
286
|
+
...(bed !== undefined && bedRefused === 0 ? { ambientBed: bed } : {}),
|
|
287
|
+
// Only while on, and only with a narration to level, so the run is otherwise the one it was.
|
|
288
|
+
...pickLoudness(sources.audio, loudnessOfForm(form.loudness, input.loudness ?? defaultLoudness)),
|
|
289
|
+
// Pauses between sentences: the default for a new run, with a narration to pace.
|
|
290
|
+
...(sources.audio === "generate" ? pausesOfForm(form) : {}),
|
|
220
291
|
edgeSilenceSeconds: measure("edgeSilenceSeconds", sources.audio !== "off", defaultEdgeSilenceSeconds, edgeSilenceSecondsProblem),
|
|
221
292
|
};
|
|
222
293
|
const parsed = runDraftSchema.safeParse(draft);
|
|
@@ -230,3 +301,51 @@ export function toAdmissionDraft(input) {
|
|
|
230
301
|
? { ok: false, fields }
|
|
231
302
|
: { ok: true, draft: parsed.data };
|
|
232
303
|
}
|
|
304
|
+
function pickLoudness(audio, loudness) {
|
|
305
|
+
return audio === "off" || loudness === undefined ? {} : { loudness };
|
|
306
|
+
}
|
|
307
|
+
// More images for long videos, from what Play's control holds: the rate as images per hour,
|
|
308
|
+
// and the narration length it is planned for, which is the provided article's own words or
|
|
309
|
+
// else Review's expected words (`images/scale.ts`).
|
|
310
|
+
function imageScaleOf(document, fields) {
|
|
311
|
+
const { form } = document;
|
|
312
|
+
const typed = form.imageScale?.value.trim() ?? "";
|
|
313
|
+
const value = typed === "" ? Number.NaN : Number(typed);
|
|
314
|
+
const minutes = form.imageScale?.every !== "hour";
|
|
315
|
+
const inRange = minutes
|
|
316
|
+
? value >= everyMinutesMin && value <= everyMinutesMax
|
|
317
|
+
: value >= imagesPerHourMin && value <= imagesPerHourMax;
|
|
318
|
+
if (!inRange)
|
|
319
|
+
fields.push({
|
|
320
|
+
field: "imageScale.value",
|
|
321
|
+
message: minutes
|
|
322
|
+
? `Enter a number of minutes between ${String(everyMinutesMin)} and ${String(everyMinutesMax)} under Images → More images for long videos.`
|
|
323
|
+
: `Enter a number of images per hour between ${String(imagesPerHourMin)} and ${String(imagesPerHourMax)} under Images → More images for long videos.`,
|
|
324
|
+
});
|
|
325
|
+
const perHour = !inRange
|
|
326
|
+
? imagesPerHourMin
|
|
327
|
+
: minutes
|
|
328
|
+
? perHourFromMinutes(value)
|
|
329
|
+
: Math.round(value * 10000) / 10000;
|
|
330
|
+
if (form.sources.article === "provide")
|
|
331
|
+
return { perHour, words: Math.max(1, wordCount(form.provided.article)) };
|
|
332
|
+
// Review checks the expected words on its own (`review-inputs.ts`); a draft whose figure is
|
|
333
|
+
// not a whole number yet plans for Play's starting one.
|
|
334
|
+
const expected = Number(document.expectedWords.trim());
|
|
335
|
+
return {
|
|
336
|
+
perHour,
|
|
337
|
+
words: Number.isInteger(expected) && expected >= 1 && expected <= expectedWordsMax
|
|
338
|
+
? expected
|
|
339
|
+
: defaultExpectedWords,
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
// "Describe tables and figures" and "Skip code" are stored only when on, so a run started
|
|
343
|
+
// with them off saves the config every run made before them did.
|
|
344
|
+
function onlyWhenOn(audio) {
|
|
345
|
+
const { describeFigures, skipCode, ...rest } = audio;
|
|
346
|
+
return {
|
|
347
|
+
...rest,
|
|
348
|
+
...(describeFigures === true ? { describeFigures } : {}),
|
|
349
|
+
...(describeFigures === true && skipCode === true ? { skipCode } : {}),
|
|
350
|
+
};
|
|
351
|
+
}
|
|
@@ -2,6 +2,7 @@ import { checkRuntimeModel } from "../../catalog/runtime-models.js";
|
|
|
2
2
|
import { modelFields } from "../../catalog/validate.js";
|
|
3
3
|
import { readinessIsUsable } from "../../kernel/ports/model.js";
|
|
4
4
|
import { usesNarrationPreparation, usesShorts, usesYoutubeDescription, } from "../admission/rules.js";
|
|
5
|
+
import { usesReviews } from "../reviews/rules.js";
|
|
5
6
|
import { cliPathChanged } from "../settings/cli-paths.js";
|
|
6
7
|
import { hasKey, listVoices } from "../settings/repo.js";
|
|
7
8
|
import { usesAnimation } from "../video/edit-settings.js";
|
|
@@ -36,6 +37,8 @@ export function choices(runs) {
|
|
|
36
37
|
usesShorts(d) ||
|
|
37
38
|
usesAnimation(d),
|
|
38
39
|
},
|
|
40
|
+
// The reviewer is its own text model, checked like the LLM row.
|
|
41
|
+
{ field: "reviews", family: "llm", choice: d.reviews, needed: usesReviews(d) },
|
|
39
42
|
])
|
|
40
43
|
.flatMap((c) => (c.needed && c.choice ? [{ ...c, ...c.choice }] : []));
|
|
41
44
|
return [...new Map(selected.map((c) => [`${c.family}:${c.provider}:${c.model}`, c])).values()];
|
|
@@ -58,6 +58,8 @@ export function attachmentRefs(document) {
|
|
|
58
58
|
: [{ ...p.reference, kind: "reference" }]),
|
|
59
59
|
// The shorts' music is staged like narration audio, so it is an audio attachment.
|
|
60
60
|
...(p.shortsMusic == null ? [] : [{ ...p.shortsMusic, kind: "audio" }]),
|
|
61
|
+
// So is the ambient bed's own file.
|
|
62
|
+
...(p.ambientBed == null ? [] : [{ ...p.ambientBed, kind: "audio" }]),
|
|
61
63
|
];
|
|
62
64
|
}
|
|
63
65
|
export function requestHash(value) {
|
|
@@ -3,6 +3,7 @@ import { readFileSync, statSync } from "node:fs";
|
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { modelFields } from "../../catalog/validate.js";
|
|
5
5
|
import { admit } from "../admission/rules.js";
|
|
6
|
+
import { brandedForm, brandedRun, castSnapshot, castVoicedRun, draftChannel, } from "../channels/runs.js";
|
|
6
7
|
import { estimateRun } from "../estimate/index.js";
|
|
7
8
|
import { listEntries } from "../library/repo.js";
|
|
8
9
|
import { pickTemplates, renderPicked } from "../library/slots.js";
|
|
@@ -51,12 +52,27 @@ export function resolveReviewInputs(deps, view, font) {
|
|
|
51
52
|
field: "variants",
|
|
52
53
|
message: "You can review at most 50 videos at once, counting the first one. Remove some variations.",
|
|
53
54
|
});
|
|
54
|
-
const
|
|
55
|
-
|
|
55
|
+
const entries = [...(document.librarySnapshot?.entries ?? []), ...listEntries(deps.db)];
|
|
56
|
+
// The channel's brand kit fills what the setup leaves at its default, and its cast rides
|
|
57
|
+
// along for the images (`slices/channels/runs.ts`).
|
|
58
|
+
const channel = draftChannel(deps.db, document);
|
|
59
|
+
const useBrandKit = document.form.useBrandKit !== false;
|
|
60
|
+
const branded = toAdmissionDraft({
|
|
61
|
+
document: {
|
|
62
|
+
...document,
|
|
63
|
+
form: brandedForm(deps.db, document.form, channel.brand, entries),
|
|
64
|
+
},
|
|
56
65
|
attachments: fresh.value.attachments,
|
|
57
|
-
entries
|
|
66
|
+
entries,
|
|
58
67
|
silenceGapSeconds: readSettings(deps).silenceGapSeconds,
|
|
68
|
+
loudness: readSettings(deps).loudness,
|
|
59
69
|
});
|
|
70
|
+
const converted = branded.ok
|
|
71
|
+
? {
|
|
72
|
+
ok: true,
|
|
73
|
+
draft: castVoicedRun(deps.db, brandedRun(branded.draft, channel, castSnapshot(deps.db, channel.id), useBrandKit)),
|
|
74
|
+
}
|
|
75
|
+
: branded;
|
|
60
76
|
if (!converted.ok)
|
|
61
77
|
fields.push(...converted.fields);
|
|
62
78
|
if (!converted.ok || !words.success || fields.length)
|
|
@@ -113,6 +129,7 @@ export function resolveReviewInputs(deps, view, font) {
|
|
|
113
129
|
run.draft.provided.reference,
|
|
114
130
|
...(run.draft.provided.images ?? []),
|
|
115
131
|
run.draft.provided.shortsMusic,
|
|
132
|
+
run.draft.provided.ambientBed,
|
|
116
133
|
]));
|
|
117
134
|
const attachmentIdentity = fresh.value.attachments.flatMap((file) => file.stagedFileId !== null && activeIds.has(file.stagedFileId)
|
|
118
135
|
? [{ id: file.id, stagedFileId: file.stagedFileId, bytes: file.bytes }]
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { formats, stageKinds } from "../../kernel/pipeline.js";
|
|
3
|
+
import { languageSchema } from "../../kernel/ports/languages.js";
|
|
3
4
|
import { thinkingModes } from "../../kernel/ports/llm.js";
|
|
4
5
|
import { motionStyles, referenceSources, stageSources } from "../admission/model.js";
|
|
5
6
|
import { defaultEdgeSilenceSeconds, defaultImageSeconds, defaultMotionStyle, defaultZoomPercent, } from "../admission/rules.js";
|
|
@@ -8,7 +9,10 @@ import { checkpointRowSchema, checkpointStageSchema } from "../checkpoints/schem
|
|
|
8
9
|
import { documentSettingsSchema } from "../document/theme-schema.js";
|
|
9
10
|
import { librarySnapshotSchema } from "../library/snapshot.js";
|
|
10
11
|
import { chunkModes } from "../narration/chunk.js";
|
|
12
|
+
import { reviewModes, reviewStages } from "../reviews/model.js";
|
|
11
13
|
import { subtitleModes, subtitlePositions } from "../subtitles/model.js";
|
|
14
|
+
import { ambientBedSources } from "../video/ambient-bed.js";
|
|
15
|
+
import { voicesSettingsSchema } from "../voices/model.js";
|
|
12
16
|
const id = z.uuid();
|
|
13
17
|
const text = z.string();
|
|
14
18
|
const values = z.record(text, text).readonly();
|
|
@@ -44,6 +48,9 @@ export const playDraftFormSchema = z
|
|
|
44
48
|
voice: text,
|
|
45
49
|
usePronunciationGlossary: z.boolean().optional(),
|
|
46
50
|
shareGlossary: z.boolean().optional(),
|
|
51
|
+
useNarrationAliases: z.boolean().optional(),
|
|
52
|
+
describeFigures: z.boolean().optional(),
|
|
53
|
+
skipCode: z.boolean().optional(),
|
|
47
54
|
})
|
|
48
55
|
.readonly(),
|
|
49
56
|
images: provider.readonly(),
|
|
@@ -60,9 +67,33 @@ export const playDraftFormSchema = z
|
|
|
60
67
|
.optional(),
|
|
61
68
|
articlePrompt: text,
|
|
62
69
|
narrationPrompt: text.optional(),
|
|
70
|
+
// Absent on drafts and templates saved before three thumbnails: one.
|
|
71
|
+
thumbnailCount: z.union([z.literal(1), z.literal(3)]).optional(),
|
|
72
|
+
// Level the volume (`loudness/model.ts`). Absent on drafts and templates nobody changed it
|
|
73
|
+
// in: Settings → General's default, which is on for a new run. The targets are LUFS; absent
|
|
74
|
+
// is Settings' own.
|
|
75
|
+
loudness: z
|
|
76
|
+
.object({
|
|
77
|
+
enabled: z.boolean(),
|
|
78
|
+
videoLufs: z.number().optional(),
|
|
79
|
+
audioFilesLufs: z.number().optional(),
|
|
80
|
+
})
|
|
81
|
+
.strict()
|
|
82
|
+
.readonly()
|
|
83
|
+
.optional(),
|
|
84
|
+
// Pauses between sentences and paragraphs in seconds, raw text like every number on Play.
|
|
85
|
+
// Absent on drafts and templates nobody changed them in: the sentence default and no
|
|
86
|
+
// paragraph minimum (`narration/pauses-model.ts`).
|
|
87
|
+
sentencePause: text.optional(),
|
|
88
|
+
paragraphPause: text.optional(),
|
|
63
89
|
// Absent on drafts and templates saved before the YouTube description: off, built-in prompt.
|
|
64
90
|
youtubeDescription: z.boolean().optional(),
|
|
91
|
+
showFigures: z.boolean().optional(),
|
|
65
92
|
descriptionPrompt: text.optional(),
|
|
93
|
+
// The project language (`kernel/ports/languages.ts`). Absent on drafts and templates saved
|
|
94
|
+
// before it, and on a draft nobody picked one for: the channel's language, else English.
|
|
95
|
+
// English picked on purpose is kept as "en", so a channel's language does not replace it.
|
|
96
|
+
language: languageSchema.optional(),
|
|
66
97
|
// Absent on drafts and templates saved before Shorts: off. The numbers are raw text, like
|
|
67
98
|
// every other number on Play; the prompts are names, "" being the built-in ones.
|
|
68
99
|
shorts: z
|
|
@@ -83,7 +114,33 @@ export const playDraftFormSchema = z
|
|
|
83
114
|
.strict()
|
|
84
115
|
.readonly()
|
|
85
116
|
.optional(),
|
|
117
|
+
// Absent on drafts and templates saved before automatic reviews: every review Off. The
|
|
118
|
+
// retries are raw text like every other number on Play; a stage's prompt is a Review
|
|
119
|
+
// prompt's name, "" being the built-in one.
|
|
120
|
+
reviews: z
|
|
121
|
+
.object({
|
|
122
|
+
provider: text,
|
|
123
|
+
model: text,
|
|
124
|
+
thinking: z.enum(thinkingModes).optional(),
|
|
125
|
+
retries: text,
|
|
126
|
+
stages: z
|
|
127
|
+
.partialRecord(z.enum(reviewStages), z
|
|
128
|
+
.object({ mode: z.enum(reviewModes), prompt: text })
|
|
129
|
+
.strict()
|
|
130
|
+
.readonly())
|
|
131
|
+
.readonly(),
|
|
132
|
+
})
|
|
133
|
+
.strict()
|
|
134
|
+
.readonly()
|
|
135
|
+
.optional(),
|
|
86
136
|
imagePrompts: z.array(z.object({ name: text, number: text }).strict().readonly()).readonly(),
|
|
137
|
+
// More images for long videos, as typed: "every [value] minutes" or "[value] per hour".
|
|
138
|
+
// Absent is off, which is what every draft and template saved before it was.
|
|
139
|
+
imageScale: z
|
|
140
|
+
.object({ every: z.enum(["minutes", "hour"]), value: text })
|
|
141
|
+
.strict()
|
|
142
|
+
.readonly()
|
|
143
|
+
.optional(),
|
|
87
144
|
thumbnailPrompt: text,
|
|
88
145
|
intro: text,
|
|
89
146
|
outro: text,
|
|
@@ -107,9 +164,30 @@ export const playDraftFormSchema = z
|
|
|
107
164
|
edgeSilenceSeconds: text.default(String(defaultEdgeSilenceSeconds)),
|
|
108
165
|
zoomPercent: text.default(String(defaultZoomPercent)),
|
|
109
166
|
motionStyle: z.enum(motionStyles).default(defaultMotionStyle),
|
|
167
|
+
// The silence between narration segments for this run, raw text like the numbers above.
|
|
168
|
+
// Absent or empty: the gap Settings has.
|
|
169
|
+
silenceGapSeconds: text.optional(),
|
|
110
170
|
// Absent on drafts and templates saved before the edit settings: today's slideshow. Every
|
|
111
171
|
// field is a pick from a list, so it is kept as the settings themselves.
|
|
112
172
|
videoEdit: videoEditSchema.strict().readonly().optional(),
|
|
173
|
+
// Whether the channel's brand kit fills what this setup leaves at its default
|
|
174
|
+
// (`slices/channels/runs.ts`). Absent is on.
|
|
175
|
+
useBrandKit: z.boolean().optional(),
|
|
176
|
+
// Absent on drafts and templates saved before multiple voices: the Narration format. Every
|
|
177
|
+
// number is a pick from a list, so it is kept as the settings themselves.
|
|
178
|
+
voices: voicesSettingsSchema.strict().readonly().optional(),
|
|
179
|
+
// The ambient bed under the long video (`video/ambient-bed.ts`), numbers as typed. Absent
|
|
180
|
+
// takes the channel's brand kit's, if any; "none" asks for no bed whatever the channel has.
|
|
181
|
+
ambientBed: z
|
|
182
|
+
.object({
|
|
183
|
+
source: z.enum(["none", ...ambientBedSources]),
|
|
184
|
+
level: text,
|
|
185
|
+
fadeIn: text,
|
|
186
|
+
tail: text,
|
|
187
|
+
})
|
|
188
|
+
.strict()
|
|
189
|
+
.readonly()
|
|
190
|
+
.optional(),
|
|
113
191
|
values,
|
|
114
192
|
provided: z
|
|
115
193
|
.object({
|
|
@@ -123,6 +201,9 @@ export const playDraftFormSchema = z
|
|
|
123
201
|
// The Shorts step's background music, uploaded as an audio attachment. Absent on
|
|
124
202
|
// drafts and templates saved before Play offered it: no music.
|
|
125
203
|
shortsMusic: file.nullable().optional(),
|
|
204
|
+
// The ambient bed's own file, an audio attachment; used only while its source is
|
|
205
|
+
// "upload". Absent on drafts and templates saved before it.
|
|
206
|
+
ambientBed: file.nullable().optional(),
|
|
126
207
|
})
|
|
127
208
|
.strict()
|
|
128
209
|
.readonly(),
|
|
@@ -138,6 +219,8 @@ export const playDraftDocumentSchema = z
|
|
|
138
219
|
.strict()
|
|
139
220
|
.readonly()
|
|
140
221
|
.optional(),
|
|
222
|
+
// The channel picked on Play. Absent runs in the template's channel, or the default one.
|
|
223
|
+
channelId: id.optional(),
|
|
141
224
|
form: playDraftFormSchema,
|
|
142
225
|
section: z.enum(["content", "outputs", "style", "review"]),
|
|
143
226
|
variants: z.array(z.object({ id, title: text, values }).strict().readonly()).readonly(),
|
|
@@ -220,6 +303,9 @@ const costEstimateSchema = z
|
|
|
220
303
|
low: z.number().nullable(),
|
|
221
304
|
high: z.number().nullable(),
|
|
222
305
|
detail: text,
|
|
306
|
+
onPlan: z.boolean().optional(),
|
|
307
|
+
apiLow: z.number().nullable().optional(),
|
|
308
|
+
apiHigh: z.number().nullable().optional(),
|
|
223
309
|
})
|
|
224
310
|
.strict()
|
|
225
311
|
.readonly())
|
|
@@ -227,6 +313,9 @@ const costEstimateSchema = z
|
|
|
227
313
|
low: z.number(),
|
|
228
314
|
high: z.number(),
|
|
229
315
|
unknown: z.number(),
|
|
316
|
+
apiLow: z.number().optional(),
|
|
317
|
+
apiHigh: z.number().optional(),
|
|
318
|
+
apiUnknown: z.number().optional(),
|
|
230
319
|
expectedWords: z.number(),
|
|
231
320
|
catalogueDate: text.nullable(),
|
|
232
321
|
assumptions: z.array(text).readonly(),
|
|
@@ -44,7 +44,7 @@ function parseStoredJson(value) {
|
|
|
44
44
|
throw error;
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
|
-
function documentOf(row) {
|
|
47
|
+
export function documentOf(row) {
|
|
48
48
|
if (row.schema_version !== 1)
|
|
49
49
|
return null;
|
|
50
50
|
const parsed = playDraftDocumentSchema.safeParse(parseStoredJson(row.document_json));
|
|
@@ -214,6 +214,9 @@ export function forkDraft(deps, input) {
|
|
|
214
214
|
...(p.shortsMusic === undefined
|
|
215
215
|
? {}
|
|
216
216
|
: { shortsMusic: p.shortsMusic === null ? null : rewrite(p.shortsMusic) }),
|
|
217
|
+
...(p.ambientBed === undefined
|
|
218
|
+
? {}
|
|
219
|
+
: { ambientBed: p.ambientBed === null ? null : rewrite(p.ambientBed) }),
|
|
217
220
|
},
|
|
218
221
|
},
|
|
219
222
|
};
|
|
@@ -80,7 +80,47 @@ export async function startPlayDraft(deps, input) {
|
|
|
80
80
|
};
|
|
81
81
|
const checked = await checkDraftReadiness(captured, claimed.value.runs);
|
|
82
82
|
const oldFiles = attachmentRows(deps.db, input.draftId).flatMap((a) => a.staged_file_id === null ? [] : [a.staged_file_id]);
|
|
83
|
-
|
|
83
|
+
let result;
|
|
84
|
+
try {
|
|
85
|
+
result = createReviewedRuns(deps, input, captured, checked, catalogue);
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
if (!(error instanceof StartedRunMismatch))
|
|
89
|
+
throw error;
|
|
90
|
+
// Nothing the review promised was created: the transaction rolled back.
|
|
91
|
+
releaseStartClaim(deps, input, true);
|
|
92
|
+
try {
|
|
93
|
+
deps.log.write("error", "play.start.run-count", { detail: error.message });
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
/* The refusal below still reaches the person. */
|
|
97
|
+
}
|
|
98
|
+
return { ok: false, reason: "stale-review", currentVersion: input.baseVersion, fields: [] };
|
|
99
|
+
}
|
|
100
|
+
if (!result.ok) {
|
|
101
|
+
releaseStartClaim(deps, input, result.reason === "stale-review");
|
|
102
|
+
return result;
|
|
103
|
+
}
|
|
104
|
+
if (!result.value.replayed) {
|
|
105
|
+
afterCommit(deps, () => deps.recordStarted(result.value.projectIds));
|
|
106
|
+
for (const id of oldFiles)
|
|
107
|
+
afterCommit(deps, () => releaseStagedFile(deps, id));
|
|
108
|
+
afterCommit(deps, () => {
|
|
109
|
+
if (result.value.queue.length)
|
|
110
|
+
pumpQueue(deps.db, deps.runner);
|
|
111
|
+
else
|
|
112
|
+
for (const id of result.value.projectIds)
|
|
113
|
+
deps.runner.tick(id);
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
// Start creates exactly the runs the person reviewed: one project for one run, otherwise a
|
|
119
|
+
// queue entry and project per run. Anything else is thrown so the transaction rolls back.
|
|
120
|
+
class StartedRunMismatch extends Error {
|
|
121
|
+
}
|
|
122
|
+
function createReviewedRuns(deps, input, captured, checked, catalogue) {
|
|
123
|
+
return transact(deps.db, () => {
|
|
84
124
|
const receipt = readStartReceipt(deps.db, input.reviewId);
|
|
85
125
|
if (receipt)
|
|
86
126
|
return replayResult(receipt, input);
|
|
@@ -109,27 +149,12 @@ export async function startPlayDraft(deps, input) {
|
|
|
109
149
|
replayed: false,
|
|
110
150
|
...(checkpointSet.length ? { checkpointSet } : {}),
|
|
111
151
|
};
|
|
152
|
+
if (projectIds.length !== runs.length || queue.length !== (runs.length === 1 ? 0 : runs.length))
|
|
153
|
+
throw new StartedRunMismatch(`Start would create ${projectIds.length} of ${runs.length} reviewed videos (${queue.length} queued).`);
|
|
112
154
|
insertStartReceipt(deps, input, value);
|
|
113
155
|
markDraftStartedAndReleaseRefs(deps.db, input);
|
|
114
156
|
return { ok: true, value };
|
|
115
157
|
});
|
|
116
|
-
if (!result.ok) {
|
|
117
|
-
releaseStartClaim(deps, input, result.reason === "stale-review");
|
|
118
|
-
return result;
|
|
119
|
-
}
|
|
120
|
-
if (!result.value.replayed) {
|
|
121
|
-
afterCommit(deps, () => deps.recordStarted(result.value.projectIds));
|
|
122
|
-
for (const id of oldFiles)
|
|
123
|
-
afterCommit(deps, () => releaseStagedFile(deps, id));
|
|
124
|
-
afterCommit(deps, () => {
|
|
125
|
-
if (result.value.queue.length)
|
|
126
|
-
pumpQueue(deps.db, deps.runner);
|
|
127
|
-
else
|
|
128
|
-
for (const id of result.value.projectIds)
|
|
129
|
-
deps.runner.tick(id);
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
return result;
|
|
133
158
|
}
|
|
134
159
|
function afterCommit(deps, run) {
|
|
135
160
|
try {
|
|
@@ -3,11 +3,16 @@ import { transact } from "../../kernel/db/tx.js";
|
|
|
3
3
|
import { sourceOf } from "../admission/model.js";
|
|
4
4
|
import { projectById } from "../admission/repo.js";
|
|
5
5
|
import { detectSlots } from "../admission/substitute.js";
|
|
6
|
+
import { projectChannelId } from "../channels/repo.js";
|
|
6
7
|
import { listCheckpoints } from "../checkpoints/repo.js";
|
|
7
8
|
import { documentThemeOf } from "../document/model.js";
|
|
9
|
+
import { imageScaleForm } from "../images/scale.js";
|
|
8
10
|
import { requestHash } from "../play-drafts/repo.js";
|
|
11
|
+
import { reviewSettingsForm } from "../reviews/model.js";
|
|
9
12
|
import { currentRevisionId, revisionById } from "../revisions/repo.js";
|
|
10
13
|
import { shortsExtrasForm } from "../shorts/model.js";
|
|
14
|
+
import { ambientBedFormOf } from "../video/ambient-bed.js";
|
|
15
|
+
import { usesScriptPrompt } from "../voices/model.js";
|
|
11
16
|
import { createTemplate, readTemplate } from "./service.js";
|
|
12
17
|
export const projectTemplateCreateSchema = z
|
|
13
18
|
.object({
|
|
@@ -47,7 +52,11 @@ export function createTemplateFromProject(deps, input) {
|
|
|
47
52
|
const result = createTemplate(deps, {
|
|
48
53
|
id: parsed.data.id,
|
|
49
54
|
name: parsed.data.name,
|
|
50
|
-
|
|
55
|
+
// The project's channel, so the template lands where the video came from.
|
|
56
|
+
document: {
|
|
57
|
+
...documentFromProject(deps, revision),
|
|
58
|
+
channelId: projectChannelId(deps.db, project.id),
|
|
59
|
+
},
|
|
51
60
|
});
|
|
52
61
|
if (result.ok)
|
|
53
62
|
deps.db
|
|
@@ -75,14 +84,21 @@ function documentFromProject(deps, revision) {
|
|
|
75
84
|
updatedAt: revision.createdAt,
|
|
76
85
|
});
|
|
77
86
|
};
|
|
78
|
-
addPrompt("article", config.articlePrompt, "article");
|
|
87
|
+
addPrompt(usesScriptPrompt(config) ? "script" : "article", config.articlePrompt, "article");
|
|
79
88
|
addPrompt("narration", config.narrationPrompt, "narration");
|
|
80
89
|
addPrompt("description", config.descriptionPrompt, "description");
|
|
90
|
+
for (const [stage, picked] of Object.entries(config.reviews?.stages ?? {}))
|
|
91
|
+
addPrompt("review", picked.prompt, `review.${stage}`);
|
|
81
92
|
const definitions = revision.content.imageOrder.flatMap((key) => {
|
|
82
93
|
const definition = revision.content.imageDefinitions[key];
|
|
83
94
|
return definition === undefined ? [] : [definition];
|
|
84
95
|
});
|
|
85
|
-
const imagePrompts =
|
|
96
|
+
const imagePrompts =
|
|
97
|
+
// A project that scales its images with the narration's length keeps its ticked prompts
|
|
98
|
+
// and the setting, rather than one prompt per image of the count it planned.
|
|
99
|
+
config.sources.images === "generate" &&
|
|
100
|
+
definitions.length > 0 &&
|
|
101
|
+
config.imageScale === undefined
|
|
86
102
|
? definitions.map((definition, index) => {
|
|
87
103
|
const key = definition.templateKey ?? "";
|
|
88
104
|
const original = revision.content.promptTemplates[key];
|
|
@@ -139,6 +155,9 @@ function documentFromProject(deps, revision) {
|
|
|
139
155
|
...(config.shorts?.enabled === true && revision.content.shortsMusic !== undefined
|
|
140
156
|
? { shortsMusic: attachment("Music from project") }
|
|
141
157
|
: {}),
|
|
158
|
+
...(config.ambientBed?.source === "upload" && revision.content.ambientBed !== undefined
|
|
159
|
+
? { ambientBed: attachment("Ambient sound from project") }
|
|
160
|
+
: {}),
|
|
142
161
|
};
|
|
143
162
|
return {
|
|
144
163
|
schemaVersion: 1,
|
|
@@ -168,8 +187,10 @@ function documentFromProject(deps, revision) {
|
|
|
168
187
|
},
|
|
169
188
|
}),
|
|
170
189
|
articlePrompt: config.articlePrompt ?? "",
|
|
190
|
+
...(config.thumbnailCount === 3 ? { thumbnailCount: 3 } : {}),
|
|
171
191
|
...(config.narrationPrompt === undefined ? {} : { narrationPrompt: config.narrationPrompt }),
|
|
172
192
|
...(config.youtubeDescription === true ? { youtubeDescription: true } : {}),
|
|
193
|
+
...(config.language === undefined ? {} : { language: config.language }),
|
|
173
194
|
...(config.descriptionPrompt === undefined
|
|
174
195
|
? {}
|
|
175
196
|
: { descriptionPrompt: config.descriptionPrompt }),
|
|
@@ -189,7 +210,31 @@ function documentFromProject(deps, revision) {
|
|
|
189
210
|
// A project without edit settings makes a template without them, so a video made from
|
|
190
211
|
// it cuts every N seconds like the project did.
|
|
191
212
|
...(config.videoEdit === undefined ? {} : { videoEdit: config.videoEdit }),
|
|
213
|
+
...(config.reviews === undefined ? {} : { reviews: reviewSettingsForm(config.reviews) }),
|
|
214
|
+
...(config.voices === undefined ? {} : { voices: config.voices }),
|
|
215
|
+
...(config.ambientBed === undefined
|
|
216
|
+
? {}
|
|
217
|
+
: { ambientBed: ambientBedFormOf(config.ambientBed) }),
|
|
218
|
+
// Level the volume at the project's targets while it is on; off leaves it to Settings'
|
|
219
|
+
// default, which is how a template made before the setting starts a run.
|
|
220
|
+
...(config.loudness === undefined
|
|
221
|
+
? {}
|
|
222
|
+
: {
|
|
223
|
+
loudness: {
|
|
224
|
+
enabled: true,
|
|
225
|
+
videoLufs: config.loudness.videoLufs,
|
|
226
|
+
audioFilesLufs: config.loudness.audioFilesLufs,
|
|
227
|
+
},
|
|
228
|
+
}),
|
|
229
|
+
// The project's pauses; a project without them leaves the template to the defaults.
|
|
230
|
+
...(config.sentencePauseSeconds === undefined
|
|
231
|
+
? {}
|
|
232
|
+
: { sentencePause: String(config.sentencePauseSeconds) }),
|
|
233
|
+
...(config.paragraphPauseSeconds === undefined
|
|
234
|
+
? {}
|
|
235
|
+
: { paragraphPause: String(config.paragraphPauseSeconds) }),
|
|
192
236
|
imagePrompts,
|
|
237
|
+
...(config.imageScale === undefined ? {} : { imageScale: imageScaleForm(config.imageScale) }),
|
|
193
238
|
thumbnailPrompt: config.thumbnailPrompt ?? "",
|
|
194
239
|
intro: config.intro?.name ?? "",
|
|
195
240
|
outro: config.outro?.name ?? "",
|
|
@@ -214,7 +259,8 @@ function documentFromProject(deps, revision) {
|
|
|
214
259
|
},
|
|
215
260
|
section: "content",
|
|
216
261
|
variants: [],
|
|
217
|
-
|
|
262
|
+
// The length a project that scales its images planned for, so the template plans the same.
|
|
263
|
+
expectedWords: String(config.imageScale?.words ?? 1500),
|
|
218
264
|
previewText: "Every story begins with a word.",
|
|
219
265
|
fontUpload: null,
|
|
220
266
|
};
|