@gentbajko/slopify 2.4.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 +11 -1
- 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 +55 -5
- package/dist/adapters/host-cli/transport.js +13 -6
- package/dist/adapters/image/bytes.js +2 -2
- package/dist/adapters/image/codex-output.js +60 -18
- package/dist/adapters/image/codex.js +166 -25
- package/dist/adapters/image/fal.js +31 -3
- package/dist/adapters/image/google.js +17 -2
- package/dist/adapters/image/openai.js +45 -16
- package/dist/adapters/image/reference.js +41 -0
- package/dist/adapters/image/replicate.js +14 -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-models.js +2 -1
- package/dist/adapters/llm/codex-server-models.js +2 -1
- 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 +26 -9
- 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/models.yaml +7 -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 +33 -12
- package/dist/catalog/runtime-models.js +1 -1
- package/dist/catalog/schema.js +12 -1
- package/dist/catalog/store.js +147 -8
- package/dist/catalog/validate.js +17 -2
- 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/events/hub.js +11 -0
- package/dist/edge/host-cli.js +1 -1
- package/dist/edge/http/actions.js +32 -10
- package/dist/edge/http/app.js +60 -2
- package/dist/edge/http/auditions.js +70 -0
- package/dist/edge/http/autostart.js +37 -0
- package/dist/edge/http/backups.js +40 -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 +45 -6
- 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/problem.js +1 -0
- 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 +128 -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 +12 -3
- package/dist/kernel/db/migrations/0022-drop-duplicate-held-work.sql +56 -0
- package/dist/kernel/db/migrations/0024-reference-attachments.sql +18 -0
- 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 +139 -16
- package/dist/kernel/ports/image.js +3 -1
- package/dist/kernel/ports/key-probe.js +1 -0
- package/dist/kernel/ports/languages.js +157 -0
- package/dist/kernel/ports/llm.js +3 -1
- 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 +18 -9
- 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 +164 -9
- 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 +321 -20
- 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 +35 -0
- package/dist/slices/admission/repo.js +24 -4
- package/dist/slices/admission/rules.js +126 -1
- package/dist/slices/admission/schema.js +68 -1
- package/dist/slices/admission/short-mode.js +47 -0
- package/dist/slices/admission/start.js +90 -16
- 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/files.js +135 -0
- package/dist/slices/backups/folder.js +43 -0
- package/dist/slices/backups/model.js +65 -0
- package/dist/slices/backups/repo.js +28 -0
- package/dist/slices/backups/schedule.js +60 -0
- package/dist/slices/backups/service.js +311 -0
- package/dist/slices/batch/index.js +9 -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 +113 -24
- 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 +15 -3
- package/dist/slices/library/snapshot.js +8 -2
- 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 +94 -0
- package/dist/slices/notifications/rules.js +102 -0
- package/dist/slices/notifications/send.js +40 -0
- package/dist/slices/notifications/settings.js +34 -0
- 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 +160 -9
- package/dist/slices/play-drafts/readiness.js +3 -0
- package/dist/slices/play-drafts/repo.js +9 -1
- package/dist/slices/play-drafts/review-inputs.js +22 -3
- package/dist/slices/play-drafts/schema.js +109 -2
- package/dist/slices/play-drafts/service.js +10 -1
- package/dist/slices/play-drafts/start.js +43 -18
- package/dist/slices/play-drafts/uploads.js +2 -1
- package/dist/slices/project-templates/from-project.js +70 -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 +29 -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 +67 -2
- 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 +15 -5
- 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 +43 -1
- package/dist/slices/rebuild/recipe-legacy.js +12 -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 +58 -0
- 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 +32 -5
- package/dist/slices/rebuild/recipe-text.js +114 -19
- package/dist/slices/rebuild/recipe-validation.js +29 -3
- package/dist/slices/rebuild/recipe-visual.js +104 -29
- 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 +14 -2
- 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 +69 -0
- package/dist/slices/rebuild/runtime-local.js +154 -27
- 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 +29 -0
- package/dist/slices/rebuild/runtime-provider.js +134 -19
- 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 +48 -23
- 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/service-readiness.js +15 -1
- package/dist/slices/rebuild/soften.js +49 -0
- package/dist/slices/rebuild/transition-repo.js +10 -3
- 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 +12 -2
- package/dist/slices/revisions/adopt.js +9 -2
- package/dist/slices/revisions/downloads.js +7 -1
- package/dist/slices/revisions/image-plan.js +183 -0
- package/dist/slices/revisions/mutation-assets.js +43 -9
- package/dist/slices/revisions/mutation-cues.js +71 -0
- package/dist/slices/revisions/mutation-prepare.js +8 -10
- package/dist/slices/revisions/mutation-request.js +2 -1
- package/dist/slices/revisions/mutations.js +97 -3
- package/dist/slices/revisions/schema.js +16 -2
- 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 +48 -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 +11 -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 +20 -0
- package/dist/slices/storage/model.js +14 -0
- package/dist/slices/storage/portable.js +95 -12
- package/dist/slices/storage/reconcile.js +19 -2
- 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-CAJNT254.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-BGgYMm2u.css +0 -1
- package/dist/web/assets/index-BVVgtntL.js +0 -171
- package/scripts/docker-run.sh +0 -3
- /package/dist/edge/{docker-projects → docker-install}/tree.js +0 -0
|
@@ -3,11 +3,16 @@ import { transact } from "../../kernel/db/tx.js";
|
|
|
3
3
|
import { sourceOf } from "../admission/model.js";
|
|
4
4
|
import { projectById } from "../admission/repo.js";
|
|
5
5
|
import { detectSlots } from "../admission/substitute.js";
|
|
6
|
+
import { projectChannelId } from "../channels/repo.js";
|
|
6
7
|
import { listCheckpoints } from "../checkpoints/repo.js";
|
|
7
8
|
import { documentThemeOf } from "../document/model.js";
|
|
9
|
+
import { imageScaleForm } from "../images/scale.js";
|
|
8
10
|
import { requestHash } from "../play-drafts/repo.js";
|
|
11
|
+
import { reviewSettingsForm } from "../reviews/model.js";
|
|
9
12
|
import { currentRevisionId, revisionById } from "../revisions/repo.js";
|
|
10
13
|
import { shortsExtrasForm } from "../shorts/model.js";
|
|
14
|
+
import { ambientBedFormOf } from "../video/ambient-bed.js";
|
|
15
|
+
import { usesScriptPrompt } from "../voices/model.js";
|
|
11
16
|
import { createTemplate, readTemplate } from "./service.js";
|
|
12
17
|
export const projectTemplateCreateSchema = z
|
|
13
18
|
.object({
|
|
@@ -47,7 +52,11 @@ export function createTemplateFromProject(deps, input) {
|
|
|
47
52
|
const result = createTemplate(deps, {
|
|
48
53
|
id: parsed.data.id,
|
|
49
54
|
name: parsed.data.name,
|
|
50
|
-
|
|
55
|
+
// The project's channel, so the template lands where the video came from.
|
|
56
|
+
document: {
|
|
57
|
+
...documentFromProject(deps, revision),
|
|
58
|
+
channelId: projectChannelId(deps.db, project.id),
|
|
59
|
+
},
|
|
51
60
|
});
|
|
52
61
|
if (result.ok)
|
|
53
62
|
deps.db
|
|
@@ -75,14 +84,21 @@ function documentFromProject(deps, revision) {
|
|
|
75
84
|
updatedAt: revision.createdAt,
|
|
76
85
|
});
|
|
77
86
|
};
|
|
78
|
-
addPrompt("article", config.articlePrompt, "article");
|
|
87
|
+
addPrompt(usesScriptPrompt(config) ? "script" : "article", config.articlePrompt, "article");
|
|
79
88
|
addPrompt("narration", config.narrationPrompt, "narration");
|
|
80
89
|
addPrompt("description", config.descriptionPrompt, "description");
|
|
90
|
+
for (const [stage, picked] of Object.entries(config.reviews?.stages ?? {}))
|
|
91
|
+
addPrompt("review", picked.prompt, `review.${stage}`);
|
|
81
92
|
const definitions = revision.content.imageOrder.flatMap((key) => {
|
|
82
93
|
const definition = revision.content.imageDefinitions[key];
|
|
83
94
|
return definition === undefined ? [] : [definition];
|
|
84
95
|
});
|
|
85
|
-
const imagePrompts =
|
|
96
|
+
const imagePrompts =
|
|
97
|
+
// A project that scales its images with the narration's length keeps its ticked prompts
|
|
98
|
+
// and the setting, rather than one prompt per image of the count it planned.
|
|
99
|
+
config.sources.images === "generate" &&
|
|
100
|
+
definitions.length > 0 &&
|
|
101
|
+
config.imageScale === undefined
|
|
86
102
|
? definitions.map((definition, index) => {
|
|
87
103
|
const key = definition.templateKey ?? "";
|
|
88
104
|
const original = revision.content.promptTemplates[key];
|
|
@@ -103,6 +119,10 @@ function documentFromProject(deps, revision) {
|
|
|
103
119
|
// and a snapshot holds one prompt per kind and name.
|
|
104
120
|
if (!prompts.some((prompt) => prompt.kind === "image" && prompt.name === config.shorts?.imagePrompt))
|
|
105
121
|
addPrompt("image", config.shorts?.imagePrompt, "shortsImage");
|
|
122
|
+
// The establishing image's prompt, the same way.
|
|
123
|
+
if (config.reference?.source === "prompt" &&
|
|
124
|
+
!prompts.some((prompt) => prompt.kind === "image" && prompt.name === config.reference?.prompt))
|
|
125
|
+
addPrompt("image", config.reference.prompt, "referencePrompt");
|
|
106
126
|
for (const category of ["intro", "outro"]) {
|
|
107
127
|
const choice = config[category];
|
|
108
128
|
const body = revision.content.promptTemplates[category] ?? config.rendered[category];
|
|
@@ -125,9 +145,19 @@ function documentFromProject(deps, revision) {
|
|
|
125
145
|
: "",
|
|
126
146
|
audio: config.sources.audio === "provide" ? attachment("Audio from project") : null,
|
|
127
147
|
thumbnail: config.sources.thumbnail === "provide" ? attachment("Thumbnail from project") : null,
|
|
148
|
+
...(config.reference?.source === "provide"
|
|
149
|
+
? { reference: attachment("Establishing image from project") }
|
|
150
|
+
: {}),
|
|
128
151
|
images: config.sources.images === "provide"
|
|
129
152
|
? (definitions.length ? definitions : (config.provided.images ?? [])).map((_, index) => attachment(`Image ${index + 1}`))
|
|
130
153
|
: [],
|
|
154
|
+
// Named like the project's other files, to be attached again on Play.
|
|
155
|
+
...(config.shorts?.enabled === true && revision.content.shortsMusic !== undefined
|
|
156
|
+
? { shortsMusic: attachment("Music from project") }
|
|
157
|
+
: {}),
|
|
158
|
+
...(config.ambientBed?.source === "upload" && revision.content.ambientBed !== undefined
|
|
159
|
+
? { ambientBed: attachment("Ambient sound from project") }
|
|
160
|
+
: {}),
|
|
131
161
|
};
|
|
132
162
|
return {
|
|
133
163
|
schemaVersion: 1,
|
|
@@ -147,9 +177,20 @@ function documentFromProject(deps, revision) {
|
|
|
147
177
|
llm: config.llm ?? { provider: "", model: "" },
|
|
148
178
|
audio: config.audio ?? { provider: "", model: "", voice: "" },
|
|
149
179
|
images: config.images ?? { provider: "", model: "" },
|
|
180
|
+
...(config.reference === undefined
|
|
181
|
+
? {}
|
|
182
|
+
: {
|
|
183
|
+
reference: {
|
|
184
|
+
source: config.reference.source,
|
|
185
|
+
prompt: config.reference.prompt ?? "",
|
|
186
|
+
thumbnail: config.reference.thumbnail !== false,
|
|
187
|
+
},
|
|
188
|
+
}),
|
|
150
189
|
articlePrompt: config.articlePrompt ?? "",
|
|
190
|
+
...(config.thumbnailCount === 3 ? { thumbnailCount: 3 } : {}),
|
|
151
191
|
...(config.narrationPrompt === undefined ? {} : { narrationPrompt: config.narrationPrompt }),
|
|
152
192
|
...(config.youtubeDescription === true ? { youtubeDescription: true } : {}),
|
|
193
|
+
...(config.language === undefined ? {} : { language: config.language }),
|
|
153
194
|
...(config.descriptionPrompt === undefined
|
|
154
195
|
? {}
|
|
155
196
|
: { descriptionPrompt: config.descriptionPrompt }),
|
|
@@ -169,7 +210,31 @@ function documentFromProject(deps, revision) {
|
|
|
169
210
|
// A project without edit settings makes a template without them, so a video made from
|
|
170
211
|
// it cuts every N seconds like the project did.
|
|
171
212
|
...(config.videoEdit === undefined ? {} : { videoEdit: config.videoEdit }),
|
|
213
|
+
...(config.reviews === undefined ? {} : { reviews: reviewSettingsForm(config.reviews) }),
|
|
214
|
+
...(config.voices === undefined ? {} : { voices: config.voices }),
|
|
215
|
+
...(config.ambientBed === undefined
|
|
216
|
+
? {}
|
|
217
|
+
: { ambientBed: ambientBedFormOf(config.ambientBed) }),
|
|
218
|
+
// Level the volume at the project's targets while it is on; off leaves it to Settings'
|
|
219
|
+
// default, which is how a template made before the setting starts a run.
|
|
220
|
+
...(config.loudness === undefined
|
|
221
|
+
? {}
|
|
222
|
+
: {
|
|
223
|
+
loudness: {
|
|
224
|
+
enabled: true,
|
|
225
|
+
videoLufs: config.loudness.videoLufs,
|
|
226
|
+
audioFilesLufs: config.loudness.audioFilesLufs,
|
|
227
|
+
},
|
|
228
|
+
}),
|
|
229
|
+
// The project's pauses; a project without them leaves the template to the defaults.
|
|
230
|
+
...(config.sentencePauseSeconds === undefined
|
|
231
|
+
? {}
|
|
232
|
+
: { sentencePause: String(config.sentencePauseSeconds) }),
|
|
233
|
+
...(config.paragraphPauseSeconds === undefined
|
|
234
|
+
? {}
|
|
235
|
+
: { paragraphPause: String(config.paragraphPauseSeconds) }),
|
|
172
236
|
imagePrompts,
|
|
237
|
+
...(config.imageScale === undefined ? {} : { imageScale: imageScaleForm(config.imageScale) }),
|
|
173
238
|
thumbnailPrompt: config.thumbnailPrompt ?? "",
|
|
174
239
|
intro: config.intro?.name ?? "",
|
|
175
240
|
outro: config.outro?.name ?? "",
|
|
@@ -194,7 +259,8 @@ function documentFromProject(deps, revision) {
|
|
|
194
259
|
},
|
|
195
260
|
section: "content",
|
|
196
261
|
variants: [],
|
|
197
|
-
|
|
262
|
+
// The length a project that scales its images planned for, so the template plans the same.
|
|
263
|
+
expectedWords: String(config.imageScale?.words ?? 1500),
|
|
198
264
|
previewText: "Every story begins with a word.",
|
|
199
265
|
fontUpload: null,
|
|
200
266
|
};
|
|
@@ -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,30 @@
|
|
|
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
|
+
})),
|
|
24
|
+
{
|
|
25
|
+
kind: "image",
|
|
26
|
+
name: input.form.reference?.source === "prompt" ? input.form.reference.prompt : "",
|
|
27
|
+
},
|
|
13
28
|
];
|
|
14
29
|
for (const choice of choices) {
|
|
15
30
|
if (choice.name === "")
|
|
@@ -28,7 +43,8 @@ export function templateSetup(deps, input) {
|
|
|
28
43
|
return { ok: false, reason: "missing-prompt" };
|
|
29
44
|
entries.push(row);
|
|
30
45
|
}
|
|
31
|
-
|
|
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);
|
|
32
48
|
return {
|
|
33
49
|
ok: true,
|
|
34
50
|
value: { ...document, fontUpload: null, librarySnapshot: { prompts, entries } },
|
|
@@ -45,6 +61,7 @@ export function freshTemplateDraft(deps, document, source) {
|
|
|
45
61
|
section: "content",
|
|
46
62
|
fontUpload: null,
|
|
47
63
|
templateSource: source,
|
|
64
|
+
channelId: templateChannelId(deps.db, source.id),
|
|
48
65
|
variants: document.variants.map((variant) => ({ ...variant, id: deps.uuid() })),
|
|
49
66
|
form: {
|
|
50
67
|
...document.form,
|
|
@@ -53,6 +70,16 @@ export function freshTemplateDraft(deps, document, source) {
|
|
|
53
70
|
audio: provided.audio === null ? null : fresh(provided.audio),
|
|
54
71
|
thumbnail: provided.thumbnail === null ? null : fresh(provided.thumbnail),
|
|
55
72
|
images: provided.images.map(fresh),
|
|
73
|
+
...(provided.reference === undefined
|
|
74
|
+
? {}
|
|
75
|
+
: { reference: provided.reference === null ? null : fresh(provided.reference) }),
|
|
76
|
+
// Like the images: the name is kept and the file is attached again on Play.
|
|
77
|
+
...(provided.shortsMusic === undefined
|
|
78
|
+
? {}
|
|
79
|
+
: { shortsMusic: provided.shortsMusic === null ? null : fresh(provided.shortsMusic) }),
|
|
80
|
+
...(provided.ambientBed === undefined
|
|
81
|
+
? {}
|
|
82
|
+
: { ambientBed: provided.ambientBed === null ? null : fresh(provided.ambientBed) }),
|
|
56
83
|
},
|
|
57
84
|
},
|
|
58
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
|
|
@@ -46,9 +50,21 @@ export function previewDetails(deps, view, allRecipes, selected) {
|
|
|
46
50
|
return [
|
|
47
51
|
{
|
|
48
52
|
key: row.key,
|
|
49
|
-
label:
|
|
53
|
+
label: row.key === "reference:image"
|
|
54
|
+
? "Establishing image"
|
|
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}`,
|
|
50
60
|
text: input.prompt,
|
|
51
|
-
settings: [
|
|
61
|
+
settings: [
|
|
62
|
+
input.provider,
|
|
63
|
+
input.model,
|
|
64
|
+
...(input.thinking === undefined ? [] : [`effort ${input.thinking}`]),
|
|
65
|
+
input.aspect,
|
|
66
|
+
...(input.reference === undefined ? [] : ["drawn from the establishing image"]),
|
|
67
|
+
].join(" · "),
|
|
52
68
|
},
|
|
53
69
|
];
|
|
54
70
|
}
|
|
@@ -85,6 +101,9 @@ export function previewDetails(deps, view, allRecipes, selected) {
|
|
|
85
101
|
});
|
|
86
102
|
return { inputChanges: parent === undefined ? [] : inputChanges(parent, view), requests };
|
|
87
103
|
}
|
|
104
|
+
function loudnessSummary(value) {
|
|
105
|
+
return `Video ${loudnessLabel(value.videoLufs, "video")}; audio files ${loudnessLabel(value.audioFilesLufs, "audioFiles")}`;
|
|
106
|
+
}
|
|
88
107
|
function narrationIdentity(row) {
|
|
89
108
|
if (row.input.kind === "tts")
|
|
90
109
|
return { logicalKey: row.input.logicalKey, segment: row.input.segment };
|
|
@@ -128,9 +147,17 @@ function inputChanges(parent, view) {
|
|
|
128
147
|
add(`${name} model`, a?.model, b?.model);
|
|
129
148
|
add(`${name} thinking`, a?.thinking, b?.thinking);
|
|
130
149
|
}
|
|
150
|
+
add("Language", languageInfo(before.language).name, languageInfo(after.language).name);
|
|
131
151
|
add("Narration voice", before.audio?.voice, after.audio?.voice);
|
|
132
152
|
add("Narration Preparation", before.narrationPrompt, after.narrationPrompt);
|
|
133
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));
|
|
134
161
|
add("YouTube description", before.youtubeDescription === true ? "On" : "Off", after.youtubeDescription === true ? "On" : "Off");
|
|
135
162
|
add("Description prompt", before.descriptionPrompt, after.descriptionPrompt);
|
|
136
163
|
add("Shorts", before.shorts?.enabled === true ? "On" : "Off", after.shorts?.enabled === true ? "On" : "Off");
|
|
@@ -140,8 +167,22 @@ function inputChanges(parent, view) {
|
|
|
140
167
|
: `${String(before.shorts.minSeconds)}-${String(before.shorts.maxSeconds)}`, after.shorts === undefined
|
|
141
168
|
? undefined
|
|
142
169
|
: `${String(after.shorts.minSeconds)}-${String(after.shorts.maxSeconds)}`);
|
|
170
|
+
add("Thumbnails", before.thumbnailCount === 3 ? "3" : "1", after.thumbnailCount === 3 ? "3" : "1");
|
|
143
171
|
add("Shorts prompt", before.shorts?.prompt, after.shorts?.prompt);
|
|
144
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
|
+
}
|
|
180
|
+
add("Establishing image", referenceLabel(before), referenceLabel(after));
|
|
181
|
+
add("Thumbnail drawn from the establishing image", before.reference === undefined
|
|
182
|
+
? undefined
|
|
183
|
+
: before.reference.thumbnail === false
|
|
184
|
+
? "Off"
|
|
185
|
+
: "On", after.reference === undefined ? undefined : after.reference.thumbnail === false ? "Off" : "On");
|
|
145
186
|
add("Title on the shorts", before.shorts === undefined ? undefined : before.shorts.titleOnScreen === true ? "On" : "Off", after.shorts === undefined ? undefined : after.shorts.titleOnScreen === true ? "On" : "Off");
|
|
146
187
|
add("Shorts speed", before.shorts === undefined ? undefined : `${shortsSpeedOf(before.shorts).toFixed(2)}×`, after.shorts === undefined ? undefined : `${shortsSpeedOf(after.shorts).toFixed(2)}×`);
|
|
147
188
|
add("Shorts music", old.shortsMusic === undefined ? "None" : "Uploaded file", next.shortsMusic === undefined
|
|
@@ -154,6 +195,7 @@ function inputChanges(parent, view) {
|
|
|
154
195
|
add("Short ranges set by hand", old.shortsRanges, next.shortsRanges);
|
|
155
196
|
add("Silence gap (seconds)", before.silenceGapSeconds, after.silenceGapSeconds);
|
|
156
197
|
add("Silence at start and end (seconds)", before.edgeSilenceSeconds, after.edgeSilenceSeconds);
|
|
198
|
+
add("Ambient sound", ambientBedLabel(before, old), ambientBedLabel(after, next));
|
|
157
199
|
add("Seconds per image", before.imageSeconds, after.imageSeconds);
|
|
158
200
|
add("Zoom (%)", before.zoomPercent, after.zoomPercent);
|
|
159
201
|
add("Motion", motionStyleLabels[before.motionStyle], motionStyleLabels[after.motionStyle]);
|
|
@@ -191,3 +233,26 @@ function inputChanges(parent, view) {
|
|
|
191
233
|
add("Manual captions", old.subtitleCues?.cues, next.subtitleCues?.cues);
|
|
192
234
|
return changes;
|
|
193
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
|
+
}
|
|
245
|
+
function referenceLabel(config) {
|
|
246
|
+
const reference = config.reference;
|
|
247
|
+
if (reference === undefined)
|
|
248
|
+
return "Off";
|
|
249
|
+
return reference.source === "provide" ? "Uploaded image" : `Prompt: ${reference.prompt ?? ""}`;
|
|
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,
|