@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
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { usesDescribedNarration, usesFigureCards, usesShorts } from "../admission/rules.js";
|
|
2
|
+
import { describedBlocks, narrationBlocks, spokenNarration, } from "../narration/blocks.js";
|
|
3
|
+
import { describeMessages, spokenPassage } from "../narration/describe.js";
|
|
4
|
+
import { recipe } from "./recipe-model.js";
|
|
5
|
+
import { llmInput, matchingText, renderedPrompt } from "./recipe-text.js";
|
|
6
|
+
// "Describe tables and figures in the narration": one text-model step per table, figure,
|
|
7
|
+
// diagram, equation or code block (`narration:describe:<n>`, in reading order), whose answer
|
|
8
|
+
// is said in the block's place. The narration is chunked, prepared and spoken from that
|
|
9
|
+
// spoken text, so the captions and the word timing follow what is said; the article, the
|
|
10
|
+
// PDF and the reading view keep the block itself.
|
|
11
|
+
//
|
|
12
|
+
// Off (or absent) plans nothing here and the narration is the flattened article, so a project
|
|
13
|
+
// saved before the setting keeps every fingerprint.
|
|
14
|
+
export const describeFutureKey = "narration:describe:future";
|
|
15
|
+
export function describing(context) {
|
|
16
|
+
return usesDescribedNarration(context.config);
|
|
17
|
+
}
|
|
18
|
+
// What a description request is made of before its block is known: the instruction family,
|
|
19
|
+
// the model and the style guidance. The future step's template, so a change to any of them
|
|
20
|
+
// reaches the narration still to be written.
|
|
21
|
+
export function describeTemplate(context) {
|
|
22
|
+
const input = llmInput(context, describeMessages({ kind: "table", index: 1, source: "", section: null }, renderedPrompt(context, "narration"), context.config.language));
|
|
23
|
+
return [
|
|
24
|
+
"narration-description-v1",
|
|
25
|
+
JSON.parse(JSON.stringify({ ...input, thinkingConfig: null })),
|
|
26
|
+
context.config.audio?.skipCode === true,
|
|
27
|
+
];
|
|
28
|
+
}
|
|
29
|
+
// The step that stands in for the descriptions while the text they describe is unwritten.
|
|
30
|
+
export function describeFuture(context, dependency) {
|
|
31
|
+
return recipe(context, describeFutureKey, "audio", {
|
|
32
|
+
kind: "deferred",
|
|
33
|
+
version: 1,
|
|
34
|
+
operation: "narration-description",
|
|
35
|
+
template: [dependency.fingerprint, describeTemplate(context)],
|
|
36
|
+
}, [dependency.key]);
|
|
37
|
+
}
|
|
38
|
+
// The Markdown's described blocks as steps, numbered from `offset + 1`, and the spoken text
|
|
39
|
+
// once every one of them has answered.
|
|
40
|
+
export function describedText(context, markdown, dependsOn, offset = 0) {
|
|
41
|
+
const { config } = context;
|
|
42
|
+
const blocks = narrationBlocks(markdown, {
|
|
43
|
+
code: config.audio?.skipCode === true ? "skip" : "describe",
|
|
44
|
+
language: config.language,
|
|
45
|
+
});
|
|
46
|
+
const style = renderedPrompt(context, "narration");
|
|
47
|
+
const passages = new Map();
|
|
48
|
+
const cards = [];
|
|
49
|
+
const recipes = describedBlocks(blocks).map((block) => {
|
|
50
|
+
const index = offset + block.index;
|
|
51
|
+
const value = recipe(context, `narration:describe:${String(index)}`, "audio", {
|
|
52
|
+
...llmInput(context, describeMessages(block, style, config.language)),
|
|
53
|
+
describe: {
|
|
54
|
+
kind: block.kind,
|
|
55
|
+
index,
|
|
56
|
+
...(block.image === undefined ? {} : { image: block.image }),
|
|
57
|
+
},
|
|
58
|
+
}, dependsOn);
|
|
59
|
+
const answer = matchingText(context, value, "text");
|
|
60
|
+
passages.set(block.index, answer === null ? null : spokenPassage(answer));
|
|
61
|
+
if (usesFigureCards(config))
|
|
62
|
+
cards.push(cardRecipe(context, block, index, dependsOn));
|
|
63
|
+
return value;
|
|
64
|
+
});
|
|
65
|
+
return {
|
|
66
|
+
recipes,
|
|
67
|
+
cards,
|
|
68
|
+
spoken: spokenNarration(blocks, (block) => passages.get(block.index) ?? null),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
// The card the video shows while block `index` is described: drawn locally from the block in
|
|
72
|
+
// the brand kit's title font and colour (the 3.0 look otherwise), in the video's format, and
|
|
73
|
+
// upright too when a 16:9 project makes Shorts. Regenerated with its own token.
|
|
74
|
+
function cardRecipe(context, block, index, dependsOn) {
|
|
75
|
+
const { config } = context;
|
|
76
|
+
const formats = config.format === "16:9" && usesShorts(config) ? ["16:9", "9:16"] : [config.format];
|
|
77
|
+
return recipe(context, `figure:card:${String(index)}`, "images", {
|
|
78
|
+
kind: "local",
|
|
79
|
+
version: 1,
|
|
80
|
+
operation: "figure-card-v1",
|
|
81
|
+
values: {
|
|
82
|
+
kind: block.kind,
|
|
83
|
+
index,
|
|
84
|
+
source: block.source,
|
|
85
|
+
section: block.section,
|
|
86
|
+
image: block.image ?? null,
|
|
87
|
+
formats,
|
|
88
|
+
fontId: config.titleStyle?.fontId ?? config.subtitles?.fontId ?? "default",
|
|
89
|
+
color: config.titleStyle?.color ?? null,
|
|
90
|
+
language: config.language ?? "en",
|
|
91
|
+
},
|
|
92
|
+
}, dependsOn);
|
|
93
|
+
}
|
|
@@ -15,7 +15,7 @@ import { recipe, resourceIdentity, selectedReference, } from "./recipe-model.js"
|
|
|
15
15
|
// that step runs) has landed, so until then one deferred `animate:future` stands in for the
|
|
16
16
|
// requests and carries their cost, the way `shorts:future` does.
|
|
17
17
|
export const animateFutureKey = "animate:future";
|
|
18
|
-
export function editPlan(context, exports, youtube, images) {
|
|
18
|
+
export function editPlan(context, exports, youtube, images, cards = []) {
|
|
19
19
|
const { config } = context;
|
|
20
20
|
const edit = videoEditOf(config);
|
|
21
21
|
if (config.sources.video !== "generate")
|
|
@@ -38,6 +38,13 @@ export function editPlan(context, exports, youtube, images) {
|
|
|
38
38
|
values.push(["look-v1", edit.vignette, edit.grain, edit.grade, edit.atmosphere]);
|
|
39
39
|
if (usesChapterCards(config))
|
|
40
40
|
values.push(["cards-v1", config.subtitles?.fontId ?? "default"]);
|
|
41
|
+
// The brand kit's end screen and title style; nothing when unset, so a project without them
|
|
42
|
+
// keeps its export fingerprint.
|
|
43
|
+
const endScreen = config.endScreen?.text.trim() ?? "";
|
|
44
|
+
if (endScreen !== "")
|
|
45
|
+
values.push(["end-screen-v1", endScreen]);
|
|
46
|
+
if (config.titleStyle !== undefined && (usesChapterCards(config) || endScreen !== ""))
|
|
47
|
+
values.push(["title-style", config.titleStyle.fontId ?? null, config.titleStyle.color ?? null]);
|
|
41
48
|
if (timing !== undefined) {
|
|
42
49
|
values.push(["timing", resourceIdentity(context, timing)]);
|
|
43
50
|
dependsOn.push(timing.key);
|
|
@@ -48,6 +55,11 @@ export function editPlan(context, exports, youtube, images) {
|
|
|
48
55
|
else
|
|
49
56
|
values.push(["chapters", "headings", headingsIdentity(context)]);
|
|
50
57
|
}
|
|
58
|
+
// "Show tables and figures on screen": each card, placed where its description is spoken.
|
|
59
|
+
if (cards.length > 0) {
|
|
60
|
+
values.push(["figure-cards-v1", cards.map((value) => resourceIdentity(context, value))]);
|
|
61
|
+
dependsOn.push(...cards.map((value) => value.key));
|
|
62
|
+
}
|
|
51
63
|
const recipes = usesAnimation(config)
|
|
52
64
|
? animateRecipes(context, images, timing, fromDescription ? description : undefined)
|
|
53
65
|
: [];
|
|
@@ -1,12 +1,21 @@
|
|
|
1
|
+
import { projectLanguage } from "../../kernel/ports/languages.js";
|
|
1
2
|
import { usesShorts, usesYoutubeDescription } from "../admission/rules.js";
|
|
3
|
+
import { usesShortMode } from "../admission/short-mode.js";
|
|
4
|
+
import { reviewsNarration } from "../reviews/rules.js";
|
|
5
|
+
import { timingOperation } from "../subtitles/model.js";
|
|
2
6
|
import { audioExportArgs } from "../video/audio-export-args.js";
|
|
3
7
|
import { editNeedsTiming } from "../video/edit-settings.js";
|
|
8
|
+
import { usesVoices } from "../voices/model.js";
|
|
9
|
+
import { portraitValues } from "../voices/portraits.js";
|
|
10
|
+
import { masterPlan } from "./recipe-loudness.js";
|
|
4
11
|
import { recipe, resourceIdentity, } from "./recipe-model.js";
|
|
5
12
|
export function exportRecipes(context, audio) {
|
|
6
13
|
const { config, content } = context;
|
|
7
14
|
if (audio.mediaFingerprint === null)
|
|
8
15
|
return [];
|
|
9
16
|
const recipes = [];
|
|
17
|
+
// Level the volume: the listening files are mastered to the audio files' target.
|
|
18
|
+
const master = masterPlan(context, audio.levels, "audioFiles");
|
|
10
19
|
if (config.sources.video === "off")
|
|
11
20
|
recipes.push(recipe(context, "export:wav", "video", {
|
|
12
21
|
kind: "local",
|
|
@@ -15,30 +24,51 @@ export function exportRecipes(context, audio) {
|
|
|
15
24
|
values: [
|
|
16
25
|
audio.mediaFingerprint,
|
|
17
26
|
audioExportArgs([{ kind: "body", path: "$body", seconds: 0 }], "$output"),
|
|
27
|
+
...master.values,
|
|
18
28
|
],
|
|
19
|
-
}, audio.keys));
|
|
29
|
+
}, [...audio.keys, ...master.keys]));
|
|
20
30
|
const captions = config.subtitles !== undefined && config.subtitles.mode !== "off";
|
|
21
|
-
// The YouTube description's chapters, the shorts' clips and captions,
|
|
22
|
-
// chapter cards and chapter openers use the same word timing,
|
|
23
|
-
// captions off; only the caption files below wait for captions.
|
|
31
|
+
// The YouTube description's chapters, the shorts' clips and captions, a short's own
|
|
32
|
+
// captions, and the video's cuts, chapter cards and chapter openers use the same word timing,
|
|
33
|
+
// so it runs for them even with captions off; only the caption files below wait for captions.
|
|
34
|
+
const voices = usesVoices(config) ? config.voices : undefined;
|
|
24
35
|
if (!captions &&
|
|
25
36
|
!usesYoutubeDescription(config) &&
|
|
26
37
|
!usesShorts(config) &&
|
|
27
|
-
!
|
|
38
|
+
!usesShortMode(config) &&
|
|
39
|
+
!editNeedsTiming(config) &&
|
|
40
|
+
!reviewsNarration(config) &&
|
|
41
|
+
voices?.audioFiles !== true)
|
|
28
42
|
return recipes;
|
|
29
43
|
const timing = recipe(context, "subtitles:timing", "video", {
|
|
30
44
|
kind: "local",
|
|
31
45
|
version: 1,
|
|
32
|
-
operation:
|
|
46
|
+
operation: timingOperation(projectLanguage(config)),
|
|
33
47
|
// The lead-in moves every word, so the edge silence is part of the timing.
|
|
34
48
|
values: [
|
|
35
49
|
audio.timeline,
|
|
36
50
|
config.silenceGapSeconds,
|
|
37
|
-
|
|
51
|
+
// The project language; an English project reads "en" here as it always did.
|
|
52
|
+
config.language ?? config.subtitles?.language ?? "en",
|
|
38
53
|
config.edgeSilenceSeconds,
|
|
54
|
+
// Each word learns its speaker and turn on a multi-voice run.
|
|
55
|
+
...(voices === undefined ? [] : ["voice-words-v1"]),
|
|
39
56
|
],
|
|
40
57
|
}, audio.keys);
|
|
41
58
|
recipes.push(timing);
|
|
59
|
+
if (voices?.audioFiles === true)
|
|
60
|
+
recipes.push(recipe(context, "voices:files", "video", {
|
|
61
|
+
kind: "local",
|
|
62
|
+
version: 1,
|
|
63
|
+
operation: "audio-files-v1",
|
|
64
|
+
values: [
|
|
65
|
+
audio.mediaFingerprint,
|
|
66
|
+
resourceIdentity(context, timing),
|
|
67
|
+
(audio.sections ?? []).map((section) => [section.title, section.firstTurn]),
|
|
68
|
+
config.title,
|
|
69
|
+
...master.values,
|
|
70
|
+
],
|
|
71
|
+
}, [...audio.keys, timing.key, ...master.keys], { unresolved: audio.sections === undefined }));
|
|
42
72
|
if (config.subtitles === undefined || config.subtitles.mode === "off")
|
|
43
73
|
return recipes;
|
|
44
74
|
const cues = content.subtitleCues;
|
|
@@ -53,7 +83,12 @@ export function exportRecipes(context, audio) {
|
|
|
53
83
|
kind: "local",
|
|
54
84
|
version: 1,
|
|
55
85
|
operation: "manual-cues-v1",
|
|
56
|
-
|
|
86
|
+
// A cue's speaker only when it has one, so captions edited before speakers were
|
|
87
|
+
// kept (and every one-voice run's) keep their fingerprint.
|
|
88
|
+
values: [
|
|
89
|
+
cues.audioFingerprint,
|
|
90
|
+
cues.cues.map(({ speaker, ...cue }) => speaker === undefined ? { ...cue } : { ...cue, speaker }),
|
|
91
|
+
],
|
|
57
92
|
}, audio.keys);
|
|
58
93
|
recipes.push(cueRecipe);
|
|
59
94
|
recipes.push(recipe(context, "subtitles:files", "video", {
|
|
@@ -66,6 +101,11 @@ export function exportRecipes(context, audio) {
|
|
|
66
101
|
config.subtitles.fontId,
|
|
67
102
|
config.subtitles.fontSize,
|
|
68
103
|
config.subtitles.position,
|
|
104
|
+
// Only when the brand kit set them, so captions made before keep their fingerprint.
|
|
105
|
+
...(config.subtitles.color === undefined && config.subtitles.outlineColor === undefined
|
|
106
|
+
? []
|
|
107
|
+
: [["colours", config.subtitles.color ?? null, config.subtitles.outlineColor ?? null]]),
|
|
108
|
+
...(voices === undefined ? [] : [captionSpeakerValues(voices, audio.mediaFingerprint)]),
|
|
69
109
|
],
|
|
70
110
|
}, [cueRecipe.key]));
|
|
71
111
|
return recipes;
|
|
@@ -80,3 +120,17 @@ export function manualCuesNeedReview(context, recipes) {
|
|
|
80
120
|
cues.audioFingerprint !==
|
|
81
121
|
context.manifest.outputs.find((value) => value.workKey === timing.key && value.state === "ready")?.fingerprint);
|
|
82
122
|
}
|
|
123
|
+
// How the captions show the speakers: names, colours by place, name tags and, for a podcast or
|
|
124
|
+
// interview, the speaker panel, which runs to the end of the narration. The panel's portraits
|
|
125
|
+
// only when a speaker has one; the render reads the caption file's fingerprint, so it follows.
|
|
126
|
+
function captionSpeakerValues(voices, media) {
|
|
127
|
+
const portraits = portraitValues(voices);
|
|
128
|
+
return [
|
|
129
|
+
"voice-captions-v1",
|
|
130
|
+
voices.format,
|
|
131
|
+
voices.nameTags,
|
|
132
|
+
voices.speakers.map((speaker) => [speaker.id, speaker.name.trim()]),
|
|
133
|
+
media,
|
|
134
|
+
...(portraits === undefined ? [] : [["portraits", [...portraits]]]),
|
|
135
|
+
];
|
|
136
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { messageRoles, thinkingModes } from "../../kernel/ports/llm.js";
|
|
3
3
|
import { llmDocumentsSchema } from "../../kernel/ports/llm-documents.js";
|
|
4
|
+
import { describedKinds } from "../narration/blocks.js";
|
|
4
5
|
import { deferredOperations, localOperations } from "./recipe-model.js";
|
|
5
6
|
const value = z.lazy(() => z.union([
|
|
6
7
|
z.null(),
|
|
@@ -42,6 +43,22 @@ export const recipeInputSchema = z.discriminatedUnion("kind", [
|
|
|
42
43
|
})
|
|
43
44
|
.strict()
|
|
44
45
|
.optional(),
|
|
46
|
+
// A multi-voice run's script or speaker split: the answer is checked as a script.
|
|
47
|
+
script: z
|
|
48
|
+
.object({
|
|
49
|
+
speakers: z.array(z.object({ id: z.string(), name: z.string() }).strict()),
|
|
50
|
+
attribute: z.boolean(),
|
|
51
|
+
})
|
|
52
|
+
.strict()
|
|
53
|
+
.optional(),
|
|
54
|
+
describe: z
|
|
55
|
+
.object({
|
|
56
|
+
kind: z.enum(describedKinds),
|
|
57
|
+
index: z.number().int().positive(),
|
|
58
|
+
image: z.string().optional(),
|
|
59
|
+
})
|
|
60
|
+
.strict()
|
|
61
|
+
.optional(),
|
|
45
62
|
})
|
|
46
63
|
.strict(),
|
|
47
64
|
z
|
|
@@ -58,6 +75,19 @@ export const recipeInputSchema = z.discriminatedUnion("kind", [
|
|
|
58
75
|
segment: z.enum(["body", "intro", "outro"]),
|
|
59
76
|
pronunciation: z.null(),
|
|
60
77
|
wholeRequest: z.boolean().optional(),
|
|
78
|
+
// A multi-voice run's turn requests; without them every such piece failed to store.
|
|
79
|
+
speaker: z.string().optional(),
|
|
80
|
+
turn: z.number().int().optional(),
|
|
81
|
+
dialogue: z
|
|
82
|
+
.array(z
|
|
83
|
+
.object({
|
|
84
|
+
speaker: z.string(),
|
|
85
|
+
turn: z.number().int(),
|
|
86
|
+
voice: z.string(),
|
|
87
|
+
text: z.string(),
|
|
88
|
+
})
|
|
89
|
+
.strict())
|
|
90
|
+
.optional(),
|
|
61
91
|
})
|
|
62
92
|
.strict(),
|
|
63
93
|
z
|
|
@@ -74,6 +104,11 @@ export const recipeInputSchema = z.discriminatedUnion("kind", [
|
|
|
74
104
|
.object({ fingerprint: z.string(), assetId: z.string().nullable() })
|
|
75
105
|
.strict()
|
|
76
106
|
.optional(),
|
|
107
|
+
cast: z
|
|
108
|
+
.array(z
|
|
109
|
+
.object({ name: z.string(), description: z.string(), images: z.array(z.string()) })
|
|
110
|
+
.strict())
|
|
111
|
+
.optional(),
|
|
77
112
|
})
|
|
78
113
|
.strict(),
|
|
79
114
|
z
|
|
@@ -35,6 +35,13 @@ export function legacyOutputWorkKey(output, config) {
|
|
|
35
35
|
return `image:${output.id}`;
|
|
36
36
|
case "thumbnail":
|
|
37
37
|
return "thumbnail:image";
|
|
38
|
+
case "script_md":
|
|
39
|
+
return "script:attribute";
|
|
40
|
+
case "audio_mp3":
|
|
41
|
+
case "audio_m4b":
|
|
42
|
+
return "voices:files";
|
|
43
|
+
case "audio_levelled":
|
|
44
|
+
return `level:${output.meta.segment ?? "body"}`;
|
|
38
45
|
case "reference":
|
|
39
46
|
return referenceKey;
|
|
40
47
|
case "notes":
|
|
@@ -71,6 +78,8 @@ export function legacyOutputWorkKey(output, config) {
|
|
|
71
78
|
// Only ever made by a revision, never by the legacy stages.
|
|
72
79
|
case "animated_image":
|
|
73
80
|
return `animate:${String(output.meta.index ?? 1)}`;
|
|
81
|
+
case "figure_card":
|
|
82
|
+
return `figure:card:${String(output.meta.index ?? 1)}`;
|
|
74
83
|
case "subtitles_srt":
|
|
75
84
|
case "subtitles_vtt":
|
|
76
85
|
case "subtitle_ass":
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { pieceLufs, pieceTruePeak, truePeakOf, usesLoudness, } from "../loudness/model.js";
|
|
2
|
+
import { recipe, resourceIdentity, } from "./recipe-model.js";
|
|
3
|
+
// Level the volume (`loudness/model.ts`): beside each narration join, a step that joins the
|
|
4
|
+
// same pieces again, each brought to one loudness first. The plain join stays what it was, and
|
|
5
|
+
// the word timing keeps reading it, so turning the setting on never makes the timing, or the
|
|
6
|
+
// description, shorts and reviews written from it, again: only these steps and the exports that
|
|
7
|
+
// play their files. The keys are outside `audio:` on purpose, where the narration's own pieces
|
|
8
|
+
// and joins are recognised by their keys.
|
|
9
|
+
export const levelOperation = "level-narration-v1";
|
|
10
|
+
export function levelKey(segment) {
|
|
11
|
+
return `level:${segment}`;
|
|
12
|
+
}
|
|
13
|
+
export function levelRecipe(context, join, segment) {
|
|
14
|
+
if (!usesLoudness(context.config) || join.input.kind !== "local")
|
|
15
|
+
return undefined;
|
|
16
|
+
return recipe(context, levelKey(segment), "audio", {
|
|
17
|
+
kind: "local",
|
|
18
|
+
version: 1,
|
|
19
|
+
operation: levelOperation,
|
|
20
|
+
// The join it repeats, as that join's own recipe has it: the pieces and their layout.
|
|
21
|
+
values: [
|
|
22
|
+
join.input.operation,
|
|
23
|
+
join.input.values,
|
|
24
|
+
["piece-level-v1", pieceLufs, pieceTruePeak],
|
|
25
|
+
],
|
|
26
|
+
}, join.dependsOn, { unresolved: join.unresolved });
|
|
27
|
+
}
|
|
28
|
+
export const noMaster = { values: [], keys: [] };
|
|
29
|
+
export function masterPlan(context, levels, target) {
|
|
30
|
+
const settings = context.config.loudness;
|
|
31
|
+
if (!usesLoudness(context.config) || settings === undefined)
|
|
32
|
+
return noMaster;
|
|
33
|
+
const lufs = target === "video" ? settings.videoLufs : settings.audioFilesLufs;
|
|
34
|
+
return {
|
|
35
|
+
values: [
|
|
36
|
+
[
|
|
37
|
+
"loudness-v1",
|
|
38
|
+
levels.map((level) => resourceIdentity(context, level)),
|
|
39
|
+
lufs,
|
|
40
|
+
truePeakOf(target),
|
|
41
|
+
],
|
|
42
|
+
],
|
|
43
|
+
keys: levels.map((level) => level.key),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -4,6 +4,8 @@ import { narrationRegenerationToken, narrationRequestFingerprint } from "../narr
|
|
|
4
4
|
export const localOperations = [
|
|
5
5
|
"export-wav",
|
|
6
6
|
"wav2vec2-en-a19f851-v2-omissions",
|
|
7
|
+
"wav2vec2-xlsr56-2d48b01-v1",
|
|
8
|
+
"sentence-timing-v1",
|
|
7
9
|
"automatic-cues-v1",
|
|
8
10
|
"manual-cues-v1",
|
|
9
11
|
"subtitle-files-v1",
|
|
@@ -19,9 +21,16 @@ export const localOperations = [
|
|
|
19
21
|
"youtube-description-v1",
|
|
20
22
|
"shorts-pick-v1",
|
|
21
23
|
"short-render-v1",
|
|
24
|
+
"review-v1",
|
|
25
|
+
"concat-turns-v1",
|
|
26
|
+
"voice-captions-v1",
|
|
27
|
+
"audio-files-v1",
|
|
28
|
+
"level-narration-v1",
|
|
29
|
+
"figure-card-v1",
|
|
22
30
|
];
|
|
23
31
|
export const deferredOperations = [
|
|
24
32
|
"narration-preparation",
|
|
33
|
+
"narration-description",
|
|
25
34
|
"research-synthesis",
|
|
26
35
|
"article",
|
|
27
36
|
"entry:intro:text",
|
|
@@ -34,14 +43,24 @@ export const deferredOperations = [
|
|
|
34
43
|
"resolve-revision-recipe",
|
|
35
44
|
"shorts",
|
|
36
45
|
"animate",
|
|
46
|
+
"script-attribution",
|
|
37
47
|
];
|
|
38
48
|
export function recipe(context, key, stage, input, dependsOn = [], options = {}) {
|
|
39
|
-
const requestFingerprint = input.kind === "tts"
|
|
40
|
-
?
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
49
|
+
const requestFingerprint = input.kind === "tts" && input.dialogue !== undefined
|
|
50
|
+
? // One request, several voices: every line's voice and words are what was sent.
|
|
51
|
+
fingerprint([
|
|
52
|
+
"narration-dialogue-v1",
|
|
53
|
+
input.provider,
|
|
54
|
+
input.model,
|
|
55
|
+
input.dialogue.map((line) => [line.voice, line.text]),
|
|
56
|
+
input.segment,
|
|
57
|
+
])
|
|
58
|
+
: input.kind === "tts"
|
|
59
|
+
? narrationRequestFingerprint({
|
|
60
|
+
...input,
|
|
61
|
+
wholeText: input.wholeRequest === true ? input.logicalText : null,
|
|
62
|
+
})
|
|
63
|
+
: fingerprint(JSON.parse(JSON.stringify(input, (_key, value) => typeof value === "number" ? z.number().finite().parse(value) : value)));
|
|
45
64
|
const token = input.kind === "tts"
|
|
46
65
|
? narrationRegenerationToken(context.content.regenerationTokens, input.logicalKey, input.segment)
|
|
47
66
|
: options.token !== undefined
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { pausesOf } from "../narration/pauses-model.js";
|
|
2
|
+
export function pauseValues(config, next) {
|
|
3
|
+
const pauses = pausesOf(config);
|
|
4
|
+
if (pauses === undefined)
|
|
5
|
+
return [];
|
|
6
|
+
return [["pauses-v1", pauses.sentenceSeconds, pauses.paragraphSeconds, [...next]]];
|
|
7
|
+
}
|
|
@@ -4,7 +4,7 @@ import { prepareRequests } from "../narration/steering.js";
|
|
|
4
4
|
import { recipe, selectedReference, } from "./recipe-model.js";
|
|
5
5
|
import { llmInput, renderedPrompt } from "./recipe-text.js";
|
|
6
6
|
export function preparationTemplate(context) {
|
|
7
|
-
const input = llmInput(context, preparationMessages(renderedPrompt(context, "narration"), ""));
|
|
7
|
+
const input = llmInput(context, preparationMessages(renderedPrompt(context, "narration"), "", [], context.config.language));
|
|
8
8
|
return [
|
|
9
9
|
"inworld-tts-2",
|
|
10
10
|
1,
|
|
@@ -25,9 +25,9 @@ export function preparationFuture(context, segment, dependency, additionalDepend
|
|
|
25
25
|
],
|
|
26
26
|
}, [...new Set([dependency.key, ...additionalDependencies.map((row) => row.key)])]);
|
|
27
27
|
}
|
|
28
|
-
export function preparationForGroup(context, logicalKey, source, segment, dependsOn, maxCharacters, spans = []) {
|
|
28
|
+
export function preparationForGroup(context, logicalKey, source, segment, dependsOn, maxCharacters, spans = [], aliases = [], speaker) {
|
|
29
29
|
const preparation = recipe(context, `narration:prepare:${segment}:${logicalKey}`, "audio", {
|
|
30
|
-
...llmInput(context, preparationMessages(renderedPrompt(context, "narration"), source)),
|
|
30
|
+
...llmInput(context, preparationMessages(renderedPrompt(context, "narration"), source, aliases, context.config.language, speaker)),
|
|
31
31
|
preparation: { format: "inworld-tts-2", version: 1, source, logicalKey, segment },
|
|
32
32
|
}, dependsOn, { unresolved: source.trim() === "" });
|
|
33
33
|
if (source.trim() === "")
|
|
@@ -5,6 +5,16 @@ export function recipeProviderChoice(recipe, config) {
|
|
|
5
5
|
// The YouTube description's request is built when it runs, from the project's LLM row.
|
|
6
6
|
if (input.kind === "local" && input.operation === "youtube-description-v1")
|
|
7
7
|
return config.llm === undefined ? undefined : { ...config.llm, family: "llm" };
|
|
8
|
+
// A review's request is built when it runs, from the reviewer the project chose.
|
|
9
|
+
if (input.kind === "local" && input.operation === "review-v1")
|
|
10
|
+
return config.reviews === undefined
|
|
11
|
+
? undefined
|
|
12
|
+
: {
|
|
13
|
+
provider: config.reviews.provider,
|
|
14
|
+
model: config.reviews.model,
|
|
15
|
+
...(config.reviews.thinking === undefined ? {} : { thinking: config.reviews.thinking }),
|
|
16
|
+
family: "llm",
|
|
17
|
+
};
|
|
8
18
|
// So is the shorts' pick; what stands in for the rest of the step until it lands is
|
|
9
19
|
// mostly images, which are its cost.
|
|
10
20
|
if (input.kind === "local" && input.operation === "shorts-pick-v1")
|
|
@@ -22,7 +32,7 @@ export function recipeProviderChoice(recipe, config) {
|
|
|
22
32
|
};
|
|
23
33
|
if (input.kind !== "deferred")
|
|
24
34
|
return undefined;
|
|
25
|
-
const family = input.operation === "narration-preparation"
|
|
35
|
+
const family = input.operation === "narration-preparation" || input.operation === "narration-description"
|
|
26
36
|
? "llm"
|
|
27
37
|
: recipe.stage === "audio"
|
|
28
38
|
? "tts"
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { referenceKey } from "../admission/model.js";
|
|
2
2
|
import { usesReference } from "../admission/rules.js";
|
|
3
|
+
import { castFor } from "./recipe-cast.js";
|
|
3
4
|
import { recipe, selectedAsset, } from "./recipe-model.js";
|
|
4
5
|
import { renderedPrompt } from "./recipe-text.js";
|
|
5
6
|
// The establishing image (`RunDraft.reference`): one step of the Images stage, made before
|
|
@@ -27,7 +28,16 @@ export function referenceRecipe(context) {
|
|
|
27
28
|
semantic: "reference",
|
|
28
29
|
}, [], { unresolved: content.provided.reference === undefined });
|
|
29
30
|
const prompt = renderedPrompt(context, referencePromptKey);
|
|
30
|
-
return recipe(context, referenceKey, "images", {
|
|
31
|
+
return recipe(context, referenceKey, "images", {
|
|
32
|
+
kind: "image",
|
|
33
|
+
version: 1,
|
|
34
|
+
...imageChoice(config),
|
|
35
|
+
aspect: config.format,
|
|
36
|
+
prompt,
|
|
37
|
+
// The establishing image sets the look for the whole video, so it is drawn with every
|
|
38
|
+
// member the title mentions as well as the ones its own brief does.
|
|
39
|
+
...castField(castFor(config, config.title, prompt)),
|
|
40
|
+
}, [], { unresolved: prompt.trim() === "" });
|
|
31
41
|
}
|
|
32
42
|
// Which image the others are drawn from: the step's fingerprint, so making it again or
|
|
33
43
|
// changing it marks every image drawn from it outdated, and the asset it made.
|
|
@@ -42,3 +52,7 @@ export function imageReference(context, reference) {
|
|
|
42
52
|
},
|
|
43
53
|
};
|
|
44
54
|
}
|
|
55
|
+
// The request's `cast`, only when some member is mentioned.
|
|
56
|
+
export function castField(cast) {
|
|
57
|
+
return cast === undefined ? {} : { cast };
|
|
58
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { narrationItemKey, reviewKey, reviewModeOf, reviewPromptKey, reviewRetriesOf, } from "../reviews/model.js";
|
|
2
|
+
import { activeReviewStages } from "../reviews/rules.js";
|
|
3
|
+
import { recipe, resourceIdentity, } from "./recipe-model.js";
|
|
4
|
+
import { renderedPrompt } from "./recipe-text.js";
|
|
5
|
+
// A review is a step of its own, `review:<item>`, planned like any other: it waits for the
|
|
6
|
+
// item, its fingerprint follows the item's output, the reviewer and the prompt, and it is
|
|
7
|
+
// kept and reused while those stay the same. What depends on a reviewed item waits for its
|
|
8
|
+
// review too, so the run moves on only once the reviewer has answered - the automatic twin
|
|
9
|
+
// of a review checkpoint. With every review Off nothing here is added and no step changes.
|
|
10
|
+
export function withReviews(context, recipes) {
|
|
11
|
+
const settings = context.config.reviews;
|
|
12
|
+
const stages = activeReviewStages(context.config);
|
|
13
|
+
if (settings === undefined || stages.length === 0)
|
|
14
|
+
return recipes;
|
|
15
|
+
const byKey = new Map(recipes.map((row) => [row.key, row]));
|
|
16
|
+
const items = stages.flatMap((stage) => reviewedItems(stage, recipes, byKey));
|
|
17
|
+
const reviews = items.map((item) => recipe(context, reviewKey(item.itemKey), stageOf(item.stage), {
|
|
18
|
+
kind: "local",
|
|
19
|
+
version: 1,
|
|
20
|
+
operation: "review-v1",
|
|
21
|
+
values: [
|
|
22
|
+
item.stage,
|
|
23
|
+
item.itemKey,
|
|
24
|
+
item.inputs.map((input) => [input.key, resourceIdentity(context, input)]),
|
|
25
|
+
settings.provider,
|
|
26
|
+
settings.model,
|
|
27
|
+
settings.thinking ?? null,
|
|
28
|
+
// Blank means the stage's built-in prompt; the step fills it in, so a new built-in
|
|
29
|
+
// wording ships as a new operation version rather than a silent change here.
|
|
30
|
+
settings.stages[item.stage]?.prompt?.trim()
|
|
31
|
+
? renderedPrompt(context, reviewPromptKey(item.stage))
|
|
32
|
+
: "",
|
|
33
|
+
reviewModeOf(settings, item.stage),
|
|
34
|
+
reviewRetriesOf(settings),
|
|
35
|
+
],
|
|
36
|
+
}, item.inputs.map((input) => input.key),
|
|
37
|
+
// A paid LLM call: readiness, the charge warning and the estimate treat it like one.
|
|
38
|
+
{ kind: "provider" }));
|
|
39
|
+
const gated = recipes.map((row) => {
|
|
40
|
+
const waits = items.flatMap((item, index) => {
|
|
41
|
+
const review = reviews[index];
|
|
42
|
+
if (review === undefined || !row.dependsOn.some((key) => item.gates.includes(key)))
|
|
43
|
+
return [];
|
|
44
|
+
// A step the review itself reads (the word timing the narration review hears) must not
|
|
45
|
+
// wait for it.
|
|
46
|
+
return upstream(review, byKey).has(row.key) ? [] : [review.key];
|
|
47
|
+
});
|
|
48
|
+
return waits.length === 0 ? row : { ...row, dependsOn: [...row.dependsOn, ...waits] };
|
|
49
|
+
});
|
|
50
|
+
return [...gated, ...reviews];
|
|
51
|
+
}
|
|
52
|
+
function reviewedItems(stage, recipes, byKey) {
|
|
53
|
+
const one = (itemKey, inputs, gates) => ({ stage, itemKey, inputs, gates });
|
|
54
|
+
switch (stage) {
|
|
55
|
+
case "article": {
|
|
56
|
+
const article = byKey.get("article:body");
|
|
57
|
+
return article === undefined || article.kind !== "provider"
|
|
58
|
+
? []
|
|
59
|
+
: [one(article.key, [article], [article.key])];
|
|
60
|
+
}
|
|
61
|
+
case "images": {
|
|
62
|
+
const reference = byKey.get("reference:image");
|
|
63
|
+
return recipes
|
|
64
|
+
.filter((row) => row.key.startsWith("image:") && row.input.kind === "image")
|
|
65
|
+
.map((row) => one(row.key, reference === undefined ? [row] : [row, reference], [row.key]));
|
|
66
|
+
}
|
|
67
|
+
// One thumbnail, or each of three when the project makes three to compare.
|
|
68
|
+
case "thumbnail":
|
|
69
|
+
return recipes
|
|
70
|
+
.filter((row) => /^thumbnail:image(?::\d+)?$/.test(row.key) && row.kind === "provider")
|
|
71
|
+
.map((row) => one(row.key, [row], [row.key]));
|
|
72
|
+
case "narration": {
|
|
73
|
+
const timing = byKey.get("subtitles:timing");
|
|
74
|
+
if (timing === undefined)
|
|
75
|
+
return [];
|
|
76
|
+
const article = byKey.get("article:body");
|
|
77
|
+
return [
|
|
78
|
+
one(narrationItemKey, article === undefined ? [timing] : [timing, article], [
|
|
79
|
+
timing.key,
|
|
80
|
+
...timing.dependsOn,
|
|
81
|
+
]),
|
|
82
|
+
];
|
|
83
|
+
}
|
|
84
|
+
case "shorts":
|
|
85
|
+
return recipes.flatMap((row) => {
|
|
86
|
+
const match = /^(shorts:\d+):render$/.exec(row.key);
|
|
87
|
+
return match?.[1] === undefined ? [] : [one(match[1], [row], [row.key])];
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// The Images stage runs the image reviews and the Video stage the shorts'; the others run in
|
|
92
|
+
// the stage that made the item.
|
|
93
|
+
function stageOf(stage) {
|
|
94
|
+
switch (stage) {
|
|
95
|
+
case "article":
|
|
96
|
+
return "article";
|
|
97
|
+
case "images":
|
|
98
|
+
return "images";
|
|
99
|
+
case "narration":
|
|
100
|
+
return "audio";
|
|
101
|
+
case "thumbnail":
|
|
102
|
+
return "thumbnail";
|
|
103
|
+
case "shorts":
|
|
104
|
+
return "video";
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function upstream(review, byKey) {
|
|
108
|
+
const seen = new Set();
|
|
109
|
+
const queue = [...review.dependsOn];
|
|
110
|
+
for (let key = queue.pop(); key !== undefined; key = queue.pop()) {
|
|
111
|
+
if (seen.has(key))
|
|
112
|
+
continue;
|
|
113
|
+
seen.add(key);
|
|
114
|
+
queue.push(...(byKey.get(key)?.dependsOn ?? []));
|
|
115
|
+
}
|
|
116
|
+
return seen;
|
|
117
|
+
}
|