@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,26 @@
|
|
|
1
|
+
import { detectSlots } from "../admission/substitute.js";
|
|
2
|
+
// A template keeps the settings, never what was typed for one video. A keyword the project
|
|
3
|
+
// title names ({{Topic}} in "D&D Lore: {{Topic}}") is the video's topic: it changes with every
|
|
4
|
+
// video, so a template stores it empty. Every other keyword ({{minWords}}, {{style}}) is a
|
|
5
|
+
// setting and keeps its value.
|
|
6
|
+
export function topicKeywords(title) {
|
|
7
|
+
return detectSlots(title).names;
|
|
8
|
+
}
|
|
9
|
+
// The values a template stores: the topic keywords emptied, the rest as they were.
|
|
10
|
+
export function templateValues(title, values) {
|
|
11
|
+
const topics = topicKeywords(title);
|
|
12
|
+
// Prototype-free, as `slices/admission/rules.ts` explains: a keyword can be "__proto__".
|
|
13
|
+
const kept = Object.create(null);
|
|
14
|
+
for (const [name, value] of Object.entries(values))
|
|
15
|
+
kept[name] = topics.includes(name) ? "" : value;
|
|
16
|
+
return kept;
|
|
17
|
+
}
|
|
18
|
+
// The Play document a template is saved from, without the one-off values: the topic keywords
|
|
19
|
+
// emptied and the queued extra videos dropped (each is a topic of its own).
|
|
20
|
+
export function templateDocument(document) {
|
|
21
|
+
return {
|
|
22
|
+
...document,
|
|
23
|
+
form: { ...document.form, values: templateValues(document.form.title, document.form.values) },
|
|
24
|
+
variants: [],
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { projectTemplateSchema } from "./schema.js";
|
|
3
|
-
|
|
3
|
+
// A template in the trash (`deleted_at` set, Settings → Trash) is invisible to every read here.
|
|
4
|
+
const select = `SELECT t.id,r.version,r.name,t.created_at AS createdAt,r.created_at AS updatedAt,r.document_json AS document FROM project_templates t JOIN project_template_revisions r ON r.template_id=t.id AND t.deleted_at IS NULL`;
|
|
4
5
|
const templateSummarySchema = z
|
|
5
6
|
.object({
|
|
6
7
|
id: z.uuid(),
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { transact } from "../../kernel/db/tx.js";
|
|
3
|
+
import { resolveChannelId, templateChannels } from "../channels/repo.js";
|
|
3
4
|
import { requestHash } from "../play-drafts/repo.js";
|
|
4
5
|
import { createDraft, readDraft } from "../play-drafts/service.js";
|
|
5
6
|
import { insertTemplateRevision, templateById, templateSummaries } from "./repo.js";
|
|
6
7
|
import { templateCreateSchema, templateDeleteSchema, templateInstantiateSchema, templateUpdateSchema, } from "./schema.js";
|
|
7
8
|
import { freshTemplateDraft, templateSetup } from "./setup.js";
|
|
8
9
|
export function listTemplates(deps) {
|
|
9
|
-
|
|
10
|
+
const channels = templateChannels(deps.db);
|
|
11
|
+
return templateSummaries(deps.db).map((template) => ({
|
|
12
|
+
...template,
|
|
13
|
+
channelId: channels.get(template.id) ?? resolveChannelId(deps.db, undefined),
|
|
14
|
+
}));
|
|
10
15
|
}
|
|
11
16
|
export function readTemplate(deps, id, version) {
|
|
12
17
|
if (!z.uuid().safeParse(id).success ||
|
|
@@ -39,9 +44,11 @@ export function createTemplate(deps, input) {
|
|
|
39
44
|
createdAt: at,
|
|
40
45
|
updatedAt: at,
|
|
41
46
|
};
|
|
47
|
+
// The channel is the template's, not part of its setup: moving it (Channels)
|
|
48
|
+
// makes no new version, and a draft made from it asks the template (`draftChannel`).
|
|
42
49
|
deps.db
|
|
43
|
-
.prepare("INSERT INTO project_templates(id,head_version,creation_hash,created_at) VALUES (?,1
|
|
44
|
-
.run(id, hash, at);
|
|
50
|
+
.prepare("INSERT INTO project_templates(id,head_version,creation_hash,created_at,channel_id) VALUES (?,1,?,?,?)")
|
|
51
|
+
.run(id, hash, at, resolveChannelId(deps.db, document.channelId));
|
|
45
52
|
insertTemplateRevision(deps.db, value);
|
|
46
53
|
return { ok: true, value };
|
|
47
54
|
});
|
|
@@ -96,7 +103,11 @@ export function deleteTemplate(deps, input) {
|
|
|
96
103
|
.prepare("SELECT 1 FROM schedules WHERE template_id=? AND deleted_at IS NULL LIMIT 1")
|
|
97
104
|
.get(parsed.data.id))
|
|
98
105
|
return { ok: false, reason: "referenced-by-schedule" };
|
|
99
|
-
|
|
106
|
+
// Into the trash (Settings → Trash), not gone: `slices/trash` restores it or removes it
|
|
107
|
+
// for good. A schedule that is itself in the trash may still name it.
|
|
108
|
+
deps.db
|
|
109
|
+
.prepare("UPDATE project_templates SET deleted_at=? WHERE id=? AND deleted_at IS NULL")
|
|
110
|
+
.run(deps.clock.now().toISOString(), parsed.data.id);
|
|
100
111
|
return { ok: true, value: { deleted: true } };
|
|
101
112
|
});
|
|
102
113
|
}
|
|
@@ -1,15 +1,26 @@
|
|
|
1
|
+
import { templateChannelId } from "../channels/repo.js";
|
|
1
2
|
import { snapshotEntry, snapshotPrompt } from "../library/snapshot.js";
|
|
3
|
+
import { templateDocument } from "./one-off.js";
|
|
2
4
|
export function templateSetup(deps, input) {
|
|
3
5
|
const prompts = [];
|
|
4
6
|
const entries = [];
|
|
5
7
|
const choices = [
|
|
6
|
-
{
|
|
8
|
+
{
|
|
9
|
+
kind: input.form.voices?.source === "script" && input.form.sources.audio === "generate"
|
|
10
|
+
? "script"
|
|
11
|
+
: "article",
|
|
12
|
+
name: input.form.articlePrompt,
|
|
13
|
+
},
|
|
7
14
|
{ kind: "narration", name: input.form.narrationPrompt ?? "" },
|
|
8
15
|
{ kind: "description", name: input.form.descriptionPrompt ?? "" },
|
|
9
16
|
...input.form.imagePrompts.map((row) => ({ kind: "image", name: row.name })),
|
|
10
17
|
{ kind: "thumbnail", name: input.form.thumbnailPrompt },
|
|
11
18
|
{ kind: "shorts", name: input.form.shorts?.prompt ?? "" },
|
|
12
19
|
{ kind: "image", name: input.form.shorts?.imagePrompt ?? "" },
|
|
20
|
+
...Object.values(input.form.reviews?.stages ?? {}).map((stage) => ({
|
|
21
|
+
kind: "review",
|
|
22
|
+
name: stage.mode === "off" ? "" : stage.prompt,
|
|
23
|
+
})),
|
|
13
24
|
{
|
|
14
25
|
kind: "image",
|
|
15
26
|
name: input.form.reference?.source === "prompt" ? input.form.reference.prompt : "",
|
|
@@ -32,7 +43,8 @@ export function templateSetup(deps, input) {
|
|
|
32
43
|
return { ok: false, reason: "missing-prompt" };
|
|
33
44
|
entries.push(row);
|
|
34
45
|
}
|
|
35
|
-
|
|
46
|
+
// Only the settings are kept: the topic typed for one video is left empty (`one-off.ts`).
|
|
47
|
+
const { templateSource: _source, channelId: _channel, ...document } = templateDocument(input);
|
|
36
48
|
return {
|
|
37
49
|
ok: true,
|
|
38
50
|
value: { ...document, fontUpload: null, librarySnapshot: { prompts, entries } },
|
|
@@ -49,6 +61,7 @@ export function freshTemplateDraft(deps, document, source) {
|
|
|
49
61
|
section: "content",
|
|
50
62
|
fontUpload: null,
|
|
51
63
|
templateSource: source,
|
|
64
|
+
channelId: templateChannelId(deps.db, source.id),
|
|
52
65
|
variants: document.variants.map((variant) => ({ ...variant, id: deps.uuid() })),
|
|
53
66
|
form: {
|
|
54
67
|
...document.form,
|
|
@@ -64,6 +77,9 @@ export function freshTemplateDraft(deps, document, source) {
|
|
|
64
77
|
...(provided.shortsMusic === undefined
|
|
65
78
|
? {}
|
|
66
79
|
: { shortsMusic: provided.shortsMusic === null ? null : fresh(provided.shortsMusic) }),
|
|
80
|
+
...(provided.ambientBed === undefined
|
|
81
|
+
? {}
|
|
82
|
+
: { ambientBed: provided.ambientBed === null ? null : fresh(provided.ambientBed) }),
|
|
67
83
|
},
|
|
68
84
|
},
|
|
69
85
|
};
|
|
@@ -72,7 +72,7 @@ export function admitPreview(deps, input) {
|
|
|
72
72
|
workIds.push(String(existing.id));
|
|
73
73
|
if (existing.state !== "running") {
|
|
74
74
|
deps.db
|
|
75
|
-
.prepare("UPDATE revision_work SET state='pending',dispatch_state='allowed',failure_reason=NULL WHERE id=?")
|
|
75
|
+
.prepare("UPDATE revision_work SET state='pending',dispatch_state='allowed',failure_reason=NULL,failure_kind=NULL,retry_at=NULL,auto_retries=0 WHERE id=?")
|
|
76
76
|
.run(String(existing.id));
|
|
77
77
|
deps.db
|
|
78
78
|
.prepare(`UPDATE revision_work_pieces SET state='pending',dispatch_state='allowed'
|
|
@@ -30,6 +30,9 @@ const costRowSchema = z.object({
|
|
|
30
30
|
low: z.number().finite().nonnegative().nullable(),
|
|
31
31
|
high: z.number().finite().nonnegative().nullable(),
|
|
32
32
|
detail: z.string(),
|
|
33
|
+
onPlan: z.boolean().optional(),
|
|
34
|
+
apiLow: z.number().finite().nonnegative().nullable().optional(),
|
|
35
|
+
apiHigh: z.number().finite().nonnegative().nullable().optional(),
|
|
33
36
|
});
|
|
34
37
|
const costEstimateSchema = z.object({
|
|
35
38
|
currency: z.literal("USD"),
|
|
@@ -37,6 +40,9 @@ const costEstimateSchema = z.object({
|
|
|
37
40
|
low: z.number().finite().nonnegative(),
|
|
38
41
|
high: z.number().finite().nonnegative(),
|
|
39
42
|
unknown: z.number().int().nonnegative(),
|
|
43
|
+
apiLow: z.number().finite().nonnegative().optional(),
|
|
44
|
+
apiHigh: z.number().finite().nonnegative().optional(),
|
|
45
|
+
apiUnknown: z.number().int().nonnegative().optional(),
|
|
40
46
|
expectedWords: z.number().finite().nonnegative(),
|
|
41
47
|
catalogueDate: z.string().nullable(),
|
|
42
48
|
assumptions: z.array(z.string()),
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { stageKinds } from "../../kernel/pipeline.js";
|
|
2
|
+
import { languageInfo } from "../../kernel/ports/languages.js";
|
|
2
3
|
import { sourceOf } from "../admission/model.js";
|
|
3
4
|
import { motionStyleLabels } from "../admission/rules.js";
|
|
4
5
|
import { documentThemeLabel } from "../document/model.js";
|
|
6
|
+
import { loudnessLabel } from "../loudness/model.js";
|
|
7
|
+
import { reviewModeLabels, reviewModeOf, reviewStageLabels, reviewStages, } from "../reviews/model.js";
|
|
5
8
|
import { getRevisionView } from "../revisions/view.js";
|
|
6
9
|
import { musicVolumeOf, shortsSpeedOf } from "../shorts/model.js";
|
|
10
|
+
import { ambientBedLabels } from "../video/ambient-bed.js";
|
|
7
11
|
import { videoEditOf, videoEditRows } from "../video/edit-settings.js";
|
|
8
12
|
export function previewDetails(deps, view, allRecipes, selected) {
|
|
9
13
|
const parent = view.revision.parentId === null
|
|
@@ -48,9 +52,11 @@ export function previewDetails(deps, view, allRecipes, selected) {
|
|
|
48
52
|
key: row.key,
|
|
49
53
|
label: row.key === "reference:image"
|
|
50
54
|
? "Establishing image"
|
|
51
|
-
:
|
|
52
|
-
?
|
|
53
|
-
:
|
|
55
|
+
: row.key.startsWith("thumbnail:image:")
|
|
56
|
+
? `Thumbnail image ${row.key.slice("thumbnail:image:".length)}`
|
|
57
|
+
: index < 0
|
|
58
|
+
? "Thumbnail image"
|
|
59
|
+
: `Image ${index + 1}`,
|
|
54
60
|
text: input.prompt,
|
|
55
61
|
settings: [
|
|
56
62
|
input.provider,
|
|
@@ -95,6 +101,9 @@ export function previewDetails(deps, view, allRecipes, selected) {
|
|
|
95
101
|
});
|
|
96
102
|
return { inputChanges: parent === undefined ? [] : inputChanges(parent, view), requests };
|
|
97
103
|
}
|
|
104
|
+
function loudnessSummary(value) {
|
|
105
|
+
return `Video ${loudnessLabel(value.videoLufs, "video")}; audio files ${loudnessLabel(value.audioFilesLufs, "audioFiles")}`;
|
|
106
|
+
}
|
|
98
107
|
function narrationIdentity(row) {
|
|
99
108
|
if (row.input.kind === "tts")
|
|
100
109
|
return { logicalKey: row.input.logicalKey, segment: row.input.segment };
|
|
@@ -138,9 +147,17 @@ function inputChanges(parent, view) {
|
|
|
138
147
|
add(`${name} model`, a?.model, b?.model);
|
|
139
148
|
add(`${name} thinking`, a?.thinking, b?.thinking);
|
|
140
149
|
}
|
|
150
|
+
add("Language", languageInfo(before.language).name, languageInfo(after.language).name);
|
|
141
151
|
add("Narration voice", before.audio?.voice, after.audio?.voice);
|
|
142
152
|
add("Narration Preparation", before.narrationPrompt, after.narrationPrompt);
|
|
143
153
|
add("Narration chunking", before.chunking, after.chunking);
|
|
154
|
+
add("Narration aliases", before.audio?.useNarrationAliases === true ? aliasLines(before.narrationAliases) : "Off", after.audio?.useNarrationAliases === true ? aliasLines(after.narrationAliases) : "Off");
|
|
155
|
+
const described = (audio) => audio?.describeFigures !== true ? "Off" : audio.skipCode === true ? "On, code left out" : "On";
|
|
156
|
+
add("Describe tables and figures", described(before.audio), described(after.audio));
|
|
157
|
+
add("Show tables and figures on screen", before.showFigures === true ? "On" : "Off", after.showFigures === true ? "On" : "Off");
|
|
158
|
+
add("Pause between sentences (seconds)", before.sentencePauseSeconds ?? 0, after.sentencePauseSeconds ?? 0);
|
|
159
|
+
add("Pause between paragraphs (seconds)", before.paragraphPauseSeconds ?? 0, after.paragraphPauseSeconds ?? 0);
|
|
160
|
+
add("Level the volume", before.loudness === undefined ? "Off" : loudnessSummary(before.loudness), after.loudness === undefined ? "Off" : loudnessSummary(after.loudness));
|
|
144
161
|
add("YouTube description", before.youtubeDescription === true ? "On" : "Off", after.youtubeDescription === true ? "On" : "Off");
|
|
145
162
|
add("Description prompt", before.descriptionPrompt, after.descriptionPrompt);
|
|
146
163
|
add("Shorts", before.shorts?.enabled === true ? "On" : "Off", after.shorts?.enabled === true ? "On" : "Off");
|
|
@@ -150,8 +167,16 @@ function inputChanges(parent, view) {
|
|
|
150
167
|
: `${String(before.shorts.minSeconds)}-${String(before.shorts.maxSeconds)}`, after.shorts === undefined
|
|
151
168
|
? undefined
|
|
152
169
|
: `${String(after.shorts.minSeconds)}-${String(after.shorts.maxSeconds)}`);
|
|
170
|
+
add("Thumbnails", before.thumbnailCount === 3 ? "3" : "1", after.thumbnailCount === 3 ? "3" : "1");
|
|
153
171
|
add("Shorts prompt", before.shorts?.prompt, after.shorts?.prompt);
|
|
154
172
|
add("Shorts image prompt", before.shorts?.imagePrompt, after.shorts?.imagePrompt);
|
|
173
|
+
add("Reviewer provider", before.reviews?.provider, after.reviews?.provider);
|
|
174
|
+
add("Reviewer model", before.reviews?.model, after.reviews?.model);
|
|
175
|
+
add("Reviewer redos", before.reviews?.retries, after.reviews?.retries);
|
|
176
|
+
for (const stage of reviewStages) {
|
|
177
|
+
add(`${reviewStageLabels[stage]} review`, reviewModeLabels[reviewModeOf(before.reviews, stage)], reviewModeLabels[reviewModeOf(after.reviews, stage)]);
|
|
178
|
+
add(`${reviewStageLabels[stage]} review prompt`, before.reviews?.stages[stage]?.prompt, after.reviews?.stages[stage]?.prompt);
|
|
179
|
+
}
|
|
155
180
|
add("Establishing image", referenceLabel(before), referenceLabel(after));
|
|
156
181
|
add("Thumbnail drawn from the establishing image", before.reference === undefined
|
|
157
182
|
? undefined
|
|
@@ -170,6 +195,7 @@ function inputChanges(parent, view) {
|
|
|
170
195
|
add("Short ranges set by hand", old.shortsRanges, next.shortsRanges);
|
|
171
196
|
add("Silence gap (seconds)", before.silenceGapSeconds, after.silenceGapSeconds);
|
|
172
197
|
add("Silence at start and end (seconds)", before.edgeSilenceSeconds, after.edgeSilenceSeconds);
|
|
198
|
+
add("Ambient sound", ambientBedLabel(before, old), ambientBedLabel(after, next));
|
|
173
199
|
add("Seconds per image", before.imageSeconds, after.imageSeconds);
|
|
174
200
|
add("Zoom (%)", before.zoomPercent, after.zoomPercent);
|
|
175
201
|
add("Motion", motionStyleLabels[before.motionStyle], motionStyleLabels[after.motionStyle]);
|
|
@@ -207,9 +233,26 @@ function inputChanges(parent, view) {
|
|
|
207
233
|
add("Manual captions", old.subtitleCues?.cues, next.subtitleCues?.cues);
|
|
208
234
|
return changes;
|
|
209
235
|
}
|
|
236
|
+
// The ambient bed in one line; undefined without one, so a project that never had one lists
|
|
237
|
+
// no change.
|
|
238
|
+
function ambientBedLabel(config, content) {
|
|
239
|
+
const bed = config.ambientBed;
|
|
240
|
+
if (bed === undefined)
|
|
241
|
+
return undefined;
|
|
242
|
+
const file = bed.source === "upload" && content.ambientBed !== undefined ? " (uploaded file)" : "";
|
|
243
|
+
return `${ambientBedLabels[bed.source]}${file}, ${String(bed.levelDb)} dB, fade in ${String(bed.fadeInSeconds)} s, tail ${String(bed.tailSeconds)} s`;
|
|
244
|
+
}
|
|
210
245
|
function referenceLabel(config) {
|
|
211
246
|
const reference = config.reference;
|
|
212
247
|
if (reference === undefined)
|
|
213
248
|
return "Off";
|
|
214
249
|
return reference.source === "provide" ? "Uploaded image" : `Prompt: ${reference.prompt ?? ""}`;
|
|
215
250
|
}
|
|
251
|
+
// One line per alias, as the Library shows it, so a changed alias reads as a changed line.
|
|
252
|
+
function aliasLines(aliases) {
|
|
253
|
+
if (aliases === undefined || aliases.length === 0)
|
|
254
|
+
return "On, no aliases copied";
|
|
255
|
+
return aliases
|
|
256
|
+
.map((alias) => `${alias.written} → ${alias.spoken}${alias.wholeWord ? "" : " (inside words too)"}${alias.caseSensitive ? " (match case)" : ""}`)
|
|
257
|
+
.join("\n");
|
|
258
|
+
}
|
|
@@ -168,13 +168,20 @@ export function planPreview(deps, view, catalogue, selection, id) {
|
|
|
168
168
|
providedReuseRequired: execution.reviews.map((row) => row.key),
|
|
169
169
|
costs: estimateRequests(work.flatMap((row) => priceRecipes(row.inflight || !requiresNewSubmission(deps, view.revision.id, row.key, row.fingerprint)
|
|
170
170
|
? { ...row, disposition: "reuse" }
|
|
171
|
-
: row, recipes.find((recipe) => recipe.key === row.key))),
|
|
171
|
+
: row, recipes.find((recipe) => recipe.key === row.key), view.revision.config)),
|
|
172
|
+
// The whole catalogue, as Play's review prices with: a CLI row's API figure is read
|
|
173
|
+
// from the API models, which the execution snapshot leaves out.
|
|
174
|
+
catalogue),
|
|
172
175
|
wholeRequestNotice: plan.wholeRequestNotice,
|
|
173
|
-
warnings:
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
176
|
+
warnings: [
|
|
177
|
+
...(submissions.some((row) => row.uncertain)
|
|
178
|
+
? [
|
|
179
|
+
"A previous request was submitted without a saved result or resumable job. Retrying may charge you again.",
|
|
180
|
+
]
|
|
181
|
+
: []),
|
|
182
|
+
...(plan.glossaryNotice ? [plan.glossaryNotice] : []),
|
|
183
|
+
...(plan.speakerPronunciationNotice ? [plan.speakerPronunciationNotice] : []),
|
|
184
|
+
],
|
|
178
185
|
};
|
|
179
186
|
return { ok: true, value: { preview, execution } };
|
|
180
187
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { aliasMatches } from "../../kernel/ports/narration-aliases.js";
|
|
2
|
+
import { narrationAliasesOf, usesNarrationPreparation, usesPronunciationGlossary, } from "../admission/rules.js";
|
|
3
|
+
import { aliasSpans, withAliasSpans } from "../narration/aliases.js";
|
|
2
4
|
import { narrationRegenerationToken, normalizeNarrationText, planNarration, } from "../narration/plan.js";
|
|
3
5
|
import { pronunciationSpans } from "../narration/pronunciation.js";
|
|
4
6
|
import { prepareRequests } from "../narration/steering.js";
|
|
@@ -20,18 +22,20 @@ export function narrationParts(context, logicalKey, originalText, segment, depen
|
|
|
20
22
|
if (!glossary.ok)
|
|
21
23
|
return [refusedPart(context, logicalKey, dependsOn, glossary.reason)];
|
|
22
24
|
const logicalText = normalizeNarrationText(override?.kind === "text" ? override.text : originalText);
|
|
23
|
-
|
|
25
|
+
// Aliases first: where one applies, the glossary does not.
|
|
26
|
+
const aliases = narrationAliasesOf(context.config);
|
|
27
|
+
const matches = aliasMatches(logicalText, aliases);
|
|
28
|
+
const spans = withAliasSpans(pronunciationSpans(logicalText, glossary.entries), aliasSpans(logicalText, aliases));
|
|
24
29
|
const wholeRequest = segment !== "body" || (context.config.chunking?.mode ?? "whole") === "whole";
|
|
25
30
|
const choice = context.config.audio;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
!row.deprecated);
|
|
31
|
+
// A retired model's character limit is still its limit: the automatic catalogue check marks
|
|
32
|
+
// models deprecated, and that alone must not re-split narration and outdate a project.
|
|
33
|
+
const model = context.catalogue?.tts.find((row) => row.provider === choice?.provider && row.id === choice.model && row.enabled);
|
|
30
34
|
const logicalLimit = Math.max(2, logicalText.length +
|
|
31
35
|
spans.reduce((sum, span) => sum + Math.max(0, span.text.length - (span.end - span.start)), 0));
|
|
32
36
|
const maxCharacters = model?.tts.maxCharacters ?? logicalLimit;
|
|
33
37
|
if (usesNarrationPreparation(context.config)) {
|
|
34
|
-
const prepared = preparationForGroup(context, logicalKey, logicalText, segment, dependsOn, maxCharacters, spans);
|
|
38
|
+
const prepared = preparationForGroup(context, logicalKey, logicalText, segment, dependsOn, maxCharacters, spans, matches);
|
|
35
39
|
preparations.push(prepared.preparation);
|
|
36
40
|
if (prepared.refusal !== null)
|
|
37
41
|
return [refusedPart(context, logicalKey, [prepared.preparation.key], prepared.refusal)];
|
|
@@ -135,6 +139,35 @@ export function pronunciationFutureValues(context, glossary, article, logicalKey
|
|
|
135
139
|
],
|
|
136
140
|
];
|
|
137
141
|
}
|
|
142
|
+
// What aliases add to a narration recipe that can't be worked out yet. With none copied into
|
|
143
|
+
// the project (every project from before aliases), nothing: its fingerprints stay as they were.
|
|
144
|
+
export function aliasFutureValues(context, logicalKey, source) {
|
|
145
|
+
const aliases = narrationAliasesOf(context.config);
|
|
146
|
+
if (aliases.length === 0)
|
|
147
|
+
return [];
|
|
148
|
+
const override = context.content.narrationOverrides[logicalKey];
|
|
149
|
+
if (override?.kind === "asset")
|
|
150
|
+
return [];
|
|
151
|
+
// Unknown generated text may contain any alias.
|
|
152
|
+
if (source === null)
|
|
153
|
+
return [
|
|
154
|
+
[
|
|
155
|
+
"narration-aliases-v1",
|
|
156
|
+
aliases.map((alias) => [alias.written, alias.spoken, alias.wholeWord, alias.caseSensitive]),
|
|
157
|
+
],
|
|
158
|
+
];
|
|
159
|
+
const clean = normalizeNarrationText(override?.kind === "text" ? override.text : source);
|
|
160
|
+
const matches = aliasMatches(clean, aliases);
|
|
161
|
+
return matches.length === 0
|
|
162
|
+
? []
|
|
163
|
+
: [
|
|
164
|
+
[
|
|
165
|
+
"narration-aliases-v1",
|
|
166
|
+
logicalKey,
|
|
167
|
+
matches.map((match) => [match.start, match.end, match.spoken]),
|
|
168
|
+
],
|
|
169
|
+
];
|
|
170
|
+
}
|
|
138
171
|
export function voiceValues(context) {
|
|
139
172
|
return [
|
|
140
173
|
context.config.audio?.provider ?? null,
|
|
@@ -4,25 +4,37 @@ import { defaultChunking } from "../narration/chunk.js";
|
|
|
4
4
|
import { concatArgs } from "../narration/concat.js";
|
|
5
5
|
import { normalizeNarrationText } from "../narration/plan.js";
|
|
6
6
|
import { pronunciationChunks } from "../narration/pronunciation-chunks.js";
|
|
7
|
-
import { narrationParts, pronunciationFutureValues, voiceValues } from "./recipe-audio-parts.js";
|
|
7
|
+
import { aliasFutureValues, narrationParts, pronunciationFutureValues, voiceValues, } from "./recipe-audio-parts.js";
|
|
8
|
+
import { levelRecipe } from "./recipe-loudness.js";
|
|
8
9
|
import { recipe, resourceIdentity, selectedReference, } from "./recipe-model.js";
|
|
9
10
|
import { narrationFileRecipe } from "./recipe-narration-text.js";
|
|
11
|
+
import { pauseValues } from "./recipe-pauses.js";
|
|
10
12
|
import { preparationFuture, preparationTemplate } from "./recipe-preparation.js";
|
|
13
|
+
import { voiceBodyRecipes } from "./recipe-voices.js";
|
|
11
14
|
export function bodyNarrationGroups(context, text) {
|
|
12
|
-
return text.
|
|
15
|
+
return text.narrationText === null
|
|
13
16
|
? []
|
|
14
|
-
: pronunciationChunks(normalizeNarrationText(text.
|
|
17
|
+
: pronunciationChunks(normalizeNarrationText(text.narrationText), context.config.chunking ?? defaultChunking, usesPronunciationGlossary(context.config) && text.glossary?.ok ? text.glossary.entries : [], new Set(Object.keys(context.content.narrationOverrides)), context.content.narrationSources);
|
|
15
18
|
}
|
|
16
19
|
export function audioRecipes(context, text) {
|
|
17
20
|
const { config, content } = context;
|
|
18
21
|
const recipes = [];
|
|
19
22
|
if (config.sources.audio === "off")
|
|
20
|
-
return { recipes, mediaFingerprint: null, timeline: [], keys: [] };
|
|
23
|
+
return { recipes, mediaFingerprint: null, timeline: [], keys: [], levels: [] };
|
|
21
24
|
const prepare = usesNarrationPreparation(config);
|
|
22
25
|
const pronounce = usesPronunciationGlossary(config);
|
|
23
26
|
const narrationFiles = prepare || pronounce;
|
|
24
27
|
let body;
|
|
25
|
-
let bodyTranscript = text.
|
|
28
|
+
let bodyTranscript = text.narrationText ??
|
|
29
|
+
(text.descriptions.length === 0
|
|
30
|
+
? text.article.fingerprint
|
|
31
|
+
: [
|
|
32
|
+
"described-v1",
|
|
33
|
+
text.article.fingerprint,
|
|
34
|
+
text.descriptions.map((row) => row.fingerprint),
|
|
35
|
+
]);
|
|
36
|
+
let sections;
|
|
37
|
+
let cards = [];
|
|
26
38
|
if (config.sources.audio === "provide") {
|
|
27
39
|
body = recipe(context, "audio:provided", "audio", {
|
|
28
40
|
kind: "provided",
|
|
@@ -32,18 +44,31 @@ export function audioRecipes(context, text) {
|
|
|
32
44
|
}, [text.article.key], { unresolved: content.provided.audio === undefined });
|
|
33
45
|
recipes.push(body);
|
|
34
46
|
}
|
|
47
|
+
else if (text.script !== undefined && config.voices !== undefined) {
|
|
48
|
+
const voiced = voiceBodyRecipes(context, config.voices, text.script, text.glossary);
|
|
49
|
+
recipes.push(...voiced.preparations, ...voiced.cards, ...voiced.parts);
|
|
50
|
+
cards = voiced.cards;
|
|
51
|
+
if (narrationFiles)
|
|
52
|
+
recipes.push(narrationFileRecipe(context, "body", voiced.parts));
|
|
53
|
+
recipes.push(voiced.body);
|
|
54
|
+
body = voiced.body;
|
|
55
|
+
bodyTranscript = voiced.transcript;
|
|
56
|
+
sections = voiced.sections;
|
|
57
|
+
}
|
|
35
58
|
else {
|
|
36
59
|
const groups = bodyNarrationGroups(context, text);
|
|
37
60
|
const parts = [];
|
|
38
61
|
const transcripts = [];
|
|
39
62
|
const futurePronunciation = [];
|
|
40
|
-
|
|
63
|
+
recipes.push(...text.descriptions, ...text.cards);
|
|
64
|
+
cards = text.cards;
|
|
65
|
+
let pending = text.narrationText === null;
|
|
41
66
|
for (const { key: logicalKey, text: logicalText } of groups) {
|
|
42
67
|
transcripts.push({
|
|
43
68
|
original: logicalText,
|
|
44
69
|
effective: effectiveText(context, logicalKey, logicalText),
|
|
45
70
|
});
|
|
46
|
-
futurePronunciation.push(...pronunciationFutureValues(context, text.glossary, text.article, logicalKey, logicalText));
|
|
71
|
+
futurePronunciation.push(...pronunciationFutureValues(context, text.glossary, text.article, logicalKey, logicalText), ...aliasFutureValues(context, logicalKey, logicalText));
|
|
47
72
|
const group = narrationParts(context, logicalKey, logicalText, "body", [text.article.key], recipes, text.glossary);
|
|
48
73
|
if (group.length === 0)
|
|
49
74
|
pending = true;
|
|
@@ -51,10 +76,10 @@ export function audioRecipes(context, text) {
|
|
|
51
76
|
}
|
|
52
77
|
if (transcripts.some((row) => row.effective !== row.original))
|
|
53
78
|
bodyTranscript = ["narration-transcript-v1", transcripts.map((row) => row.effective)];
|
|
54
|
-
if (text.
|
|
55
|
-
futurePronunciation.push(...pronunciationFutureValues(context, text.glossary, text.article, "audio:body:future", null));
|
|
79
|
+
if (text.narrationText === null) {
|
|
80
|
+
futurePronunciation.push(...pronunciationFutureValues(context, text.glossary, text.article, "audio:body:future", null), ...aliasFutureValues(context, "audio:body:future", null));
|
|
56
81
|
if (prepare)
|
|
57
|
-
recipes.push(preparationFuture(context, "body", text.article));
|
|
82
|
+
recipes.push(preparationFuture(context, "body", text.article, text.descriptions));
|
|
58
83
|
}
|
|
59
84
|
if (pending && prepare)
|
|
60
85
|
parts.length = 0;
|
|
@@ -69,8 +94,15 @@ export function audioRecipes(context, text) {
|
|
|
69
94
|
chunkingValues(context),
|
|
70
95
|
...(prepare ? [preparationTemplate(context)] : []),
|
|
71
96
|
...futurePronunciation,
|
|
97
|
+
...(text.descriptions.length === 0
|
|
98
|
+
? []
|
|
99
|
+
: [["described-v1", text.descriptions.map((row) => row.fingerprint)]]),
|
|
72
100
|
],
|
|
73
|
-
}, [
|
|
101
|
+
}, [
|
|
102
|
+
text.article.key,
|
|
103
|
+
...text.descriptions.map((row) => row.key),
|
|
104
|
+
...preparationKeys(recipes, "body"),
|
|
105
|
+
]));
|
|
74
106
|
recipes.push(...parts);
|
|
75
107
|
if (narrationFiles)
|
|
76
108
|
recipes.push(narrationFileRecipe(context, "body", parts));
|
|
@@ -81,14 +113,30 @@ export function audioRecipes(context, text) {
|
|
|
81
113
|
values: [
|
|
82
114
|
parts.map((part) => resourceIdentity(context, part)),
|
|
83
115
|
parts.length === 1 ? "copy-single-file" : concatArgs("$parts", "$output"),
|
|
116
|
+
// Pauses between sentences; a paragraph's between pieces cut at paragraphs.
|
|
117
|
+
...pauseValues(config, parts.map((_part, at) => at === parts.length - 1
|
|
118
|
+
? "end"
|
|
119
|
+
: (config.chunking ?? defaultChunking).mode === "paragraph"
|
|
120
|
+
? "paragraph"
|
|
121
|
+
: "sentence")),
|
|
84
122
|
],
|
|
85
|
-
}, parts.map((part) => part.key), { unresolved: text.
|
|
123
|
+
}, parts.map((part) => part.key), { unresolved: text.narrationText !== null && groups.length === 0 });
|
|
86
124
|
recipes.push(body);
|
|
87
125
|
}
|
|
88
126
|
const ordered = [];
|
|
127
|
+
const levels = [];
|
|
128
|
+
const level = (join, segment) => {
|
|
129
|
+
// An uploaded narration is the user's own file: only the exports' master reaches it.
|
|
130
|
+
const levelled = config.sources.audio === "generate" ? levelRecipe(context, join, segment) : undefined;
|
|
131
|
+
if (levelled === undefined)
|
|
132
|
+
return;
|
|
133
|
+
recipes.push(levelled);
|
|
134
|
+
levels.push(levelled);
|
|
135
|
+
};
|
|
89
136
|
for (const category of ["intro", "body", "outro"]) {
|
|
90
137
|
if (category === "body") {
|
|
91
138
|
ordered.push({ value: body, transcript: bodyTranscript });
|
|
139
|
+
level(body, "body");
|
|
92
140
|
continue;
|
|
93
141
|
}
|
|
94
142
|
const entry = text.entries[category];
|
|
@@ -116,6 +164,7 @@ export function audioRecipes(context, text) {
|
|
|
116
164
|
voiceValues(context),
|
|
117
165
|
...(prepare ? [preparationTemplate(context)] : []),
|
|
118
166
|
...pronunciationFutureValues(context, text.glossary, text.article, logicalKey, entry.text),
|
|
167
|
+
...aliasFutureValues(context, logicalKey, entry.text),
|
|
119
168
|
],
|
|
120
169
|
}, [...dependencies, ...preparationKeys(recipes, category)], invalidGlossary === undefined ? {} : { unresolved: true, refusal: invalidGlossary }));
|
|
121
170
|
recipes.push(...parts);
|
|
@@ -126,9 +175,11 @@ export function audioRecipes(context, text) {
|
|
|
126
175
|
values: [
|
|
127
176
|
parts.map((part) => resourceIdentity(context, part)),
|
|
128
177
|
parts.length === 1 ? "copy-single-file" : concatArgs("$parts", "$output"),
|
|
178
|
+
...pauseValues(config, parts.map((_part, at) => (at === parts.length - 1 ? "end" : "sentence"))),
|
|
129
179
|
],
|
|
130
180
|
}, parts.map((part) => part.key));
|
|
131
181
|
recipes.push(audio);
|
|
182
|
+
level(audio, category);
|
|
132
183
|
if (narrationFiles)
|
|
133
184
|
recipes.push(narrationFileRecipe(context, category, parts));
|
|
134
185
|
ordered.push({
|
|
@@ -158,6 +209,9 @@ export function audioRecipes(context, text) {
|
|
|
158
209
|
]),
|
|
159
210
|
timeline,
|
|
160
211
|
keys: ordered.map(({ value }) => value.key),
|
|
212
|
+
levels,
|
|
213
|
+
...(sections === undefined ? {} : { sections }),
|
|
214
|
+
...(cards.length === 0 ? {} : { cards }),
|
|
161
215
|
};
|
|
162
216
|
}
|
|
163
217
|
function effectiveText(context, key, original) {
|
|
@@ -3,7 +3,10 @@ import { audioRecipes } from "./recipe-audio.js";
|
|
|
3
3
|
import { documentRecipes } from "./recipe-document.js";
|
|
4
4
|
import { editPlan } from "./recipe-edit.js";
|
|
5
5
|
import { exportRecipes } from "./recipe-exports.js";
|
|
6
|
+
import { masterPlan } from "./recipe-loudness.js";
|
|
7
|
+
import { resourceIdentity } from "./recipe-model.js";
|
|
6
8
|
import { imageReference, referenceRecipe } from "./recipe-reference.js";
|
|
9
|
+
import { withReviews } from "./recipe-reviews.js";
|
|
7
10
|
import { shortsRecipes } from "./recipe-shorts.js";
|
|
8
11
|
import { textRecipes } from "./recipe-text.js";
|
|
9
12
|
import { thumbnailRecipes, visualAssets, visualRecipes } from "./recipe-visual.js";
|
|
@@ -13,20 +16,21 @@ export function buildRecipes(context) {
|
|
|
13
16
|
const audio = audioRecipes(context, text);
|
|
14
17
|
const exports = exportRecipes(context, audio);
|
|
15
18
|
const captions = exports.find((value) => value.key === "subtitles:files");
|
|
19
|
+
const timing = exports.find((value) => value.key === "subtitles:timing");
|
|
16
20
|
// The establishing image, when it is on: every other image is drawn from it.
|
|
17
21
|
const reference = referenceRecipe(context);
|
|
18
22
|
const drawnFrom = imageReference(context, reference);
|
|
19
23
|
const thumbnail = thumbnailRecipes(context, text.recipes, referenceForThumbnail(context.config) ? drawnFrom : undefined);
|
|
20
24
|
const youtube = youtubeRecipes(context, exports);
|
|
21
|
-
return [
|
|
25
|
+
return withReviews(context, [
|
|
22
26
|
...text.recipes,
|
|
23
27
|
...audio.recipes,
|
|
24
28
|
...exports,
|
|
25
29
|
...youtube,
|
|
26
30
|
...(reference === undefined ? [] : [reference]),
|
|
27
|
-
...shortsRecipes(context, exports, drawnFrom),
|
|
31
|
+
...shortsRecipes(context, exports, drawnFrom, audio.cards ?? [], masterPlan(context, audio.levels, "video")),
|
|
28
32
|
...thumbnail,
|
|
29
33
|
...documentRecipes(context, text, thumbnail),
|
|
30
|
-
...visualAssets(context, visualRecipes(context.config, context.content, audio.mediaFingerprint, captions?.fingerprint ?? null, (images) => editPlan(context, exports, youtube, images), drawnFrom)),
|
|
31
|
-
];
|
|
34
|
+
...visualAssets(context, visualRecipes(context.config, context.content, audio.mediaFingerprint, captions?.fingerprint ?? null, (images) => editPlan(context, exports, youtube, images, audio.cards ?? []), drawnFrom, timing === undefined ? null : resourceIdentity(context, timing), masterPlan(context, audio.levels, "video"))),
|
|
35
|
+
]);
|
|
32
36
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { castMentions } from "../channels/cast-match.js";
|
|
2
|
+
// The cast members an image request is drawn with: the ones its texts mention, at most
|
|
3
|
+
// `castMembersPerImage` of them in the order they are first mentioned. Undefined when none is,
|
|
4
|
+
// so the request - and its fingerprint - is exactly what it was before the channel had a cast.
|
|
5
|
+
// A member's name and description go with its pictures, since the note that introduces each
|
|
6
|
+
// picture to the provider names them.
|
|
7
|
+
export const castMembersPerImage = 4;
|
|
8
|
+
export function castFor(config, ...texts) {
|
|
9
|
+
const text = texts.filter((value) => typeof value === "string" && value !== "").join("\n");
|
|
10
|
+
const mentioned = castMentions(text, config.cast).slice(0, castMembersPerImage);
|
|
11
|
+
return mentioned.length === 0
|
|
12
|
+
? undefined
|
|
13
|
+
: mentioned.map((member) => ({
|
|
14
|
+
name: member.name,
|
|
15
|
+
description: member.description,
|
|
16
|
+
images: member.images,
|
|
17
|
+
}));
|
|
18
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { fingerprint } from "../../kernel/runner/work.js";
|
|
2
2
|
import { checkpointClosure, checkpointFingerprint } from "../checkpoints/fingerprint.js";
|
|
3
|
+
import { imageCountsOf } from "../images/scale.js";
|
|
3
4
|
import { buildRecipes } from "./recipe-build.js";
|
|
4
5
|
export function reviewCheckpointSet(runs, catalogue, attachments, fontHash) {
|
|
5
6
|
return runs.flatMap((run, runIndex) => {
|
|
@@ -11,15 +12,16 @@ export function reviewCheckpointSet(runs, catalogue, attachments, fontHash) {
|
|
|
11
12
|
id,
|
|
12
13
|
{ source: "provide", assetId: id, prompt: null, templateKey: null },
|
|
13
14
|
])
|
|
14
|
-
:
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
: // The count the run's first revision plans (`revisions/adopt-content.ts`).
|
|
16
|
+
imageCountsOf(config).flatMap((count, index) => Array.from({ length: count }, (_, number) => [
|
|
17
|
+
`${index}:${number}`,
|
|
18
|
+
{
|
|
19
|
+
source: "generate",
|
|
20
|
+
assetId: null,
|
|
21
|
+
prompt: run.rendered[`imagePrompts.${index}`] ?? null,
|
|
22
|
+
templateKey: `imagePrompts.${index}`,
|
|
23
|
+
},
|
|
24
|
+
]));
|
|
23
25
|
const content = {
|
|
24
26
|
articleMarkdown: config.sources.article === "provide" ? config.provided.article : undefined,
|
|
25
27
|
provided: { audio: config.provided.audio, thumbnail: config.provided.thumbnail },
|