@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
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { stageKinds } from "../../kernel/pipeline.js";
|
|
2
|
+
import { imageScaleProblem } from "../images/scale.js";
|
|
3
|
+
import { loudnessFields } from "../loudness/model.js";
|
|
4
|
+
import { pauseFields } from "../narration/pauses-model.js";
|
|
5
|
+
import { reviewFields } from "../reviews/rules.js";
|
|
2
6
|
import { shortsSettingsProblems } from "../shorts/model.js";
|
|
7
|
+
import { ambientBedProblems, usesAmbientBed } from "../video/ambient-bed.js";
|
|
3
8
|
import { usesAnimation, videoEditProblems } from "../video/edit-settings.js";
|
|
9
|
+
import { usesVoices, voicesProblems } from "../voices/model.js";
|
|
4
10
|
import { sourceOf } from "./model.js";
|
|
11
|
+
import { shortModeFields } from "./short-mode.js";
|
|
5
12
|
export const titleMax = 200;
|
|
6
13
|
export const valueMax = 200;
|
|
7
14
|
export const numberPerPromptMax = 20;
|
|
@@ -84,7 +91,8 @@ export function admit(input) {
|
|
|
84
91
|
draft.outro?.mode === "llm" ||
|
|
85
92
|
usesNarrationPreparation(draft) ||
|
|
86
93
|
usesYoutubeDescription(draft) ||
|
|
87
|
-
usesShorts(draft)
|
|
94
|
+
usesShorts(draft) ||
|
|
95
|
+
(usesVoices(draft) && draft.voices?.source === "attribute");
|
|
88
96
|
if (needsLlm && !chosen(draft.llm)) {
|
|
89
97
|
fields.push({ field: "llm", message: "Choose a text (LLM) provider and model." });
|
|
90
98
|
}
|
|
@@ -93,6 +101,7 @@ export function admit(input) {
|
|
|
93
101
|
}
|
|
94
102
|
const voiced = draft.audio;
|
|
95
103
|
fields.push(...narrationPreparationFields(draft));
|
|
104
|
+
fields.push(...voicesFields(draft));
|
|
96
105
|
if (sources.audio === "generate") {
|
|
97
106
|
if (voiced === undefined || !chosen(voiced)) {
|
|
98
107
|
fields.push({ field: "audio", message: "Choose a narration provider and model." });
|
|
@@ -124,8 +133,13 @@ export function admit(input) {
|
|
|
124
133
|
}
|
|
125
134
|
fields.push(...youtubeDescriptionFields(draft));
|
|
126
135
|
fields.push(...shortsFields(draft));
|
|
136
|
+
fields.push(...shortModeFields(draft));
|
|
127
137
|
fields.push(...videoEditFields(draft));
|
|
138
|
+
fields.push(...ambientBedFields(draft, input.staged));
|
|
139
|
+
fields.push(...loudnessFields(draft.loudness));
|
|
140
|
+
fields.push(...pauseFields(draft));
|
|
128
141
|
fields.push(...referenceFields(draft));
|
|
142
|
+
fields.push(...reviewFields(draft));
|
|
129
143
|
if (usesReference(draft) && draft.reference?.source === "provide")
|
|
130
144
|
checkFile(input.staged, draft.provided.reference, "images", "provided.reference", "Upload the establishing image, or set Establishing image to Off in the Images section.", fields);
|
|
131
145
|
checkProvided(draft, input.staged, fields);
|
|
@@ -210,6 +224,9 @@ function checkImagePrompts(draft, fields) {
|
|
|
210
224
|
message: `A video can have at most ${imagesPerRunMax} images; these prompts ask for ${total}. Lower the numbers.`,
|
|
211
225
|
});
|
|
212
226
|
}
|
|
227
|
+
const scaleProblem = draft.imageScale === undefined ? undefined : imageScaleProblem(draft.imageScale);
|
|
228
|
+
if (scaleProblem !== undefined)
|
|
229
|
+
fields.push({ field: "imageScale", message: scaleProblem });
|
|
213
230
|
}
|
|
214
231
|
function checkProvided(draft, staged, fields) {
|
|
215
232
|
const { sources, provided } = draft;
|
|
@@ -320,6 +337,21 @@ export function narrationPreparationFields(draft) {
|
|
|
320
337
|
]
|
|
321
338
|
: [];
|
|
322
339
|
}
|
|
340
|
+
// Multiple voices, checked only while narration is generated: a format left set with
|
|
341
|
+
// narration Off or uploaded asks for nothing.
|
|
342
|
+
export function voicesFields(draft) {
|
|
343
|
+
if (draft.voices === undefined || !usesVoices(draft))
|
|
344
|
+
return [];
|
|
345
|
+
const fields = [...voicesProblems(draft.voices)];
|
|
346
|
+
// Preparation adds delivery tags to the turns of speakers on Inworld TTS-2, so it needs one.
|
|
347
|
+
if (!blank(draft.narrationPrompt) &&
|
|
348
|
+
!draft.voices.speakers.some((speaker) => speaker.voice.provider === "inworld" && speaker.voice.model === "inworld-tts-2"))
|
|
349
|
+
fields.push({
|
|
350
|
+
field: "narrationPrompt",
|
|
351
|
+
message: "Narration preparation adds delivery cues only to speakers on Inworld's TTS-2 model, and no speaker uses it. Choose that model for a speaker under Speakers, or clear Narration Preparation in Audio → Advanced.",
|
|
352
|
+
});
|
|
353
|
+
return fields;
|
|
354
|
+
}
|
|
323
355
|
// The YouTube description is written from the narration's word timings, so it needs
|
|
324
356
|
// narration; it runs whether the video renders or only the WAV is exported.
|
|
325
357
|
export function usesYoutubeDescription(draft) {
|
|
@@ -394,9 +426,55 @@ export function videoEditFields(draft) {
|
|
|
394
426
|
message: problem.message,
|
|
395
427
|
}));
|
|
396
428
|
}
|
|
429
|
+
// The ambient bed, checked only while the long video renders with narration; its own file only
|
|
430
|
+
// while the source is "upload". Named by where the control sits: Play's Export rail.
|
|
431
|
+
export function ambientBedFields(draft, staged) {
|
|
432
|
+
const bed = draft.ambientBed;
|
|
433
|
+
if (bed === undefined || !usesAmbientBed(draft))
|
|
434
|
+
return [];
|
|
435
|
+
const where = "under Outputs → Export → Ambient sound";
|
|
436
|
+
const fields = ambientBedProblems(bed).map((problem) => ({
|
|
437
|
+
field: `ambientBed.${problem.field}`,
|
|
438
|
+
message: `${problem.message} Change it ${where}.`,
|
|
439
|
+
}));
|
|
440
|
+
if (bed.source !== "upload")
|
|
441
|
+
return fields;
|
|
442
|
+
const id = draft.provided.ambientBed;
|
|
443
|
+
const file = id === undefined ? undefined : staged.find((candidate) => candidate.id === id);
|
|
444
|
+
if (file === undefined || file.stageKind !== "audio")
|
|
445
|
+
fields.push({
|
|
446
|
+
field: "ambientBed.file",
|
|
447
|
+
message: `The ambient sound's audio file is missing, so it can't be added to the project. Choose the file ${where}, or pick Rain, Fireplace or Wind instead.`,
|
|
448
|
+
});
|
|
449
|
+
else if (file.state !== "staged")
|
|
450
|
+
fields.push({
|
|
451
|
+
field: "ambientBed.file",
|
|
452
|
+
message: `The ambient sound's audio file is still uploading, so the run can't start yet. Wait for it to finish ${where}.`,
|
|
453
|
+
});
|
|
454
|
+
return fields;
|
|
455
|
+
}
|
|
397
456
|
function chosen(choice) {
|
|
398
457
|
return choice !== undefined && choice.provider.trim() !== "" && choice.model.trim() !== "";
|
|
399
458
|
}
|
|
459
|
+
// The aliases a run narrates with: its copied Library → Aliases while Use narration aliases is
|
|
460
|
+
// on for generated audio, otherwise none. Any voice provider: an alias is plain text.
|
|
461
|
+
export function narrationAliasesOf(draft) {
|
|
462
|
+
return draft.sources.audio === "generate" && draft.audio?.useNarrationAliases === true
|
|
463
|
+
? (draft.narrationAliases ?? [])
|
|
464
|
+
: [];
|
|
465
|
+
}
|
|
466
|
+
// Describe tables and figures in the narration: generated narration, the setting on, and a
|
|
467
|
+
// text model to write the descriptions. Without a text model the narration flattens the
|
|
468
|
+
// article as it always did, so the default being on never asks for one.
|
|
469
|
+
export function usesDescribedNarration(draft) {
|
|
470
|
+
return (draft.sources.audio === "generate" && draft.audio?.describeFigures === true && chosen(draft.llm));
|
|
471
|
+
}
|
|
472
|
+
// "Show tables and figures on screen": a video, and the narration describing its blocks.
|
|
473
|
+
export function usesFigureCards(draft) {
|
|
474
|
+
return (usesDescribedNarration(draft) &&
|
|
475
|
+
draft.sources.video === "generate" &&
|
|
476
|
+
draft.showFigures === true);
|
|
477
|
+
}
|
|
400
478
|
export function usesPronunciationGlossary(draft) {
|
|
401
479
|
return (draft.sources.audio === "generate" &&
|
|
402
480
|
draft.audio?.usePronunciationGlossary === true &&
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { languageSchema } from "../../kernel/ports/languages.js";
|
|
2
3
|
import { thinkingModes } from "../../kernel/ports/llm.js";
|
|
4
|
+
import { castSnapshotSchema } from "../channels/schema.js";
|
|
3
5
|
import { checkpointStageSchema } from "../checkpoints/schema.js";
|
|
4
6
|
import { documentSettingsSchema } from "../document/theme-schema.js";
|
|
7
|
+
import { narrationAliasesSchema } from "../narration/aliases-schema.js";
|
|
5
8
|
import { chunkModes } from "../narration/chunk.js";
|
|
9
|
+
import { reviewModes, reviewStages } from "../reviews/model.js";
|
|
6
10
|
import { subtitleConfigSchema } from "../subtitles/model.js";
|
|
11
|
+
import { ambientBedSchema } from "../video/ambient-bed-schema.js";
|
|
7
12
|
import { animateModes, atmospheres, colorGrades, cutModes, lookLevels, transitionKinds, } from "../video/edit-settings.js";
|
|
8
|
-
import {
|
|
13
|
+
import { voicesSettingsSchema } from "../voices/model.js";
|
|
14
|
+
import { entryModes, formats, motionStyles, referenceSources, runModes, stageSources, } from "./model.js";
|
|
9
15
|
import { defaultEdgeSilenceSeconds, defaultImageSeconds, defaultMotionStyle, defaultZoomPercent, } from "./rules.js";
|
|
16
|
+
const hexColour = z.string().regex(/^#[0-9A-Fa-f]{6}$/);
|
|
10
17
|
const providerChoice = z.object({
|
|
11
18
|
provider: z.string(),
|
|
12
19
|
model: z.string(),
|
|
@@ -29,6 +36,8 @@ export const videoEditSchema = z.object({
|
|
|
29
36
|
// The shape Play posts and the shape `projects.config` holds, in one place: the second
|
|
30
37
|
// is the first plus the rendered prompt texts.
|
|
31
38
|
export const runDraftSchema = z.object({
|
|
39
|
+
// Absent is the long video.
|
|
40
|
+
mode: z.enum(runModes).optional(),
|
|
32
41
|
checkpoints: z
|
|
33
42
|
.array(checkpointStageSchema)
|
|
34
43
|
.max(3)
|
|
@@ -55,8 +64,12 @@ export const runDraftSchema = z.object({
|
|
|
55
64
|
voice: z.string(),
|
|
56
65
|
usePronunciationGlossary: z.boolean().optional(),
|
|
57
66
|
shareGlossary: z.boolean().optional(),
|
|
67
|
+
useNarrationAliases: z.boolean().optional(),
|
|
68
|
+
describeFigures: z.boolean().optional(),
|
|
69
|
+
skipCode: z.boolean().optional(),
|
|
58
70
|
})
|
|
59
71
|
.optional(),
|
|
72
|
+
narrationAliases: narrationAliasesSchema.optional(),
|
|
60
73
|
sharedGlossary: z
|
|
61
74
|
.array(z.object({
|
|
62
75
|
term: z.string().min(1).max(200),
|
|
@@ -76,7 +89,10 @@ export const runDraftSchema = z.object({
|
|
|
76
89
|
articlePrompt: z.string().optional(),
|
|
77
90
|
narrationPrompt: z.string().optional(),
|
|
78
91
|
imagePrompts: z.array(z.object({ name: z.string(), number: z.number() })),
|
|
92
|
+
// The ranges are `slices/images/scale.ts`'s, checked by admission, not the schema's.
|
|
93
|
+
imageScale: z.object({ perHour: z.number(), words: z.number() }).optional(),
|
|
79
94
|
thumbnailPrompt: z.string().optional(),
|
|
95
|
+
thumbnailCount: z.union([z.literal(1), z.literal(3)]).optional(),
|
|
80
96
|
intro: entryChoice.optional(),
|
|
81
97
|
outro: entryChoice.optional(),
|
|
82
98
|
values: z.record(z.string(), z.string()),
|
|
@@ -89,6 +105,8 @@ export const runDraftSchema = z.object({
|
|
|
89
105
|
reference: z.string().optional(),
|
|
90
106
|
// The staged file of the shorts' background music; used only while Shorts is on.
|
|
91
107
|
shortsMusic: z.string().optional(),
|
|
108
|
+
// The staged file of the ambient bed; used only while its source is "upload".
|
|
109
|
+
ambientBed: z.string().optional(),
|
|
92
110
|
}),
|
|
93
111
|
// Optional until Play carries the control; unknown keys are stripped by this schema, so
|
|
94
112
|
// a mode not listed here would never reach the audio stage.
|
|
@@ -109,6 +127,7 @@ export const runDraftSchema = z.object({
|
|
|
109
127
|
subtitles: subtitleConfigSchema.optional(),
|
|
110
128
|
document: documentSettingsSchema.optional(),
|
|
111
129
|
youtubeDescription: z.boolean().optional(),
|
|
130
|
+
showFigures: z.boolean().optional(),
|
|
112
131
|
descriptionPrompt: z.string().optional(),
|
|
113
132
|
// The ranges are `slices/shorts/model.ts`'s, checked by admission, not the schema's.
|
|
114
133
|
shorts: z
|
|
@@ -127,6 +146,44 @@ export const runDraftSchema = z.object({
|
|
|
127
146
|
.optional(),
|
|
128
147
|
// The ranges are `slices/video/edit-settings.ts`'s, checked by admission, not the schema's.
|
|
129
148
|
videoEdit: videoEditSchema.optional(),
|
|
149
|
+
channelId: z.string().optional(),
|
|
150
|
+
useBrandKit: z.literal(false).optional(),
|
|
151
|
+
cast: castSnapshotSchema.optional(),
|
|
152
|
+
earlierEpisodes: z
|
|
153
|
+
.array(z.object({ title: z.string(), summary: z.string() }))
|
|
154
|
+
.readonly()
|
|
155
|
+
.optional(),
|
|
156
|
+
titleStyle: z
|
|
157
|
+
.object({
|
|
158
|
+
fontId: z
|
|
159
|
+
.string()
|
|
160
|
+
.max(160)
|
|
161
|
+
.regex(/^[A-Za-z0-9_-]+$/)
|
|
162
|
+
.optional(),
|
|
163
|
+
color: hexColour.optional(),
|
|
164
|
+
})
|
|
165
|
+
.optional(),
|
|
166
|
+
endScreen: z.object({ text: z.string().max(200) }).optional(),
|
|
167
|
+
// The retry range is `slices/reviews/model.ts`'s, checked by admission, not the schema's.
|
|
168
|
+
reviews: z
|
|
169
|
+
.object({
|
|
170
|
+
provider: z.string(),
|
|
171
|
+
model: z.string(),
|
|
172
|
+
thinking: z.enum(thinkingModes).optional(),
|
|
173
|
+
retries: z.number().optional(),
|
|
174
|
+
stages: z.partialRecord(z.enum(reviewStages), z.object({ mode: z.enum(reviewModes), prompt: z.string().optional() })),
|
|
175
|
+
})
|
|
176
|
+
.optional(),
|
|
177
|
+
// The rules are `slices/voices/model.ts`'s, checked by admission, not the schema's.
|
|
178
|
+
voices: voicesSettingsSchema.optional(),
|
|
179
|
+
ambientBed: ambientBedSchema.optional(),
|
|
180
|
+
// Level the volume (`loudness/model.ts`); the range is checked by admission.
|
|
181
|
+
loudness: z.object({ videoLufs: z.number(), audioFilesLufs: z.number() }).strict().optional(),
|
|
182
|
+
// Pauses between sentences and paragraphs (`narration/pauses-model.ts`); checked by admission.
|
|
183
|
+
sentencePauseSeconds: z.number().optional(),
|
|
184
|
+
paragraphPauseSeconds: z.number().optional(),
|
|
185
|
+
// Absent is English (`kernel/ports/languages.ts`).
|
|
186
|
+
language: languageSchema.optional(),
|
|
130
187
|
});
|
|
131
188
|
export const runConfigSchema = runDraftSchema.extend({
|
|
132
189
|
rendered: z.record(z.string(), z.string()),
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { sourceOf } from "./model.js";
|
|
2
|
+
// The short-only run mode: the same stages as a long video, narrowed to one vertical clip of
|
|
3
|
+
// about a minute. The article is written for a short, the narration and images are made as
|
|
4
|
+
// usual, and the Video stage renders the whole narration through the Shorts renderer
|
|
5
|
+
// (`shorts/render.ts`) with its word-by-word captions instead of the slideshow. A short has
|
|
6
|
+
// no thumbnail, PDF or shorts of its own.
|
|
7
|
+
export function usesShortMode(draft) {
|
|
8
|
+
return draft.mode === "short";
|
|
9
|
+
}
|
|
10
|
+
// Said where each control sits, so a draft or a Save that breaks the mode names the fix.
|
|
11
|
+
export function shortModeFields(draft) {
|
|
12
|
+
if (!usesShortMode(draft))
|
|
13
|
+
return [];
|
|
14
|
+
const fields = [];
|
|
15
|
+
if (draft.format !== "9:16")
|
|
16
|
+
fields.push({
|
|
17
|
+
field: "format",
|
|
18
|
+
message: "A short is vertical. Choose the 9:16 format, or make a long video instead.",
|
|
19
|
+
});
|
|
20
|
+
if (sourceOf(draft.sources, "video") !== "generate")
|
|
21
|
+
fields.push({
|
|
22
|
+
field: "sources.video",
|
|
23
|
+
message: "A short is a video. Set Video to Generate.",
|
|
24
|
+
});
|
|
25
|
+
if (sourceOf(draft.sources, "audio") === "off")
|
|
26
|
+
fields.push({
|
|
27
|
+
field: "sources.audio",
|
|
28
|
+
message: "A short's length and captions come from its narration. Set Narration to Generate or Provide.",
|
|
29
|
+
});
|
|
30
|
+
if (sourceOf(draft.sources, "images") === "off")
|
|
31
|
+
fields.push({
|
|
32
|
+
field: "sources.images",
|
|
33
|
+
message: "A short shows images behind its captions. Set Images to Generate or Provide.",
|
|
34
|
+
});
|
|
35
|
+
for (const kind of ["thumbnail", "document"])
|
|
36
|
+
if (sourceOf(draft.sources, kind) !== "off")
|
|
37
|
+
fields.push({
|
|
38
|
+
field: `sources.${kind}`,
|
|
39
|
+
message: `A short has no ${kind === "thumbnail" ? "thumbnail" : "PDF"}. Set ${kind === "thumbnail" ? "Thumbnail" : "Document"} to Off.`,
|
|
40
|
+
});
|
|
41
|
+
if (draft.shorts?.enabled === true)
|
|
42
|
+
fields.push({
|
|
43
|
+
field: "shorts.enabled",
|
|
44
|
+
message: "A short isn't cut into more shorts. Turn Shorts off.",
|
|
45
|
+
});
|
|
46
|
+
return fields;
|
|
47
|
+
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { transact } from "../../kernel/db/tx.js";
|
|
2
2
|
import { stageKinds } from "../../kernel/pipeline.js";
|
|
3
3
|
import { storeArticleText } from "../article/store.js";
|
|
4
|
+
import { resolveChannelId, setProjectChannel } from "../channels/repo.js";
|
|
4
5
|
import { defaultDocumentTheme } from "../document/model.js";
|
|
6
|
+
import { earlierEpisodesFor } from "../episodes/related.js";
|
|
7
|
+
import { listNarrationAliases } from "../narration/aliases-library.js";
|
|
5
8
|
import { collectSharedGlossary } from "../narration/shared-glossary.js";
|
|
6
9
|
import { admitInitialRevision } from "../rebuild/runtime-admission.js";
|
|
7
10
|
import { adoptBaseline } from "../revisions/adopt.js";
|
|
@@ -10,6 +13,7 @@ import { discardPreparedAssets } from "../storage/assets.js";
|
|
|
10
13
|
import { prepareStagedFile } from "../storage/prepare.js";
|
|
11
14
|
import { attachStagedFile, storeText } from "../storage/staging.js";
|
|
12
15
|
import { releaseStagedFile } from "../storage/staging-refs.js";
|
|
16
|
+
import { usesAmbientBed } from "../video/ambient-bed.js";
|
|
13
17
|
import { sourceOf, } from "./model.js";
|
|
14
18
|
import { insertProject, insertStage } from "./repo.js";
|
|
15
19
|
import { usesPronunciationGlossary, usesReference } from "./rules.js";
|
|
@@ -30,11 +34,19 @@ export function startRun(deps, draft, rendered, retainStaged = false, templates)
|
|
|
30
34
|
const shared = usesPronunciationGlossary(draft) && draft.audio?.shareGlossary === true
|
|
31
35
|
? collectSharedGlossary(deps).entries
|
|
32
36
|
: [];
|
|
37
|
+
// Library → Aliases as they are now: later edits there never change this project.
|
|
38
|
+
const aliases = draft.sources.audio === "generate" && draft.audio?.useNarrationAliases === true
|
|
39
|
+
? listNarrationAliases(deps.db)
|
|
40
|
+
: [];
|
|
33
41
|
// The title may name keywords too ("D&D Lore: {{Topic}}"), filled like a prompt's, so a
|
|
34
42
|
// template or Play keeps the pattern and each project gets its own title.
|
|
35
43
|
const title = render(draft.title, draft.values).trim() || draft.title;
|
|
44
|
+
// The channel's related earlier episodes as they are now (`slices/episodes`): later edits
|
|
45
|
+
// there never change this project. A draft never brings its own.
|
|
46
|
+
const { earlierEpisodes: _carried, ...fresh } = draft;
|
|
47
|
+
const earlier = earlierEpisodesFor(deps.db, draft, title);
|
|
36
48
|
const config = {
|
|
37
|
-
...
|
|
49
|
+
...fresh,
|
|
38
50
|
title,
|
|
39
51
|
// A new project always names its document theme: a config without one reads as the
|
|
40
52
|
// DiceMaster of older projects (see documentThemeOf), which no new project should get.
|
|
@@ -43,6 +55,8 @@ export function startRun(deps, draft, rendered, retainStaged = false, templates)
|
|
|
43
55
|
: {}),
|
|
44
56
|
rendered,
|
|
45
57
|
...(shared.length === 0 ? {} : { sharedGlossary: shared }),
|
|
58
|
+
...(aliases.length === 0 ? {} : { narrationAliases: aliases }),
|
|
59
|
+
...(earlier === undefined ? {} : { earlierEpisodes: earlier }),
|
|
46
60
|
};
|
|
47
61
|
const project = {
|
|
48
62
|
id,
|
|
@@ -72,18 +86,25 @@ export function startRun(deps, draft, rendered, retainStaged = false, templates)
|
|
|
72
86
|
// as the form left them and the same Play can simply be pressed again.
|
|
73
87
|
const moved = [];
|
|
74
88
|
let music;
|
|
89
|
+
let bed;
|
|
75
90
|
try {
|
|
76
91
|
transact(deps.db, () => {
|
|
77
92
|
insertProject(deps.db, project);
|
|
93
|
+
setProjectChannel(deps.db, id, resolveChannelId(deps.db, draft.channelId));
|
|
78
94
|
for (const stage of stages) {
|
|
79
95
|
insertStage(deps.db, stage);
|
|
80
96
|
}
|
|
81
97
|
attachProvided(deps, id, draft, moved, retainStaged);
|
|
82
98
|
music = attachShortsMusic(deps, id, draft, moved);
|
|
99
|
+
bed = attachAmbientBed(deps, id, draft, moved);
|
|
83
100
|
// The music is named by the first revision, as Edit project → Shorts names it, so the
|
|
84
101
|
// project gets its revision now even where it would otherwise wait for its first view.
|
|
85
|
-
|
|
86
|
-
|
|
102
|
+
// The ambient bed's own file is named the same way.
|
|
103
|
+
if (deps.catalogue !== undefined || music !== undefined || bed !== undefined) {
|
|
104
|
+
const baseline = adoptBaseline(deps, id, templates, {
|
|
105
|
+
...(music === undefined ? {} : { shortsMusic: music.id }),
|
|
106
|
+
...(bed === undefined ? {} : { ambientBed: bed.id }),
|
|
107
|
+
});
|
|
87
108
|
if (!baseline.ok)
|
|
88
109
|
throw new Error("The new project has no revision.");
|
|
89
110
|
if (deps.catalogue !== undefined)
|
|
@@ -93,8 +114,9 @@ export function startRun(deps, draft, rendered, retainStaged = false, templates)
|
|
|
93
114
|
}
|
|
94
115
|
catch (error) {
|
|
95
116
|
// The copy was written outside the transaction; nothing names it once it rolled back.
|
|
96
|
-
|
|
97
|
-
|
|
117
|
+
const written = [music, bed].flatMap((asset) => (asset === undefined ? [] : [asset]));
|
|
118
|
+
if (written.length > 0)
|
|
119
|
+
discardPreparedAssets(deps, written);
|
|
98
120
|
throw error;
|
|
99
121
|
}
|
|
100
122
|
for (const source of retainStaged ? [] : moved) {
|
|
@@ -148,6 +170,20 @@ function attachShortsMusic(deps, projectId, draft, collected) {
|
|
|
148
170
|
collected.push(stagedFileId);
|
|
149
171
|
return result.asset;
|
|
150
172
|
}
|
|
173
|
+
// The ambient bed's own file, copied into the project as an asset of its own like the shorts'
|
|
174
|
+
// music. Only while the long video gets the bed and its source is "upload".
|
|
175
|
+
function attachAmbientBed(deps, projectId, draft, collected) {
|
|
176
|
+
const stagedFileId = draft.provided.ambientBed;
|
|
177
|
+
if (draft.ambientBed?.source !== "upload" || !usesAmbientBed(draft) || stagedFileId === undefined)
|
|
178
|
+
return undefined;
|
|
179
|
+
const result = prepareStagedFile(deps, { projectId, stagedFileId, role: "audio_body" });
|
|
180
|
+
if (!result.ok)
|
|
181
|
+
// admit() already refused a missing or still-copying file, as for the stages' own files.
|
|
182
|
+
throw new Error(`the ambient sound's audio file could not be attached: ${result.reason}`);
|
|
183
|
+
insertAsset(deps.db, result.asset);
|
|
184
|
+
collected.push(stagedFileId);
|
|
185
|
+
return result.asset;
|
|
186
|
+
}
|
|
151
187
|
function attach(deps, projectId, kind, stagedFileId, role, collected, retainStaged, index) {
|
|
152
188
|
if (stagedFileId === undefined) {
|
|
153
189
|
throw new Error(`the ${kind} stage is provided but carries no staged file`);
|
|
@@ -82,7 +82,7 @@ function truncated(answer) {
|
|
|
82
82
|
// Empty response → failed attempt.
|
|
83
83
|
function written(answer) {
|
|
84
84
|
return answer.text.trim() === ""
|
|
85
|
-
? "The AI model returned an empty article.
|
|
85
|
+
? "The AI model returned an empty article. Try again; if it keeps happening, choose a different model in Edit project → Providers."
|
|
86
86
|
: undefined;
|
|
87
87
|
}
|
|
88
88
|
function finished(answer) {
|
|
@@ -91,6 +91,6 @@ function finished(answer) {
|
|
|
91
91
|
return empty;
|
|
92
92
|
}
|
|
93
93
|
return truncated(answer)
|
|
94
|
-
? `The article was still unfinished after ${String(continuationLimit)} continuations because the AI model kept stopping at its length limit. Ask for a shorter article in Edit project → Prompts, or choose a model with a larger output limit in Edit project → Providers, then
|
|
94
|
+
? `The article was still unfinished after ${String(continuationLimit)} continuations because the AI model kept stopping at its length limit. Ask for a shorter article in Edit project → Prompts, or choose a model with a larger output limit in Edit project → Providers, then Try again.`
|
|
95
95
|
: undefined;
|
|
96
96
|
}
|
|
@@ -20,11 +20,11 @@ export async function prepareProvidedArticleSegments(deps, audioContext, provide
|
|
|
20
20
|
audioContext.signal.throwIfAborted();
|
|
21
21
|
const article = stagesOf(deps.db, projectId).find((stage) => stage.kind === "article");
|
|
22
22
|
if (!article)
|
|
23
|
-
throw new Error("Slopify hit an internal error (the project has no article stage).
|
|
23
|
+
throw new Error("Slopify hit an internal error (the project has no article stage). Try again; if it happens again, use Download diagnostics in Settings and report it.");
|
|
24
24
|
const outputs = outputsOf(deps.db, projectId);
|
|
25
25
|
const plain = outputs.find((output) => output.role === "article_txt");
|
|
26
26
|
if (!plain)
|
|
27
|
-
throw new Error("The article text you provided is missing, so the intro and outro can't be written from it. Paste the article again in Edit project → Article, then
|
|
27
|
+
throw new Error("The article text you provided is missing, so the intro and outro can't be written from it. Paste the article again in Edit project → Article, then Try again.");
|
|
28
28
|
const narration = readFileSync(outputPath(deps.paths, projectId, plain.path), "utf8");
|
|
29
29
|
const saved = outputs.find((output) => output.stageKind === "article" && output.role === "instructions");
|
|
30
30
|
let instructions = saved
|
|
@@ -3,6 +3,7 @@ import { z } from "zod";
|
|
|
3
3
|
import { messageRoles } from "../../kernel/ports/llm.js";
|
|
4
4
|
import { insertPiece, piecesOf, setPiece } from "../../kernel/runner/piece-repo.js";
|
|
5
5
|
import { projectById } from "../admission/repo.js";
|
|
6
|
+
import { withEarlierEpisodes } from "../episodes/related.js";
|
|
6
7
|
import { outputPath } from "../storage/layout.js";
|
|
7
8
|
import { outputsOf } from "../storage/repo.js";
|
|
8
9
|
import { storeText } from "../storage/staging.js";
|
|
@@ -20,10 +21,13 @@ export async function runArticle(deps, context, providers) {
|
|
|
20
21
|
if (choice === undefined || articlePrompt === undefined) {
|
|
21
22
|
// Admission refuses a run whose article is Generate without both, so
|
|
22
23
|
// reaching here is a bug in admission rather than something the user did.
|
|
23
|
-
throw new Error("No AI model or article prompt is set for writing the article. Choose a model in Edit project → Providers and check Edit project → Prompts, then
|
|
24
|
+
throw new Error("No AI model or article prompt is set for writing the article. Choose a model in Edit project → Providers and check Edit project → Prompts, then Try again.");
|
|
24
25
|
}
|
|
25
26
|
const notes = researchNotes(deps, projectId);
|
|
26
|
-
const brief = {
|
|
27
|
+
const brief = {
|
|
28
|
+
articlePrompt: withEarlierEpisodes(articlePrompt, project.config.earlierEpisodes),
|
|
29
|
+
...(notes === undefined ? {} : { notes }),
|
|
30
|
+
};
|
|
27
31
|
const result = await articleBody(deps, context, providers, choice, brief);
|
|
28
32
|
if (!result.ok)
|
|
29
33
|
return "held";
|
|
@@ -7,12 +7,12 @@ export async function writeSegment(providers, choice, config, category, article,
|
|
|
7
7
|
return { ok: true, value: undefined };
|
|
8
8
|
const body = config.rendered[category];
|
|
9
9
|
if (body === undefined)
|
|
10
|
-
throw new Error(`Slopify hit an internal error (the ${category} text was never filled in).
|
|
10
|
+
throw new Error(`Slopify hit an internal error (the ${category} text was never filled in). Try again; if it happens again, use Download diagnostics in Settings and report it.`);
|
|
11
11
|
const common = { category, name: picked.name, mode: picked.mode };
|
|
12
12
|
if (picked.mode === "text")
|
|
13
13
|
return { ok: true, value: { ...common, text: body, tokens: noTokens } };
|
|
14
14
|
if (choice === undefined)
|
|
15
|
-
throw new Error(`No AI model is set for writing the ${category}. Choose one in Edit project → Providers, then
|
|
15
|
+
throw new Error(`No AI model is set for writing the ${category}. Choose one in Edit project → Providers, then Try again.`);
|
|
16
16
|
const messages = segmentMessages(body, config, article);
|
|
17
17
|
sent.push({ label: category === "intro" ? "Intro" : "Outro", messages });
|
|
18
18
|
const answer = await providers.llm({
|
|
@@ -21,7 +21,7 @@ export async function writeSegment(providers, choice, config, category, article,
|
|
|
21
21
|
...(choice.thinking === undefined ? {} : { thinking: choice.thinking }),
|
|
22
22
|
messages,
|
|
23
23
|
check: (given) => given.text.trim() === ""
|
|
24
|
-
? `The AI model returned an empty ${category}.
|
|
24
|
+
? `The AI model returned an empty ${category}. Try again; if it keeps happening, choose a different model in Edit project → Providers.`
|
|
25
25
|
: undefined,
|
|
26
26
|
});
|
|
27
27
|
if (!answer.ok)
|
|
@@ -23,13 +23,21 @@ export function folderProblem(paths, folder) {
|
|
|
23
23
|
return "This folder is one Slopify uses for its own temporary files. Pick another folder, or leave Folder empty to use the default.";
|
|
24
24
|
return undefined;
|
|
25
25
|
}
|
|
26
|
-
// Where the folder is on the user's computer. In Docker only the projects folder
|
|
27
|
-
// with the host, so a folder elsewhere lives in the
|
|
26
|
+
// Where the folder is on the user's computer. In Docker only the projects folder (and, from
|
|
27
|
+
// 3.0, the Backups folder) is shared with the host, so a folder elsewhere lives in the
|
|
28
|
+
// container's private volume.
|
|
28
29
|
export function hostFolder(paths, location, folder) {
|
|
29
30
|
if (!location.container)
|
|
30
31
|
return folder;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
const shared = [
|
|
33
|
+
[paths.projects, location.hostProjects],
|
|
34
|
+
[paths.backups, location.hostBackups],
|
|
35
|
+
];
|
|
36
|
+
for (const [inContainer, onHost] of shared.toSorted((a, b) => b[0].length - a[0].length)) {
|
|
37
|
+
if (!onHost || !within(inContainer, folder))
|
|
38
|
+
continue;
|
|
39
|
+
const inside = relative(inContainer, folder);
|
|
40
|
+
return inside === "" ? onHost : join(onHost, inside);
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
35
43
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { transact } from "../../kernel/db/tx.js";
|
|
3
3
|
import { derive } from "../../kernel/runner/graph.js";
|
|
4
|
-
import { projectPaused, stagesOf } from "../admission/repo.js";
|
|
4
|
+
import { liveProject, projectPaused, stagesOf } from "../admission/repo.js";
|
|
5
5
|
import { startRun } from "../admission/start.js";
|
|
6
6
|
import { deleteStagedFile } from "../storage/repo.js";
|
|
7
7
|
import { releaseStagedFile } from "../storage/staging-refs.js";
|
|
@@ -11,10 +11,13 @@ const queueRow = z.object({
|
|
|
11
11
|
position: z.number(),
|
|
12
12
|
state: z.enum(["queued", "active", "finished"]),
|
|
13
13
|
});
|
|
14
|
+
// A project in the trash leaves the queue's view, so the queue moves on past it; restored,
|
|
15
|
+
// it is back in its place.
|
|
14
16
|
export function queueEntries(db, batchId) {
|
|
15
17
|
return db
|
|
16
18
|
.prepare(`SELECT project_id AS projectId, batch_id AS batchId, position, state
|
|
17
19
|
FROM project_queue ${batchId === undefined ? "WHERE state != 'finished'" : "WHERE batch_id = ?"}
|
|
20
|
+
AND ${liveProject("project_queue", "project_id")}
|
|
18
21
|
ORDER BY position`)
|
|
19
22
|
.all(...(batchId === undefined ? [] : [batchId]))
|
|
20
23
|
.map((row) => queueRow.parse(row));
|
|
@@ -50,6 +53,8 @@ export function enqueueBatch(deps, batchId, runs, retainStaged = false) {
|
|
|
50
53
|
used.add(id);
|
|
51
54
|
if (draft.shorts?.enabled === true && draft.provided.shortsMusic)
|
|
52
55
|
used.add(draft.provided.shortsMusic);
|
|
56
|
+
if (draft.ambientBed?.source === "upload" && draft.provided.ambientBed)
|
|
57
|
+
used.add(draft.provided.ambientBed);
|
|
53
58
|
}
|
|
54
59
|
for (const id of retainStaged ? [] : used) {
|
|
55
60
|
sources.add(id);
|
|
@@ -68,7 +73,7 @@ export function pumpQueue(db, runner) {
|
|
|
68
73
|
const status = derive(stages, projectPaused(db, entry.projectId));
|
|
69
74
|
if (runner.hasInflight?.(entry.projectId))
|
|
70
75
|
return;
|
|
71
|
-
if (["done", "failed", "canceled"].includes(status)) {
|
|
76
|
+
if (["done", "partial", "failed", "canceled"].includes(status)) {
|
|
72
77
|
db.prepare("UPDATE project_queue SET state = 'finished' WHERE project_id = ?").run(entry.projectId);
|
|
73
78
|
continue;
|
|
74
79
|
}
|
|
@@ -53,7 +53,7 @@ async function cancel(deps, projectId) {
|
|
|
53
53
|
if (paused)
|
|
54
54
|
setProjectPaused(deps.db, projectId, false, at);
|
|
55
55
|
deps.db
|
|
56
|
-
.prepare("UPDATE revision_work SET state='canceled',dispatch_state='held',failure_reason
|
|
56
|
+
.prepare("UPDATE revision_work SET state='canceled',dispatch_state='held',failure_reason=?,retry_at=NULL WHERE project_id=? AND state!='done'")
|
|
57
57
|
.run(canceledByUser, projectId);
|
|
58
58
|
deps.db
|
|
59
59
|
.prepare("UPDATE revision_work_pieces SET state='held',dispatch_state='held' WHERE work_id IN (SELECT id FROM revision_work WHERE project_id=?) AND state!='done'")
|