@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,286 @@
|
|
|
1
|
+
import { aliasMatches, applyAliases } from "../../kernel/ports/narration-aliases.js";
|
|
2
|
+
import { splitText } from "../../kernel/ports/text.js";
|
|
3
|
+
import { narrationAliasesOf, usesNarrationPreparation } from "../admission/rules.js";
|
|
4
|
+
import { aliasSpans, withAliasSpans } from "../narration/aliases.js";
|
|
5
|
+
import { normalizeNarrationText } from "../narration/plan.js";
|
|
6
|
+
import { parsePronunciationGlossary, pronunciationSpans, } from "../narration/pronunciation.js";
|
|
7
|
+
import { withSharedGlossary } from "../narration/shared-glossary.js";
|
|
8
|
+
import { prepareRequests } from "../narration/steering.js";
|
|
9
|
+
import { groupTurns } from "../voices/grouping.js";
|
|
10
|
+
import { speakerPace } from "../voices/model.js";
|
|
11
|
+
import { parseScript } from "../voices/script.js";
|
|
12
|
+
import { describedText, describeFuture, describing } from "./recipe-describe.js";
|
|
13
|
+
import { recipe, resourceIdentity, } from "./recipe-model.js";
|
|
14
|
+
import { pauseValues } from "./recipe-pauses.js";
|
|
15
|
+
import { preparationForGroup, preparationFuture, preparationTemplate, } from "./recipe-preparation.js";
|
|
16
|
+
export function voiceBodyRecipes(context, voices, script, glossary) {
|
|
17
|
+
const parts = [];
|
|
18
|
+
const preparations = [];
|
|
19
|
+
const layout = [];
|
|
20
|
+
// Which parts end a turn, for the pauses between sentences (a turn gap of 0 is still a turn).
|
|
21
|
+
const turnEnds = new Set();
|
|
22
|
+
let transcript = script.fingerprint ?? script.text ?? null;
|
|
23
|
+
const describe = describing(context);
|
|
24
|
+
const parsed = script.text === null ? undefined : parseScript(script.text, voices.speakers, describe);
|
|
25
|
+
const prepare = voices.speakers.some((speaker) => preparesTurns(context.config, speaker));
|
|
26
|
+
// The narration still to be worked out: the whole script's while it is being written, or
|
|
27
|
+
// every turn's while a turn's delivery cues are (as a single voice waits for all of its own).
|
|
28
|
+
const future = () => recipe(context, "audio:body:future", "audio", {
|
|
29
|
+
kind: "deferred",
|
|
30
|
+
version: 1,
|
|
31
|
+
operation: "body-narration",
|
|
32
|
+
template: [
|
|
33
|
+
script.fingerprint ?? script.text ?? null,
|
|
34
|
+
speakerValues(voices),
|
|
35
|
+
...(prepare ? [preparationTemplate(context)] : []),
|
|
36
|
+
...(described.length === 0
|
|
37
|
+
? []
|
|
38
|
+
: [["described-v1", described.map((row) => row.fingerprint)]]),
|
|
39
|
+
],
|
|
40
|
+
}, [
|
|
41
|
+
...new Set([
|
|
42
|
+
...script.dependsOn,
|
|
43
|
+
...described.map((row) => row.key),
|
|
44
|
+
...preparations.map((row) => row.key),
|
|
45
|
+
]),
|
|
46
|
+
]);
|
|
47
|
+
let pending = false;
|
|
48
|
+
// A table, figure, equation or code block inside a turn is described within that turn: the
|
|
49
|
+
// turn says the description where the block was, in the same voice.
|
|
50
|
+
const described = [];
|
|
51
|
+
const cards = [];
|
|
52
|
+
let turns = parsed?.ok === true ? parsed.script.turns : [];
|
|
53
|
+
if (describe && script.text === null) {
|
|
54
|
+
const from = script.dependsOn[0];
|
|
55
|
+
if (from !== undefined)
|
|
56
|
+
described.push(describeFuture(context, { key: from, fingerprint: script.fingerprint ?? from }));
|
|
57
|
+
}
|
|
58
|
+
else if (describe && parsed?.ok === true) {
|
|
59
|
+
let offset = 0;
|
|
60
|
+
turns = turns.map((turn) => {
|
|
61
|
+
if (turn.markdown === undefined)
|
|
62
|
+
return turn;
|
|
63
|
+
const said = describedText(context, turn.markdown, script.dependsOn, offset);
|
|
64
|
+
if (said.recipes.length === 0)
|
|
65
|
+
return turn;
|
|
66
|
+
offset += said.recipes.length;
|
|
67
|
+
described.push(...said.recipes);
|
|
68
|
+
cards.push(...said.cards);
|
|
69
|
+
if (said.spoken === null) {
|
|
70
|
+
pending = true;
|
|
71
|
+
return turn;
|
|
72
|
+
}
|
|
73
|
+
return { ...turn, text: said.spoken.trim().replace(/\s*\n\s*/g, " ") };
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
preparations.push(...described);
|
|
77
|
+
if (script.text === null) {
|
|
78
|
+
// Turns not yet known are prepared behind one future step, as a single voice's text is.
|
|
79
|
+
const from = script.dependsOn[0];
|
|
80
|
+
if (prepare && from !== undefined)
|
|
81
|
+
preparations.push(preparationFuture(context, "body", { key: from, fingerprint: script.fingerprint ?? from }));
|
|
82
|
+
parts.push(future());
|
|
83
|
+
}
|
|
84
|
+
else if (parsed !== undefined && !parsed.ok) {
|
|
85
|
+
parts.push(refused(context, "audio:body:turn:1", script.dependsOn, `The script can't be narrated: ${parsed.reason} Fix it in Edit project → Article, then Try again.`));
|
|
86
|
+
}
|
|
87
|
+
else if (pending) {
|
|
88
|
+
parts.push(future());
|
|
89
|
+
}
|
|
90
|
+
else if (parsed !== undefined) {
|
|
91
|
+
transcript = ["voices-transcript-v1", turns.map((turn) => [turn.speaker, turn.text])];
|
|
92
|
+
const aliases = narrationAliasesOf(context.config);
|
|
93
|
+
for (const group of groupTurns(turns, voices.speakers, voices.nativeDialogue, (text) => applyAliases(text, aliases).length)) {
|
|
94
|
+
const first = group.turns[0];
|
|
95
|
+
const speaker = voices.speakers.find((one) => one.id === first?.speaker);
|
|
96
|
+
if (first === undefined || speaker === undefined)
|
|
97
|
+
continue;
|
|
98
|
+
const made = groupParts(context, voices, group.native, group.turns, speaker, glossary, script, preparations);
|
|
99
|
+
if (made.length === 0)
|
|
100
|
+
pending = true;
|
|
101
|
+
made.forEach((part, index) => {
|
|
102
|
+
parts.push(part);
|
|
103
|
+
if (index === made.length - 1)
|
|
104
|
+
turnEnds.add(layout.length);
|
|
105
|
+
layout.push([speakerPace(speaker), index === made.length - 1 ? voices.turnGapSeconds : 0]);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
// No gap after the last turn: the timeline's own gap follows it.
|
|
109
|
+
const last = layout.at(-1);
|
|
110
|
+
if (last !== undefined)
|
|
111
|
+
last[1] = 0;
|
|
112
|
+
if (pending) {
|
|
113
|
+
parts.length = 0;
|
|
114
|
+
layout.length = 0;
|
|
115
|
+
turnEnds.clear();
|
|
116
|
+
parts.push(future());
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const body = recipe(context, "audio:body:concat", "audio", {
|
|
120
|
+
kind: "local",
|
|
121
|
+
version: 1,
|
|
122
|
+
operation: "concat-turns-v1",
|
|
123
|
+
values: [
|
|
124
|
+
parts.map((part) => resourceIdentity(context, part)),
|
|
125
|
+
layout,
|
|
126
|
+
// Pauses between sentences inside a turn; the turn gap stays the layout's.
|
|
127
|
+
...pauseValues(context.config, layout.map(([, gap], at) => at === layout.length - 1 ? "end" : gap > 0 || turnEnds.has(at) ? "turn" : "sentence")),
|
|
128
|
+
],
|
|
129
|
+
}, parts.map((part) => part.key), { unresolved: parsed?.ok !== true || pending });
|
|
130
|
+
return {
|
|
131
|
+
preparations,
|
|
132
|
+
cards,
|
|
133
|
+
parts,
|
|
134
|
+
body,
|
|
135
|
+
transcript,
|
|
136
|
+
...(parsed?.ok === true ? { sections: parsed.script.sections } : {}),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function groupParts(context, voices, native, turns, speaker, glossary, script, preparations) {
|
|
140
|
+
const first = turns[0];
|
|
141
|
+
if (first === undefined)
|
|
142
|
+
return [];
|
|
143
|
+
const logicalKey = `audio:body:turn:${String(first.index)}`;
|
|
144
|
+
const override = context.content.narrationOverrides[logicalKey];
|
|
145
|
+
if (override?.kind === "asset")
|
|
146
|
+
return [
|
|
147
|
+
recipe(context, `${logicalKey}:1`, "audio", {
|
|
148
|
+
kind: "provided",
|
|
149
|
+
version: 1,
|
|
150
|
+
assetId: override.assetId,
|
|
151
|
+
semantic: [turns.map((turn) => turn.text).join("\n"), speaker.id],
|
|
152
|
+
}, script.dependsOn, { tokenKey: logicalKey }),
|
|
153
|
+
];
|
|
154
|
+
if (native) {
|
|
155
|
+
const byId = new Map(voices.speakers.map((one) => [one.id, one]));
|
|
156
|
+
// Narration aliases reach every line of the one request, each turn on its own so an alias
|
|
157
|
+
// never joins the end of one turn to the start of the next. The clean turns stay the
|
|
158
|
+
// transcript, as they do for a voice that gets one request per turn.
|
|
159
|
+
const aliases = narrationAliasesOf(context.config);
|
|
160
|
+
const spoken = turns.map((turn) => applyAliases(turn.text, aliases));
|
|
161
|
+
const logicalText = turns.map((turn) => turn.text).join("\n");
|
|
162
|
+
const text = spoken.join("\n");
|
|
163
|
+
return [
|
|
164
|
+
recipe(context, `${logicalKey}:1`, "audio", {
|
|
165
|
+
kind: "tts",
|
|
166
|
+
version: 1,
|
|
167
|
+
provider: speaker.voice.provider,
|
|
168
|
+
model: speaker.voice.model,
|
|
169
|
+
voice: speaker.voice.voice,
|
|
170
|
+
text,
|
|
171
|
+
...(text === logicalText ? {} : { spokenText: logicalText }),
|
|
172
|
+
logicalKey,
|
|
173
|
+
logicalText,
|
|
174
|
+
segment: "body",
|
|
175
|
+
pronunciation: null,
|
|
176
|
+
dialogue: turns.map((turn, index) => ({
|
|
177
|
+
speaker: turn.speaker,
|
|
178
|
+
turn: turn.index,
|
|
179
|
+
voice: byId.get(turn.speaker)?.voice.voice ?? "",
|
|
180
|
+
text: spoken[index] ?? turn.text,
|
|
181
|
+
})),
|
|
182
|
+
}, script.dependsOn, { tokenKey: logicalKey }),
|
|
183
|
+
];
|
|
184
|
+
}
|
|
185
|
+
const logicalText = normalizeNarrationText(override?.kind === "text" ? override.text : first.text);
|
|
186
|
+
const entries = speakerGlossary(context, speaker, glossary);
|
|
187
|
+
if (!entries.ok)
|
|
188
|
+
return [refused(context, logicalKey, script.dependsOn, entries.reason)];
|
|
189
|
+
const maxCharacters = context.catalogue?.tts.find((row) => row.provider === speaker.voice.provider &&
|
|
190
|
+
row.id === speaker.voice.model &&
|
|
191
|
+
row.enabled &&
|
|
192
|
+
!row.deprecated)?.tts.maxCharacters ?? Math.max(2, logicalText.length * 4);
|
|
193
|
+
// Narration aliases apply to every voice, the speaker's pronunciations to IPA voices only.
|
|
194
|
+
const spans = withAliasSpans(pronunciationSpans(logicalText, entries.entries), aliasSpans(logicalText, narrationAliasesOf(context.config)));
|
|
195
|
+
if (preparesTurns(context.config, speaker)) {
|
|
196
|
+
const prepared = preparationForGroup(context, logicalKey, logicalText, "body", script.dependsOn, maxCharacters, spans, aliasMatches(logicalText, narrationAliasesOf(context.config)), { name: speaker.name.trim(), role: speaker.role });
|
|
197
|
+
preparations.push(prepared.preparation);
|
|
198
|
+
if (prepared.refusal !== null)
|
|
199
|
+
return [refused(context, logicalKey, [prepared.preparation.key], prepared.refusal)];
|
|
200
|
+
return (prepared.requests ?? []).map((request, index) => recipe(context, `${logicalKey}:${String(index + 1)}`, "audio", {
|
|
201
|
+
kind: "tts",
|
|
202
|
+
version: 1,
|
|
203
|
+
provider: speaker.voice.provider,
|
|
204
|
+
model: speaker.voice.model,
|
|
205
|
+
voice: speaker.voice.voice,
|
|
206
|
+
text: request.text,
|
|
207
|
+
spokenText: request.spokenText,
|
|
208
|
+
logicalKey,
|
|
209
|
+
logicalText,
|
|
210
|
+
segment: "body",
|
|
211
|
+
pronunciation: null,
|
|
212
|
+
speaker: speaker.id,
|
|
213
|
+
turn: first.index,
|
|
214
|
+
}, [prepared.preparation.key], { tokenKey: logicalKey, unresolved: logicalText.length === 0 }));
|
|
215
|
+
}
|
|
216
|
+
let requests;
|
|
217
|
+
if (spans.length > 0) {
|
|
218
|
+
const prepared = prepareRequests(logicalText, [], maxCharacters, spans);
|
|
219
|
+
if (!prepared.ok)
|
|
220
|
+
return [refused(context, logicalKey, script.dependsOn, prepared.reason)];
|
|
221
|
+
requests = prepared.requests;
|
|
222
|
+
}
|
|
223
|
+
else
|
|
224
|
+
requests = splitText(logicalText, maxCharacters).map((text) => ({ text }));
|
|
225
|
+
return requests.map((request, index) => recipe(context, `${logicalKey}:${String(index + 1)}`, "audio", {
|
|
226
|
+
kind: "tts",
|
|
227
|
+
version: 1,
|
|
228
|
+
provider: speaker.voice.provider,
|
|
229
|
+
model: speaker.voice.model,
|
|
230
|
+
voice: speaker.voice.voice,
|
|
231
|
+
text: request.text,
|
|
232
|
+
...(request.spokenText === undefined ? {} : { spokenText: request.spokenText }),
|
|
233
|
+
logicalKey,
|
|
234
|
+
logicalText,
|
|
235
|
+
segment: "body",
|
|
236
|
+
pronunciation: null,
|
|
237
|
+
speaker: speaker.id,
|
|
238
|
+
turn: first.index,
|
|
239
|
+
}, script.dependsOn, { tokenKey: logicalKey, unresolved: logicalText.length === 0 }));
|
|
240
|
+
}
|
|
241
|
+
// IPA pronunciations reach only the voices that read IPA: the speaker's own first, then the
|
|
242
|
+
// run's glossary where Use Pronunciation Glossary is on.
|
|
243
|
+
function speakerGlossary(context, speaker, glossary) {
|
|
244
|
+
if (!readsIpa(speaker))
|
|
245
|
+
return { ok: true, entries: [] };
|
|
246
|
+
const own = speaker.pronunciations?.trim()
|
|
247
|
+
? parsePronunciationGlossary(speaker.pronunciations, context.config.language)
|
|
248
|
+
: { ok: true, entries: [] };
|
|
249
|
+
if (!own.ok)
|
|
250
|
+
return {
|
|
251
|
+
ok: false,
|
|
252
|
+
reason: `${speaker.name.trim()}'s pronunciations can't be used: ${own.reason.replace("Edit the glossary or turn off Use Pronunciation Glossary.", "Fix them under Speakers (Play → Audio, or Edit project → Providers).")}`,
|
|
253
|
+
};
|
|
254
|
+
const run = context.config.audio?.usePronunciationGlossary === true && glossary?.ok === true
|
|
255
|
+
? glossary.entries
|
|
256
|
+
: [];
|
|
257
|
+
return { ok: true, entries: withSharedGlossary(own.entries, run) };
|
|
258
|
+
}
|
|
259
|
+
// A turn is prepared when the run has a Narration Preparation prompt and its speaker's voice
|
|
260
|
+
// takes Inworld TTS-2's delivery tags; every other speaker's turns are spoken as written.
|
|
261
|
+
export function preparesTurns(config, speaker) {
|
|
262
|
+
return (usesNarrationPreparation(config) &&
|
|
263
|
+
speaker.voice.provider === "inworld" &&
|
|
264
|
+
speaker.voice.model === "inworld-tts-2");
|
|
265
|
+
}
|
|
266
|
+
export function readsIpa(speaker) {
|
|
267
|
+
return (speaker.voice.provider === "inworld" &&
|
|
268
|
+
(speaker.voice.model === "inworld-tts-2" || speaker.voice.model === "inworld-tts-2-flash"));
|
|
269
|
+
}
|
|
270
|
+
function refused(context, logicalKey, dependsOn, reason) {
|
|
271
|
+
return recipe(context, `${logicalKey}:1`, "audio", { kind: "deferred", version: 1, operation: "resolve-revision-recipe", template: reason }, dependsOn, { unresolved: true, refusal: reason });
|
|
272
|
+
}
|
|
273
|
+
// What a pending script will be spoken with, so a voice change before the script lands is
|
|
274
|
+
// still a change.
|
|
275
|
+
export function speakerValues(voices) {
|
|
276
|
+
return [
|
|
277
|
+
voices.nativeDialogue,
|
|
278
|
+
voices.speakers.map((speaker) => [
|
|
279
|
+
speaker.id,
|
|
280
|
+
speaker.voice.provider,
|
|
281
|
+
speaker.voice.model,
|
|
282
|
+
speaker.voice.voice,
|
|
283
|
+
speaker.pronunciations ?? null,
|
|
284
|
+
]),
|
|
285
|
+
];
|
|
286
|
+
}
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { estimateRequests } from "../estimate/index.js";
|
|
3
|
+
import { skippedGlossaryNotice, skippedSpeakerPronunciations, skippedSpeakerPronunciationsNotice, } from "../narration/pronunciation.js";
|
|
4
|
+
import { isLocalCliProvider } from "../settings/model.js";
|
|
3
5
|
import { defaultShortsPrompt, shortsImageUpperBound } from "../shorts/model.js";
|
|
6
|
+
import { usesVoices } from "../voices/model.js";
|
|
4
7
|
import { planDependencies } from "./dependencies.js";
|
|
5
8
|
import { matchingNarrationPiece } from "./narration-reuse.js";
|
|
6
9
|
import { buildRecipes } from "./recipe-build.js";
|
|
7
10
|
import { manualCuesNeedReview } from "./recipe-exports.js";
|
|
8
11
|
import { selectedReference, } from "./recipe-model.js";
|
|
12
|
+
import { recipeProviderChoice } from "./recipe-provider-choice.js";
|
|
13
|
+
import { narrationGlossary } from "./recipe-text.js";
|
|
14
|
+
import { readsIpa } from "./recipe-voices.js";
|
|
9
15
|
export function planRevisionWork(revision, manifest, catalogue, availableAssetIds, resolved, retainedNarration = []) {
|
|
10
16
|
const selected = {
|
|
11
17
|
outputs: manifest.outputs.filter(selectedReference),
|
|
@@ -27,6 +33,7 @@ export function planRevisionWork(revision, manifest, catalogue, availableAssetId
|
|
|
27
33
|
resolved: { ...resolved, articleMarkdown: usableArticle ? resolved.articleMarkdown : null },
|
|
28
34
|
};
|
|
29
35
|
const logical = buildRecipes(logicalContext);
|
|
36
|
+
const glossary = narrationGlossary(logicalContext);
|
|
30
37
|
const context = { ...logicalContext, catalogue };
|
|
31
38
|
const recipes = buildRecipes(context).map((row) => ({
|
|
32
39
|
...row,
|
|
@@ -64,7 +71,7 @@ export function planRevisionWork(revision, manifest, catalogue, availableAssetId
|
|
|
64
71
|
null,
|
|
65
72
|
after: row.fingerprint,
|
|
66
73
|
}));
|
|
67
|
-
const priced = work.flatMap((row) => priceRecipes(row, recipes.find((one) => one.key === row.key)));
|
|
74
|
+
const priced = work.flatMap((row) => priceRecipes(row, recipes.find((one) => one.key === row.key), revision.config));
|
|
68
75
|
return {
|
|
69
76
|
recipes,
|
|
70
77
|
work,
|
|
@@ -74,6 +81,13 @@ export function planRevisionWork(revision, manifest, catalogue, availableAssetId
|
|
|
74
81
|
(revision.config.chunking?.mode ?? "whole") === "whole"
|
|
75
82
|
? "Whole-text narration is one logical request. Editing its text rebuilds every provider part of that request."
|
|
76
83
|
: null,
|
|
84
|
+
glossaryNotice: glossary?.ok === true && glossary.skipped !== undefined
|
|
85
|
+
? skippedGlossaryNotice(glossary.skipped)
|
|
86
|
+
: null,
|
|
87
|
+
// Only speakers whose voice reads IPA use their pronunciations at all.
|
|
88
|
+
speakerPronunciationNotice: usesVoices(revision.config) && revision.config.voices !== undefined
|
|
89
|
+
? skippedSpeakerPronunciationsNotice(skippedSpeakerPronunciations(revision.config.voices.speakers.filter(readsIpa), revision.config.language))
|
|
90
|
+
: null,
|
|
77
91
|
costs: estimateRequests(priced, catalogue),
|
|
78
92
|
};
|
|
79
93
|
}
|
|
@@ -187,14 +201,16 @@ const shortsTranscriptEstimate = 24000;
|
|
|
187
201
|
// What a step is charged as. Most steps are one request; what stands in for the shorts
|
|
188
202
|
// until they are picked is every clip's image prompts and, at the longest length allowed,
|
|
189
203
|
// every image, which is the most the step can cost.
|
|
190
|
-
export function priceRecipes(work, value
|
|
204
|
+
export function priceRecipes(work, value,
|
|
205
|
+
// The revision's settings, which name the provider of a request built only when it runs.
|
|
206
|
+
config) {
|
|
191
207
|
const input = value?.input;
|
|
192
208
|
const unfolds = value?.unfoldsImages;
|
|
193
209
|
// A short's image prompts once the clips are picked: the call, and the images it leads to,
|
|
194
210
|
// as many as the clip's length asks for.
|
|
195
211
|
if (work.disposition === "generate" && unfolds !== undefined)
|
|
196
212
|
return [
|
|
197
|
-
priceRecipe(work, value),
|
|
213
|
+
priceRecipe(work, value, config),
|
|
198
214
|
...Array.from({ length: unfolds.count }, () => ({
|
|
199
215
|
kind: "image",
|
|
200
216
|
stage: work.key,
|
|
@@ -223,7 +239,7 @@ export function priceRecipes(work, value) {
|
|
|
223
239
|
input?.kind !== "deferred" ||
|
|
224
240
|
input.operation !== "shorts" ||
|
|
225
241
|
!Array.isArray(input.template))
|
|
226
|
-
return [priceRecipe(work, value)];
|
|
242
|
+
return [priceRecipe(work, value, config)];
|
|
227
243
|
const [, style, provider, model, imageSeconds, count, maxSeconds, llmProvider, llmModel] = input.template;
|
|
228
244
|
const clips = typeof count === "number" ? count : 0;
|
|
229
245
|
const images = typeof imageSeconds === "number" && typeof maxSeconds === "number"
|
|
@@ -249,7 +265,7 @@ export function priceRecipes(work, value) {
|
|
|
249
265
|
})),
|
|
250
266
|
];
|
|
251
267
|
}
|
|
252
|
-
export function priceRecipe(work, value) {
|
|
268
|
+
export function priceRecipe(work, value, config) {
|
|
253
269
|
if (value === undefined || work.disposition !== "generate")
|
|
254
270
|
return {
|
|
255
271
|
kind: "local",
|
|
@@ -259,12 +275,21 @@ export function priceRecipe(work, value) {
|
|
|
259
275
|
: "Local, provided, or blocked work; no admitted generation charge.",
|
|
260
276
|
};
|
|
261
277
|
const input = value.input;
|
|
278
|
+
const onPlan = config === undefined ? undefined : cliRequest(work, value, config);
|
|
279
|
+
if (onPlan !== undefined)
|
|
280
|
+
return onPlan;
|
|
262
281
|
if (input.kind === "deferred" && input.operation === "narration-preparation")
|
|
263
282
|
return {
|
|
264
283
|
kind: "unknown",
|
|
265
284
|
stage: work.key,
|
|
266
285
|
detail: "Narration Preparation: one LLM call per future logical chunk or entry; source length is not yet known.",
|
|
267
286
|
};
|
|
287
|
+
if (input.kind === "deferred" && input.operation === "narration-description")
|
|
288
|
+
return {
|
|
289
|
+
kind: "unknown",
|
|
290
|
+
stage: work.key,
|
|
291
|
+
detail: "Narration descriptions: one LLM call per table, figure, equation or code block in the article; how many is known once the article is written.",
|
|
292
|
+
};
|
|
268
293
|
if (input.kind === "llm")
|
|
269
294
|
return {
|
|
270
295
|
kind: "llm",
|
|
@@ -273,7 +298,7 @@ export function priceRecipe(work, value) {
|
|
|
273
298
|
model: input.model,
|
|
274
299
|
inputCharacters: input.messages.reduce((sum, message) => sum + message.content.length, 0) +
|
|
275
300
|
(input.documents ?? []).reduce((sum, document) => sum + document.content.length, 0),
|
|
276
|
-
outputCharacters: work.key === "article:body" ? 9000 : 2400,
|
|
301
|
+
outputCharacters: work.key === "article:body" ? 9000 : input.describe !== undefined ? 600 : 2400,
|
|
277
302
|
detail: "Actual saved request input; output length is estimated. Retries and tools are excluded.",
|
|
278
303
|
};
|
|
279
304
|
if (input.kind === "tts")
|
|
@@ -299,6 +324,21 @@ export function priceRecipe(work, value) {
|
|
|
299
324
|
detail: "The numbered transcript is built when the step runs; its length is estimated. One more call is made when the first answer breaks the rules. Retries are excluded.",
|
|
300
325
|
};
|
|
301
326
|
}
|
|
327
|
+
// A review: the stage's prompt and the item (the article's text, or a short brief beside an
|
|
328
|
+
// image, which the provider prices as it does).
|
|
329
|
+
if (input.kind === "local" && input.operation === "review-v1") {
|
|
330
|
+
const [stage, , , provider, model, , prompt] = Array.isArray(input.values) ? input.values : [];
|
|
331
|
+
return {
|
|
332
|
+
kind: "llm",
|
|
333
|
+
stage: work.key,
|
|
334
|
+
provider: typeof provider === "string" ? provider : "",
|
|
335
|
+
model: typeof model === "string" ? model : "",
|
|
336
|
+
inputCharacters: (typeof prompt === "string" && prompt !== "" ? prompt.length : 600) +
|
|
337
|
+
(stage === "article" || stage === "narration" ? 20000 : 1500),
|
|
338
|
+
outputCharacters: 400,
|
|
339
|
+
detail: "The item is read when the step runs; its length is estimated. A failed item made again is reviewed again, which is not in this estimate.",
|
|
340
|
+
};
|
|
341
|
+
}
|
|
302
342
|
if (input.kind === "local" && input.operation === "youtube-description-v1") {
|
|
303
343
|
const [, provider, model, , prompt] = Array.isArray(input.values) ? input.values : [];
|
|
304
344
|
return {
|
|
@@ -320,3 +360,34 @@ export function priceRecipe(work, value) {
|
|
|
320
360
|
detail: "Future generated input is unknown. This group materializes under the same admitted revision; its final request count and charge are unknown.",
|
|
321
361
|
};
|
|
322
362
|
}
|
|
363
|
+
// A request built only when its step runs, on a command-line provider: it runs on the user's
|
|
364
|
+
// plan, so it is known at $0, as Play's review prices the same step. Its size is estimated
|
|
365
|
+
// for the API figure beside it; Narration Preparation's number of calls is not known yet, so
|
|
366
|
+
// it has none.
|
|
367
|
+
function cliRequest(work, value, config) {
|
|
368
|
+
if (value.input.kind !== "deferred")
|
|
369
|
+
return undefined;
|
|
370
|
+
const choice = recipeProviderChoice(value, config);
|
|
371
|
+
if (choice === undefined || !isLocalCliProvider(choice.provider))
|
|
372
|
+
return undefined;
|
|
373
|
+
if (choice.family === "image")
|
|
374
|
+
return { kind: "image", stage: work.key, provider: choice.provider, model: choice.model };
|
|
375
|
+
if (choice.family !== "llm")
|
|
376
|
+
return undefined;
|
|
377
|
+
const preparation = value.input.operation === "narration-preparation" ||
|
|
378
|
+
value.input.operation === "narration-description";
|
|
379
|
+
return {
|
|
380
|
+
kind: "llm",
|
|
381
|
+
stage: work.key,
|
|
382
|
+
provider: choice.provider,
|
|
383
|
+
model: choice.model,
|
|
384
|
+
inputCharacters: 6000,
|
|
385
|
+
outputCharacters: work.key === "article:body" ? 9000 : 2400,
|
|
386
|
+
...(preparation ? { apiUnknown: true } : {}),
|
|
387
|
+
detail: preparation
|
|
388
|
+
? value.input.operation === "narration-description"
|
|
389
|
+
? "Runs on your CLI plan, so it adds no charge. Narration descriptions make one call per table, figure, equation or code block; how many is known once the article is written, so no API figure is given."
|
|
390
|
+
: "Runs on your CLI plan, so it adds no charge. Narration Preparation makes one call per logical chunk or entry; how many is known when the step runs, so no API figure is given."
|
|
391
|
+
: "Runs on your CLI plan, so it adds no charge. The request is built when the step runs, so its API figure uses an estimated length.",
|
|
392
|
+
};
|
|
393
|
+
}
|
|
@@ -25,6 +25,8 @@ export function youtubeRecipes(context, exports) {
|
|
|
25
25
|
// ships as a new operation version rather than a silent change here.
|
|
26
26
|
config.descriptionPrompt?.trim() ? renderedPrompt(context, "description") : "",
|
|
27
27
|
config.title,
|
|
28
|
+
// The project language, only when it is not English, so English keeps its fingerprint.
|
|
29
|
+
...(config.language === undefined || config.language === "en" ? [] : [config.language]),
|
|
28
30
|
],
|
|
29
31
|
}, [timing.key],
|
|
30
32
|
// A paid LLM call: readiness, the charge warning and the estimate treat it like one.
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { projectPaused } from "../admission/repo.js";
|
|
2
|
-
|
|
2
|
+
//
|
|
3
|
+
// An automatic redo (`pendingSuperseded`) runs while the rest of the run waits: admitted work
|
|
4
|
+
// that has not started is replaced by the redo's own, so only running work and accepted jobs
|
|
5
|
+
// stand in its way.
|
|
6
|
+
export function activeConflict(deps, projectId, keys, pendingSuperseded = false) {
|
|
3
7
|
const rows = deps.db
|
|
4
8
|
.prepare("SELECT work_key,logical_key,live FROM (SELECT p.work_key,r.logical_key," +
|
|
5
9
|
"(w.state='running' OR (? AND w.state='pending' AND w.dispatch_state='allowed' AND NOT EXISTS(" +
|
|
@@ -10,7 +14,7 @@ export function activeConflict(deps, projectId, keys) {
|
|
|
10
14
|
"FROM revision_work w JOIN revision_work_pieces p ON p.work_id=w.id " +
|
|
11
15
|
"LEFT JOIN revision_work_reservations r ON r.work_id=w.id AND r.piece_id=p.id " +
|
|
12
16
|
"WHERE w.project_id=?) WHERE live OR accepted")
|
|
13
|
-
.all(projectPaused(deps.db, projectId) ? 0 : 1, projectId)
|
|
17
|
+
.all(pendingSuperseded || projectPaused(deps.db, projectId) ? 0 : 1, projectId)
|
|
14
18
|
.filter((row) => keys.some((key) => row.work_key === key ||
|
|
15
19
|
row.logical_key === key ||
|
|
16
20
|
(key.endsWith(":future") && String(row.work_key).startsWith(key.slice(0, -6)))));
|
|
@@ -18,7 +22,7 @@ export function activeConflict(deps, projectId, keys) {
|
|
|
18
22
|
return undefined;
|
|
19
23
|
return rows.some((row) => Number(row.live) === 1) ? "running" : "accepted-job";
|
|
20
24
|
}
|
|
21
|
-
export const acceptedJobMessage = "An accepted provider job for this section is waiting to be collected. Use
|
|
25
|
+
export const acceptedJobMessage = "An accepted provider job for this section is waiting to be collected. Use Try again or Continue the run, then rerun.";
|
|
22
26
|
export function conflictRefusal(conflict) {
|
|
23
27
|
return conflict === "running"
|
|
24
28
|
? { ok: false, reason: "running" }
|
|
@@ -6,6 +6,9 @@ export const recoveryActionSchema = z.discriminatedUnion("kind", [
|
|
|
6
6
|
z.object({ kind: z.literal("resume") }).strict(),
|
|
7
7
|
z.object({ kind: z.literal("retry"), stage: z.enum(stageKinds) }).strict(),
|
|
8
8
|
z.object({ kind: z.literal("rerun"), stage: z.enum(stageKinds) }).strict(),
|
|
9
|
+
// Make one reviewed item again (`slices/reviews`): an image, the thumbnail, the article, the
|
|
10
|
+
// narration or one short, and everything that depends on it, its review included.
|
|
11
|
+
z.object({ kind: z.literal("redo"), item: z.string().min(1).max(200) }).strict(),
|
|
9
12
|
]);
|
|
10
13
|
export const recoveryRequestSchema = revisionControlSchema
|
|
11
14
|
.extend({
|
|
@@ -9,19 +9,45 @@ export function sectionRoots(view, plan, stage) {
|
|
|
9
9
|
return false;
|
|
10
10
|
if (stage === "video")
|
|
11
11
|
return (row.input.kind === "local" &&
|
|
12
|
-
(row.key.startsWith("export:") ||
|
|
12
|
+
(row.key.startsWith("export:") ||
|
|
13
|
+
row.key === "subtitles:files" ||
|
|
14
|
+
row.key === "voices:files"));
|
|
13
15
|
if (stage === "audio")
|
|
14
16
|
return (row.input.kind === "tts" ||
|
|
15
17
|
(row.input.kind === "deferred" &&
|
|
16
18
|
["body-narration", "intro-narration", "outro-narration"].includes(row.input.operation)));
|
|
17
19
|
if (stage === "article")
|
|
18
|
-
return row.key === "article:body" && row.kind === "provider";
|
|
20
|
+
return ((row.key === "article:body" || row.key === "script:attribute") && row.kind === "provider");
|
|
19
21
|
if (stage === "document")
|
|
20
22
|
return row.key === "document:pdf";
|
|
21
23
|
return row.kind === "provider";
|
|
22
24
|
})
|
|
23
25
|
.map((row) => row.key);
|
|
24
26
|
}
|
|
27
|
+
// What making one reviewed item again regenerates and where its rebuild starts. The article
|
|
28
|
+
// and the narration are redone like More → make it again in its section on their stage; an image or the thumbnail
|
|
29
|
+
// by its own token, like Regenerate; a short by "Make this short again"'s token.
|
|
30
|
+
export function redoTarget(view, plan, item) {
|
|
31
|
+
const section = (stage) => {
|
|
32
|
+
const edit = regenerationEdit(view, plan, stage);
|
|
33
|
+
return edit === undefined ? undefined : { roots: sectionRoots(view, plan, stage), edit };
|
|
34
|
+
};
|
|
35
|
+
if (item === "narration")
|
|
36
|
+
return section("audio");
|
|
37
|
+
if (item === "article:body")
|
|
38
|
+
return section("article");
|
|
39
|
+
const own = (roots) => ({
|
|
40
|
+
roots,
|
|
41
|
+
edit: { config: view.revision.config, content: view.revision.content, regenerate: [item] },
|
|
42
|
+
});
|
|
43
|
+
if (/^shorts:\d+$/.test(item))
|
|
44
|
+
return plan.recipes.some((row) => row.key === `${item}:prompts`)
|
|
45
|
+
? own([`${item}:prompts`])
|
|
46
|
+
: undefined;
|
|
47
|
+
return plan.recipes.some((row) => row.key === item && row.kind === "provider")
|
|
48
|
+
? own([item])
|
|
49
|
+
: undefined;
|
|
50
|
+
}
|
|
25
51
|
export function dependentClosure(recipes, roots) {
|
|
26
52
|
const selected = new Set(roots);
|
|
27
53
|
let changed = true;
|