@gentbajko/slopify 2.5.0 → 3.0.1
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 +24 -41
- package/SUBTITLES.md +1 -1
- package/dist/adapter-registry.js +31 -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 +85 -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/gemini.js +206 -0
- 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/pcm-mp3.js +62 -0
- package/dist/adapters/tts/system.js +147 -0
- package/dist/adapters/tts/voice-languages.js +80 -0
- package/dist/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/NotoSansArabic-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/NotoSansDevanagari-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/NotoSansHebrew-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/NotoSansThai-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/OFL.txt +93 -0
- package/dist/assets/fonts/noto/SOURCE.txt +18 -0
- package/dist/assets/models.yaml +38 -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/assets/style-preview/narration.mp3 +0 -0
- package/dist/assets/style-preview/tall-1.jpg +0 -0
- package/dist/assets/style-preview/tall-2.jpg +0 -0
- package/dist/assets/style-preview/tall-3.jpg +0 -0
- package/dist/assets/style-preview/wide-1.jpg +0 -0
- package/dist/assets/style-preview/wide-2.jpg +0 -0
- package/dist/assets/style-preview/wide-3.jpg +0 -0
- package/dist/catalog/merge.js +132 -0
- package/dist/catalog/registry.js +17 -14
- package/dist/catalog/schema.js +2 -0
- package/dist/catalog/store.js +147 -8
- package/dist/catalog/validate.js +2 -2
- package/dist/compose.yaml +71 -0
- package/dist/edge/autostart/docker-record.js +32 -0
- package/dist/edge/autostart/docker.js +118 -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 +72 -0
- package/dist/edge/autostart/service.js +90 -0
- package/dist/edge/cli-args.js +71 -0
- package/dist/edge/cli.js +64 -63
- 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 +160 -0
- package/dist/edge/docker-install/state.js +146 -0
- package/dist/edge/{docker-projects → docker-install}/volume.js +1 -5
- package/dist/edge/docker.js +14 -22
- package/dist/edge/events/hub.js +3 -1
- package/dist/edge/host-cli.js +1 -1
- package/dist/edge/http/actions.js +38 -11
- package/dist/edge/http/app.js +60 -2
- package/dist/edge/http/auditions.js +70 -0
- package/dist/edge/http/autostart.js +37 -0
- package/dist/edge/http/byte-range.js +42 -0
- package/dist/edge/http/channel-memory.js +92 -0
- package/dist/edge/http/channels.js +128 -0
- package/dist/edge/http/diagnostics.js +1 -0
- package/dist/edge/http/draft-files.js +2 -4
- package/dist/edge/http/entries.js +22 -3
- package/dist/edge/http/files.js +11 -7
- 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 +212 -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/project-templates.js +19 -2
- package/dist/edge/http/projects.js +37 -100
- package/dist/edge/http/prompts.js +40 -3
- package/dist/edge/http/pronunciations.js +23 -4
- package/dist/edge/http/providers.js +119 -3
- package/dist/edge/http/reviews.js +131 -0
- package/dist/edge/http/revision-files.js +3 -5
- package/dist/edge/http/revisions.js +1 -1
- package/dist/edge/http/run-cost.js +25 -0
- package/dist/edge/http/schedules.js +115 -6
- package/dist/edge/http/settings.js +84 -6
- package/dist/edge/http/storage-files.js +50 -0
- package/dist/edge/http/storage.js +43 -2
- package/dist/edge/http/studio.js +287 -0
- package/dist/edge/http/style-preview.js +61 -0
- package/dist/edge/http/subtitles.js +1 -1
- package/dist/edge/http/trash.js +69 -0
- package/dist/edge/http/tutorials.js +59 -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 +92 -0
- package/dist/edge/native-update.js +62 -0
- package/dist/extension/slopify-studio-chrome.zip +0 -0
- package/dist/extension/slopify-studio-firefox.zip +0 -0
- package/dist/host-cli/gemini-login.js +83 -0
- package/dist/host-cli/open-folder.js +10 -10
- package/dist/host-cli/runtime.js +9 -1
- package/dist/host-cli/status.js +4 -2
- 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/db/migrations/0041-cast-hosts.sql +3 -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 +10 -5
- package/dist/kernel/ports/narration-aliases.js +60 -0
- package/dist/kernel/ports/plan-limits.js +36 -0
- package/dist/kernel/ports/system-speech.js +203 -0
- package/dist/kernel/runner/attempt.js +17 -8
- package/dist/kernel/runner/audition.js +36 -0
- package/dist/kernel/runner/graph.js +40 -5
- package/dist/kernel/runner/index.js +39 -9
- 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 +277 -16
- package/dist/model-catalog.js +2 -0
- 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/3.0.1.md +105 -0
- package/dist/patch-notes/index.json +32 -0
- package/dist/slices/admission/model.js +27 -0
- package/dist/slices/admission/repo.js +24 -4
- package/dist/slices/admission/rules.js +80 -2
- package/dist/slices/admission/schema.js +58 -1
- package/dist/slices/admission/short-mode.js +47 -0
- package/dist/slices/admission/start.js +42 -6
- 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 +221 -0
- package/dist/slices/channels/runs.js +129 -0
- package/dist/slices/channels/schema.js +152 -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 +5 -2
- package/dist/slices/control/providers.js +4 -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 +68 -2
- package/dist/slices/eta/history.js +56 -0
- package/dist/slices/eta/model.js +63 -0
- package/dist/slices/eta/view.js +25 -0
- 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 +185 -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 +47 -3
- package/dist/slices/notifications/rules.js +48 -3
- package/dist/slices/onboarding/copy.js +143 -0
- package/dist/slices/onboarding/first-run.js +72 -0
- package/dist/slices/onboarding/full-video.js +81 -0
- package/dist/slices/onboarding/install.js +119 -0
- package/dist/slices/onboarding/model.js +85 -0
- package/dist/slices/onboarding/packs.js +491 -0
- package/dist/slices/onboarding/quick-short.js +144 -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 +127 -8
- package/dist/slices/play-drafts/readiness.js +4 -1
- 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 +59 -6
- package/dist/slices/project-templates/next-chapter.js +94 -0
- package/dist/slices/project-templates/one-off.js +26 -0
- package/dist/slices/project-templates/repo.js +2 -1
- package/dist/slices/project-templates/schema.js +7 -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 +67 -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 +125 -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 +267 -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 +62 -11
- package/dist/slices/rebuild/runtime-subtitles.js +72 -19
- package/dist/slices/rebuild/runtime-voices.js +103 -0
- package/dist/slices/rebuild/runtime-youtube.js +7 -6
- package/dist/slices/rebuild/service-readiness.js +3 -3
- 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 +118 -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 +256 -0
- package/dist/slices/run-cost/pricing.js +95 -0
- package/dist/slices/run-cost/week.js +108 -0
- package/dist/slices/schedules/agenda.js +222 -0
- package/dist/slices/schedules/repo.js +58 -18
- package/dist/slices/schedules/scheduler.js +68 -17
- package/dist/slices/schedules/schema.js +179 -1
- package/dist/slices/schedules/service.js +43 -4
- package/dist/slices/schedules/similar.js +128 -0
- package/dist/slices/schedules/topic-list.js +163 -0
- package/dist/slices/schedules/topics.js +523 -0
- package/dist/slices/settings/first-run.js +106 -0
- package/dist/slices/settings/health.js +247 -0
- package/dist/slices/settings/key-guides.js +138 -0
- package/dist/slices/settings/key-test.js +93 -0
- package/dist/slices/settings/keys.js +10 -5
- package/dist/slices/settings/model-reach.js +52 -0
- package/dist/slices/settings/model.js +55 -1
- package/dist/slices/settings/models.js +5 -1
- package/dist/slices/settings/playback.js +15 -0
- package/dist/slices/settings/readiness.js +14 -2
- package/dist/slices/settings/repo.js +17 -3
- package/dist/slices/settings/tutorial-schema.js +5 -0
- 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 +63 -13
- 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 +87 -0
- package/dist/slices/storage/downloads.js +33 -1
- 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 +94 -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 +30 -0
- package/dist/slices/studio/pack.js +211 -0
- package/dist/slices/studio/queue.js +69 -0
- package/dist/slices/studio/settings.js +170 -0
- package/dist/slices/style-preview/images.js +61 -0
- package/dist/slices/style-preview/narration.js +33 -0
- package/dist/slices/style-preview/render.js +148 -0
- package/dist/slices/style-preview/schema.js +59 -0
- package/dist/slices/style-preview/service.js +92 -0
- package/dist/slices/style-preview/settings.js +139 -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/tutorials/anchors.js +57 -0
- package/dist/slices/tutorials/library.js +185 -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 +87 -0
- package/dist/slices/voices/audition.js +25 -0
- package/dist/slices/voices/cast.js +74 -0
- package/dist/slices/voices/grouping.js +59 -0
- package/dist/slices/voices/join.js +35 -0
- package/dist/slices/voices/languages.js +25 -0
- package/dist/slices/voices/model.js +218 -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 +84 -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/tutorials/AI-CLIs.md +146 -0
- package/dist/tutorials/Backups.md +121 -0
- package/dist/tutorials/Calendar.md +103 -0
- package/dist/tutorials/Cast-Library.md +112 -0
- package/dist/tutorials/Channels.md +148 -0
- package/dist/tutorials/Costs-and-Run-Cost.md +136 -0
- package/dist/tutorials/Docker.md +171 -0
- package/dist/tutorials/Document-Themes.md +207 -0
- package/dist/tutorials/Editing-a-Project.md +204 -0
- package/dist/tutorials/FAQ.md +134 -0
- package/dist/tutorials/First-Launch-and-Welcome.md +153 -0
- package/dist/tutorials/Home-and-Projects.md +174 -0
- package/dist/tutorials/Home.md +119 -0
- package/dist/tutorials/Install.md +154 -0
- package/dist/tutorials/Intros-and-Outros.md +86 -0
- package/dist/tutorials/Keyboard-Shortcuts-and-Command-Palette.md +215 -0
- package/dist/tutorials/Library-Overview.md +75 -0
- package/dist/tutorials/Models.md +133 -0
- package/dist/tutorials/Multiple-Voices.md +161 -0
- package/dist/tutorials/Narration-Aliases-and-Glossary.md +129 -0
- package/dist/tutorials/Notifications.md +86 -0
- package/dist/tutorials/Other-Languages.md +110 -0
- package/dist/tutorials/PDF-Documents.md +100 -0
- package/dist/tutorials/Play-Images.md +135 -0
- package/dist/tutorials/Play-Narration.md +167 -0
- package/dist/tutorials/Play-Outputs.md +109 -0
- package/dist/tutorials/Play-Overview.md +216 -0
- package/dist/tutorials/Play-Title-and-Article.md +135 -0
- package/dist/tutorials/Play-Video-and-Style.md +143 -0
- package/dist/tutorials/Project-Page.md +228 -0
- package/dist/tutorials/Prompts.md +136 -0
- package/dist/tutorials/Providers-and-Keys.md +290 -0
- package/dist/tutorials/Publishing-to-YouTube.md +135 -0
- package/dist/tutorials/Recovery-and-Retries.md +141 -0
- package/dist/tutorials/Reviews-and-Checkpoints.md +143 -0
- package/dist/tutorials/Schedules.md +154 -0
- package/dist/tutorials/Settings-Reference.md +258 -0
- package/dist/tutorials/Shorts.md +116 -0
- package/dist/tutorials/Start-at-Login.md +126 -0
- package/dist/tutorials/Studio-Extension.md +146 -0
- package/dist/tutorials/Templates.md +146 -0
- package/dist/tutorials/Trash.md +87 -0
- package/dist/tutorials/Troubleshooting.md +149 -0
- package/dist/tutorials/Uninstalling-and-Moving.md +113 -0
- package/dist/tutorials/Updating-and-Patch-Notes.md +120 -0
- package/dist/tutorials/Video-Editing.md +135 -0
- package/dist/tutorials/Where-Your-Files-Live.md +149 -0
- package/dist/tutorials/YouTube-Description.md +132 -0
- package/dist/tutorials/Your-First-Short.md +125 -0
- package/dist/tutorials/_Footer.md +1 -0
- package/dist/tutorials/_Sidebar.md +69 -0
- package/dist/updater/service.js +66 -21
- package/dist/web/assets/index-DBLSfqA-.css +1 -0
- package/dist/web/assets/index-DFdIhmm0.js +307 -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-B9OY1ioX.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +5 -5
- 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,10 +1,12 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import { imageBlob } from "../channels/repo.js";
|
|
3
4
|
import { outputPath } from "../storage/layout.js";
|
|
4
5
|
// The provider call an image step makes: its request, the effort when one is chosen and, while
|
|
5
6
|
// the project's Establishing image is on, that image's bytes as the visual reference.
|
|
6
7
|
export function imageCall(deps, projectId, input, previewLabel) {
|
|
7
8
|
const reference = referenceImage(deps, projectId, input);
|
|
9
|
+
const cast = castImages(deps, input);
|
|
8
10
|
return {
|
|
9
11
|
provider: input.provider,
|
|
10
12
|
model: input.model,
|
|
@@ -12,6 +14,7 @@ export function imageCall(deps, projectId, input, previewLabel) {
|
|
|
12
14
|
aspect: input.aspect,
|
|
13
15
|
...(input.thinking === undefined ? {} : { thinking: input.thinking }),
|
|
14
16
|
...(reference === undefined ? {} : { reference }),
|
|
17
|
+
...(cast === undefined ? {} : { cast }),
|
|
15
18
|
...(previewLabel === undefined ? {} : { previewLabel }),
|
|
16
19
|
};
|
|
17
20
|
}
|
|
@@ -19,7 +22,7 @@ function referenceImage(deps, projectId, input) {
|
|
|
19
22
|
if (input.reference === undefined)
|
|
20
23
|
return undefined;
|
|
21
24
|
// The step waits for the establishing image, so a missing one went away since.
|
|
22
|
-
const missing = "The establishing image this image is drawn from is missing. Use Regenerate on the establishing image in the Images section (or upload it again in Edit project → Images), then use
|
|
25
|
+
const missing = "The establishing image this image is drawn from is missing. Use Regenerate on the establishing image in the Images section (or upload it again in Edit project → Images), then use Try again.";
|
|
23
26
|
if (input.reference.assetId === null)
|
|
24
27
|
throw new Error(missing);
|
|
25
28
|
const row = z
|
|
@@ -39,9 +42,25 @@ function referenceImage(deps, projectId, input) {
|
|
|
39
42
|
}
|
|
40
43
|
const mime = imageMime(bytes);
|
|
41
44
|
if (mime === undefined)
|
|
42
|
-
throw new Error("The establishing image is not a PNG or JPEG file, so it can't be sent as a reference. Upload a PNG or JPEG in Edit project → Images → Establishing image, then use
|
|
45
|
+
throw new Error("The establishing image is not a PNG or JPEG file, so it can't be sent as a reference. Upload a PNG or JPEG in Edit project → Images → Establishing image, then use Try again.");
|
|
43
46
|
return { bytes, mime };
|
|
44
47
|
}
|
|
48
|
+
// The cast pictures the request names, read by hash from the database. They are never removed
|
|
49
|
+
// while a project may name them, so a missing one came from a backup of another install.
|
|
50
|
+
function castImages(deps, input) {
|
|
51
|
+
if (input.cast === undefined)
|
|
52
|
+
return undefined;
|
|
53
|
+
return input.cast.map((member) => ({
|
|
54
|
+
name: member.name,
|
|
55
|
+
description: member.description,
|
|
56
|
+
images: member.images.map((sha256) => {
|
|
57
|
+
const blob = imageBlob(deps.db, sha256);
|
|
58
|
+
if (blob === undefined)
|
|
59
|
+
throw new Error(`A picture of ${member.name} that this project was started with is not in this Slopify's database, so it can't be sent as a reference. Add the picture again in Channels → Cast, then start the video again from Play.`);
|
|
60
|
+
return blob;
|
|
61
|
+
}),
|
|
62
|
+
}));
|
|
63
|
+
}
|
|
45
64
|
function imageMime(bytes) {
|
|
46
65
|
const png = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
|
|
47
66
|
if (png.every((value, index) => bytes[index] === value))
|
|
@@ -1,12 +1,18 @@
|
|
|
1
|
-
import { statSync } from "node:fs";
|
|
1
|
+
import { rmSync, statSync } from "node:fs";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { referenceKey } from "../admission/model.js";
|
|
5
5
|
import { plainText } from "../article/plain.js";
|
|
6
6
|
import { splitEndMatter } from "../article/split.js";
|
|
7
|
+
import { levelPieces } from "../loudness/level-pieces.js";
|
|
7
8
|
import { joinNarration } from "../narration/concat.js";
|
|
9
|
+
import { applyPauses, planPieces } from "../narration/pauses.js";
|
|
8
10
|
import { allocateAsset, discardPreparedAssets, sealAsset } from "../storage/assets.js";
|
|
9
11
|
import { outputPath } from "../storage/layout.js";
|
|
12
|
+
import { probeDurationMs, runFfmpeg } from "../video/ffmpeg.js";
|
|
13
|
+
import { turnJoinArgs } from "../voices/join.js";
|
|
14
|
+
import { levelOperation } from "./recipe-loudness.js";
|
|
15
|
+
import { executeFigureCard } from "./runtime-figure-card.js";
|
|
10
16
|
import { publishNarrationText } from "./runtime-narration-text.js";
|
|
11
17
|
import { executionPlan, executionView, savedCatalogue } from "./runtime-plan.js";
|
|
12
18
|
import { preparedResult, preparedText, preparedTexts, publishResult, } from "./runtime-publication.js";
|
|
@@ -20,7 +26,7 @@ export async function executeLocalRecipe(deps, context, piece) {
|
|
|
20
26
|
const input = piece.input;
|
|
21
27
|
if (input.kind === "provided") {
|
|
22
28
|
if (input.assetId === null)
|
|
23
|
-
throw new Error("A file you uploaded for this project is missing. Upload it again in Edit project, then
|
|
29
|
+
throw new Error("A file you uploaded for this project is missing. Upload it again in Edit project, then Try again.");
|
|
24
30
|
const row = z
|
|
25
31
|
.object({ id: z.string(), project_id: z.string(), path: z.string(), created_at: z.string() })
|
|
26
32
|
.parse(deps.db
|
|
@@ -46,12 +52,16 @@ export async function executeLocalRecipe(deps, context, piece) {
|
|
|
46
52
|
return "done";
|
|
47
53
|
}
|
|
48
54
|
if (input.kind !== "local")
|
|
49
|
-
throw new Error("Slopify hit an internal error (a local step was set up wrongly).
|
|
55
|
+
throw new Error("Slopify hit an internal error (a local step was set up wrongly). Try again; if it happens again, use Download diagnostics in Settings and report it.");
|
|
56
|
+
if (input.operation === "figure-card-v1")
|
|
57
|
+
return executeFigureCard(deps, context, piece);
|
|
50
58
|
if (input.operation === "narration-files-v1") {
|
|
51
59
|
await publishNarrationText(deps, context, piece);
|
|
52
60
|
return "done";
|
|
53
61
|
}
|
|
54
|
-
if (input.operation === "concat-narration"
|
|
62
|
+
if (input.operation === "concat-narration" ||
|
|
63
|
+
input.operation === "concat-turns-v1" ||
|
|
64
|
+
input.operation === levelOperation) {
|
|
55
65
|
await concatenate(deps, context, piece);
|
|
56
66
|
return "done";
|
|
57
67
|
}
|
|
@@ -78,19 +88,19 @@ export async function executeLocalRecipe(deps, context, piece) {
|
|
|
78
88
|
});
|
|
79
89
|
return "done";
|
|
80
90
|
}
|
|
81
|
-
throw new Error(`Slopify hit an internal error (no handler for the step "${input.operation}").
|
|
91
|
+
throw new Error(`Slopify hit an internal error (no handler for the step "${input.operation}"). Try again; if it happens again, use Download diagnostics in Settings and report it.`);
|
|
82
92
|
}
|
|
83
93
|
async function concatenate(deps, context, piece) {
|
|
84
94
|
const view = executionView(deps, context.work.projectId, context.work.revisionId);
|
|
85
95
|
if (view === undefined)
|
|
86
|
-
throw new Error("Slopify hit an internal error (the project version being built is missing).
|
|
96
|
+
throw new Error("Slopify hit an internal error (the project version being built is missing). Try again; if it happens again, use Download diagnostics in Settings and report it.");
|
|
87
97
|
const row = deps.db
|
|
88
98
|
.prepare("SELECT recipe_context FROM revision_work WHERE id=?")
|
|
89
99
|
.get(context.work.workId);
|
|
90
100
|
const plan = executionPlan(deps, view, savedCatalogue(row?.recipe_context));
|
|
91
101
|
const recipe = plan.recipes.find((candidate) => candidate.key === piece.key && candidate.fingerprint === piece.fingerprint);
|
|
92
102
|
if (recipe === undefined)
|
|
93
|
-
throw new Error("Slopify hit an internal error (the narration chunks to join no longer match the project's saved inputs).
|
|
103
|
+
throw new Error("Slopify hit an internal error (the narration chunks to join no longer match the project's saved inputs). Try again; if it happens again, use Download diagnostics in Settings and report it.");
|
|
94
104
|
const files = recipe.dependsOn.map((key) => {
|
|
95
105
|
const input = view.pieces.find((candidate) => candidate.key === key &&
|
|
96
106
|
candidate.selected &&
|
|
@@ -98,36 +108,73 @@ async function concatenate(deps, context, piece) {
|
|
|
98
108
|
candidate.piece.state === "done" &&
|
|
99
109
|
candidate.fingerprint === plan.recipes.find((value) => value.key === key)?.fingerprint);
|
|
100
110
|
if (input?.assetId === null || input === undefined)
|
|
101
|
-
throw new Error("One narration chunk has no saved audio, so the narration can't be joined together. Regenerate the missing chunk in Edit project → Narration, then
|
|
111
|
+
throw new Error("One narration chunk has no saved audio, so the narration can't be joined together. Regenerate the missing chunk in Edit project → Narration, then Try again.");
|
|
102
112
|
const asset = deps.db
|
|
103
113
|
.prepare("SELECT path FROM project_assets WHERE id=? AND project_id=?")
|
|
104
114
|
.get(input.assetId, context.work.projectId);
|
|
105
115
|
return outputPath(deps.paths, context.work.projectId, z.string().parse(asset?.path));
|
|
106
116
|
});
|
|
117
|
+
// Level the volume repeats the join its recipe names, from the same pieces, each levelled
|
|
118
|
+
// first (`recipe-loudness.ts`).
|
|
119
|
+
const level = recipe.input.kind === "local" && recipe.input.operation === levelOperation;
|
|
120
|
+
const [operation, values] = recipe.input.kind !== "local"
|
|
121
|
+
? ["", undefined]
|
|
122
|
+
: level
|
|
123
|
+
? z.tuple([z.string(), z.unknown(), z.unknown()]).parse(recipe.input.values)
|
|
124
|
+
: [recipe.input.operation, recipe.input.values];
|
|
107
125
|
const pending = allocateAsset(deps, context.work.projectId, "narration.mp3");
|
|
126
|
+
const piecesDirectory = join(dirname(pending.absolutePath), "levelled");
|
|
108
127
|
try {
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
128
|
+
const run = { bin: deps.ffmpeg, log: deps.log, signal: context.signal };
|
|
129
|
+
const levelled = level ? await levelPieces(run, files, piecesDirectory) : undefined;
|
|
130
|
+
// Pauses between sentences: planned from the pieces as spoken, made in what is joined.
|
|
131
|
+
const pauses = pausesIn(values);
|
|
132
|
+
const paced = pauses === undefined
|
|
133
|
+
? undefined
|
|
134
|
+
: await applyPauses(run, levelled?.files ?? files, paceScaled(await planPieces(run, files.map((file, at) => ({
|
|
135
|
+
file,
|
|
136
|
+
text: pieceText(plan.recipes.find((one) => one.key === recipe.dependsOn[at])),
|
|
137
|
+
next: pauses.next[at] ?? "end",
|
|
138
|
+
})), pauses.settings), operation === "concat-turns-v1" ? values : undefined), join(piecesDirectory, "paced"));
|
|
139
|
+
const joined = paced?.files ?? levelled?.files ?? files;
|
|
140
|
+
const durationMs = operation === "concat-turns-v1"
|
|
141
|
+
? await joinTurns(run, joined, values, pending.absolutePath)
|
|
142
|
+
: await joinNarration({ bin: deps.ffmpeg, log: deps.log }, {
|
|
143
|
+
files: joined,
|
|
144
|
+
output: pending.absolutePath,
|
|
145
|
+
listPath: join(dirname(pending.absolutePath), "parts.txt"),
|
|
146
|
+
signal: context.signal,
|
|
147
|
+
reencode: level || paced !== undefined,
|
|
148
|
+
});
|
|
149
|
+
rmSync(piecesDirectory, { recursive: true, force: true });
|
|
115
150
|
context.signal.throwIfAborted();
|
|
116
151
|
const asset = sealAsset(deps, pending);
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
152
|
+
const segment = /^(?:audio|level):(intro|outro)$/.exec(piece.key)?.[1] ?? "body";
|
|
153
|
+
const role = level
|
|
154
|
+
? "audio_levelled"
|
|
155
|
+
: piece.key === "audio:intro"
|
|
156
|
+
? "audio_intro"
|
|
157
|
+
: piece.key === "audio:outro"
|
|
158
|
+
? "audio_outro"
|
|
159
|
+
: "audio_body";
|
|
160
|
+
const voice = view.revision.config.audio === undefined
|
|
161
|
+
? {}
|
|
162
|
+
: {
|
|
163
|
+
provider: view.revision.config.audio.provider,
|
|
164
|
+
model: view.revision.config.audio.model,
|
|
165
|
+
voice: view.revision.config.audio.voice,
|
|
166
|
+
};
|
|
122
167
|
await publishResult(deps, context, piece, [
|
|
123
|
-
preparedResult(deps, context, piece, role, asset, durationMs,
|
|
124
|
-
?
|
|
168
|
+
preparedResult(deps, context, piece, role, asset, durationMs, levelled === undefined
|
|
169
|
+
? voice
|
|
125
170
|
: {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
171
|
+
...voice,
|
|
172
|
+
segment: segment,
|
|
173
|
+
loudness: levelled.report,
|
|
129
174
|
}),
|
|
130
175
|
], { durationMs }, asset);
|
|
176
|
+
if (level)
|
|
177
|
+
return;
|
|
131
178
|
for (const key of recipe.dependsOn) {
|
|
132
179
|
const retained = view.pieces.find((candidate) => candidate.key === key && candidate.selected && candidate.available);
|
|
133
180
|
if (retained?.publicationId === null || retained === undefined)
|
|
@@ -140,6 +187,83 @@ async function concatenate(deps, context, piece) {
|
|
|
140
187
|
}
|
|
141
188
|
}
|
|
142
189
|
finally {
|
|
190
|
+
rmSync(piecesDirectory, { recursive: true, force: true });
|
|
143
191
|
discardPreparedAssets(deps, [pending]);
|
|
144
192
|
}
|
|
145
193
|
}
|
|
194
|
+
// The pauses a join's values ask for (`recipe-pauses.ts`): its third value, when there is one.
|
|
195
|
+
function pausesIn(values) {
|
|
196
|
+
const value = Array.isArray(values) ? values[2] : undefined;
|
|
197
|
+
const parsed = z
|
|
198
|
+
.tuple([
|
|
199
|
+
z.literal("pauses-v1"),
|
|
200
|
+
z.number(),
|
|
201
|
+
z.number(),
|
|
202
|
+
z.array(z.enum(["end", "turn", "sentence", "paragraph"])),
|
|
203
|
+
])
|
|
204
|
+
.safeParse(value);
|
|
205
|
+
if (!parsed.success)
|
|
206
|
+
return undefined;
|
|
207
|
+
const [, sentenceSeconds, paragraphSeconds, next] = parsed.data;
|
|
208
|
+
return { settings: { sentenceSeconds, paragraphSeconds }, next };
|
|
209
|
+
}
|
|
210
|
+
// A speaker's pace plays their turn faster or slower, pauses included, so a turn's added
|
|
211
|
+
// silence is scaled by it to come out at the minimum.
|
|
212
|
+
function paceScaled(plans, turnValues) {
|
|
213
|
+
const layout = Array.isArray(turnValues) ? turnValues[1] : undefined;
|
|
214
|
+
if (!Array.isArray(layout))
|
|
215
|
+
return plans;
|
|
216
|
+
return plans.map((plan, at) => {
|
|
217
|
+
const pace = z.tuple([z.number().positive(), z.number()]).safeParse(layout[at]);
|
|
218
|
+
const factor = pace.success ? pace.data[0] : 1;
|
|
219
|
+
if (factor === 1)
|
|
220
|
+
return plan;
|
|
221
|
+
return {
|
|
222
|
+
...plan,
|
|
223
|
+
padEnd: plan.padEnd * factor,
|
|
224
|
+
pauses: {
|
|
225
|
+
...plan.pauses,
|
|
226
|
+
inserts: plan.pauses.inserts.map((one) => ({ ...one, seconds: one.seconds * factor })),
|
|
227
|
+
},
|
|
228
|
+
};
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
// The words a narration piece speaks, where its sentences end: the clean words of a request, or
|
|
232
|
+
// of a piece supplied as a file.
|
|
233
|
+
function pieceText(recipe) {
|
|
234
|
+
if (recipe === undefined)
|
|
235
|
+
return "";
|
|
236
|
+
const input = recipe.input;
|
|
237
|
+
// The words as said: a prepared request's delivery tags take no time of their own.
|
|
238
|
+
if (input.kind === "tts")
|
|
239
|
+
return (input.spokenText ?? input.text).replace(/\[[^\]]*\]/g, "");
|
|
240
|
+
if (input.kind === "provided" &&
|
|
241
|
+
Array.isArray(input.semantic) &&
|
|
242
|
+
typeof input.semantic[0] === "string")
|
|
243
|
+
return input.semantic[0];
|
|
244
|
+
return "";
|
|
245
|
+
}
|
|
246
|
+
// A multi-voice narration: every turn at its speaker's pace with the gap between turns, as the
|
|
247
|
+
// recipe laid it out (`recipe-voices.ts`), measured off the file that was written. `values` is
|
|
248
|
+
// the concat-turns-v1 recipe's own values.
|
|
249
|
+
export async function joinTurns(run, files, values, output) {
|
|
250
|
+
const layout = z
|
|
251
|
+
.tuple([z.unknown(), z.array(z.tuple([z.number().positive(), z.number().nonnegative()]))])
|
|
252
|
+
// The pauses between sentences may follow (`recipe-pauses.ts`); they were made already.
|
|
253
|
+
.rest(z.unknown())
|
|
254
|
+
.parse(values)[1];
|
|
255
|
+
if (layout.length !== files.length || files.length === 0)
|
|
256
|
+
throw new Error("Slopify hit an internal error (the speaker turns to join don't match their audio). Try again; if it happens again, use Download diagnostics in Settings and report it.");
|
|
257
|
+
await runFfmpeg({
|
|
258
|
+
bin: run.bin,
|
|
259
|
+
args: turnJoinArgs(files.map((path, index) => ({
|
|
260
|
+
path,
|
|
261
|
+
pace: layout[index]?.[0] ?? 1,
|
|
262
|
+
gapAfter: layout[index]?.[1] ?? 0,
|
|
263
|
+
})), output),
|
|
264
|
+
signal: run.signal,
|
|
265
|
+
log: run.log,
|
|
266
|
+
onProgress: () => { },
|
|
267
|
+
});
|
|
268
|
+
return probeDurationMs(run.bin, output, run.signal, run.log);
|
|
269
|
+
}
|
|
@@ -136,6 +136,10 @@ function futureKey(recipe) {
|
|
|
136
136
|
return `narration:prepare:${recipe.input.preparation.segment}:future`;
|
|
137
137
|
if (recipe.input.kind === "tts")
|
|
138
138
|
return `audio:${recipe.input.segment}:future`;
|
|
139
|
+
// The descriptions unfold from the one step that stood in for them while the text was
|
|
140
|
+
// unwritten.
|
|
141
|
+
if (recipe.key.startsWith("narration:describe:") || recipe.key.startsWith("figure:card:"))
|
|
142
|
+
return "narration:describe:future";
|
|
139
143
|
if (recipe.key.startsWith("research:chapter:"))
|
|
140
144
|
return "research:planner";
|
|
141
145
|
if (recipe.key === "article:continuation")
|
|
@@ -143,6 +147,10 @@ function futureKey(recipe) {
|
|
|
143
147
|
// A short's images and render unfold from its image prompts, admitted with them: under the
|
|
144
148
|
// pick on a first run, or on their own when one short is made again and the pick is kept.
|
|
145
149
|
// The prompts unfold from the pick's answer.
|
|
150
|
+
// A short's review unfolds with the short it reviews.
|
|
151
|
+
const reviewed = /^review:(shorts:\d+)$/.exec(recipe.key);
|
|
152
|
+
if (reviewed?.[1] !== undefined)
|
|
153
|
+
return `${reviewed[1]}:prompts`;
|
|
146
154
|
const clip = /^(shorts:\d+):(?:image:\d+|render)$/.exec(recipe.key);
|
|
147
155
|
if (clip?.[1] !== undefined)
|
|
148
156
|
return `${clip[1]}:prompts`;
|
|
@@ -3,7 +3,8 @@ import { insertManifestOutput, insertManifestPiece, selectOutputRecord, selectPi
|
|
|
3
3
|
import { retainedNarrationPieces } from "./narration-history.js";
|
|
4
4
|
import { matchingNarrationPiece } from "./narration-reuse.js";
|
|
5
5
|
export function bindNarrationReuse(deps, view, recipe, ordinal) {
|
|
6
|
-
if (recipe.input.kind === "local" &&
|
|
6
|
+
if (recipe.input.kind === "local" &&
|
|
7
|
+
(recipe.input.operation === "concat-narration" || recipe.input.operation === "concat-turns-v1")) {
|
|
7
8
|
const completed = view.pieces.find((one) => one.available &&
|
|
8
9
|
one.piece.state === "done" &&
|
|
9
10
|
one.key === recipe.key &&
|
|
@@ -9,7 +9,7 @@ export function joinedNarration(parts) {
|
|
|
9
9
|
export function narrationTextParts(view, plan, segment) {
|
|
10
10
|
const concat = plan.recipes.find((row) => row.key === (segment === "body" ? "audio:body:concat" : `audio:${segment}`));
|
|
11
11
|
if (concat === undefined)
|
|
12
|
-
throw new Error(`Slopify hit an internal error (the ${segment} narration is missing from the project plan).
|
|
12
|
+
throw new Error(`Slopify hit an internal error (the ${segment} narration is missing from the project plan). Try again; if it happens again, use Download diagnostics in Settings and report it.`);
|
|
13
13
|
return concat.dependsOn.map((key) => {
|
|
14
14
|
const recipe = plan.recipes.find((row) => row.key === key);
|
|
15
15
|
const selected = view.pieces.find((row) => row.key === key &&
|
|
@@ -18,7 +18,7 @@ export function narrationTextParts(view, plan, segment) {
|
|
|
18
18
|
row.piece.state === "done" &&
|
|
19
19
|
row.fingerprint === recipe?.fingerprint);
|
|
20
20
|
if (selected === undefined || recipe === undefined)
|
|
21
|
-
throw new Error("Some narration chunks aren't finished, so the full narration text isn't available. Let the Narration stage finish (Resume, or
|
|
21
|
+
throw new Error("Some narration chunks aren't finished, so the full narration text isn't available. Let the Narration stage finish (Resume, or Try again on it), or regenerate the missing chunk in Edit project → Narration.");
|
|
22
22
|
const input = recipe.input;
|
|
23
23
|
if (input.kind === "provided" &&
|
|
24
24
|
Array.isArray(input.semantic) &&
|
|
@@ -29,13 +29,13 @@ export function narrationTextParts(view, plan, segment) {
|
|
|
29
29
|
requestText: null,
|
|
30
30
|
};
|
|
31
31
|
if (input.kind !== "tts")
|
|
32
|
-
throw new Error("Slopify hit an internal error (a narration chunk's text was never worked out).
|
|
32
|
+
throw new Error("Slopify hit an internal error (a narration chunk's text was never worked out). Try again; if it happens again, use Download diagnostics in Settings and report it.");
|
|
33
33
|
const payload = z
|
|
34
34
|
.object({ text: z.string().optional(), spokenText: z.string().optional() })
|
|
35
35
|
.parse(JSON.parse(selected.piece.payload ?? "{}"));
|
|
36
36
|
if (input.spokenText !== undefined &&
|
|
37
37
|
(payload.spokenText !== input.spokenText || payload.text !== input.text))
|
|
38
|
-
throw new Error("A narration chunk's audio no longer matches its prepared text. Regenerate that chunk in Edit project → Narration, then
|
|
38
|
+
throw new Error("A narration chunk's audio no longer matches its prepared text. Regenerate that chunk in Edit project → Narration, then Try again.");
|
|
39
39
|
return {
|
|
40
40
|
logicalKey: input.logicalKey,
|
|
41
41
|
spokenText: input.spokenText === undefined ? (payload.text ?? input.text) : (payload.spokenText ?? ""),
|
|
@@ -45,7 +45,7 @@ export function narrationTextParts(view, plan, segment) {
|
|
|
45
45
|
}
|
|
46
46
|
export async function publishNarrationText(deps, context, piece) {
|
|
47
47
|
if (piece.input.kind !== "local")
|
|
48
|
-
throw new Error("Slopify hit an internal error (the narration text file step was set up wrongly).
|
|
48
|
+
throw new Error("Slopify hit an internal error (the narration text file step was set up wrongly). Try again; if it happens again, use Download diagnostics in Settings and report it.");
|
|
49
49
|
const { segment } = z
|
|
50
50
|
.object({ segment: z.enum(["body", "intro", "outro"]) })
|
|
51
51
|
.parse(piece.input.values);
|
|
@@ -54,10 +54,10 @@ export async function publishNarrationText(deps, context, piece) {
|
|
|
54
54
|
.prepare("SELECT recipe_context FROM revision_work WHERE id=?")
|
|
55
55
|
.get(context.work.workId);
|
|
56
56
|
if (view === undefined)
|
|
57
|
-
throw new Error("Slopify hit an internal error (the project version being narrated is missing).
|
|
57
|
+
throw new Error("Slopify hit an internal error (the project version being narrated is missing). Try again; if it happens again, use Download diagnostics in Settings and report it.");
|
|
58
58
|
const plan = executionPlan(deps, view, savedCatalogue(row?.recipe_context));
|
|
59
59
|
if (!plan.recipes.some((recipe) => recipe.key === piece.key && recipe.fingerprint === piece.fingerprint))
|
|
60
|
-
throw new Error("Slopify hit an internal error (the narration text no longer matches the project's saved inputs).
|
|
60
|
+
throw new Error("Slopify hit an internal error (the narration text no longer matches the project's saved inputs). Try again; if it happens again, use Download diagnostics in Settings and report it.");
|
|
61
61
|
const parts = narrationTextParts(view, plan, segment);
|
|
62
62
|
const outputs = preparedTexts(deps, context, piece, [
|
|
63
63
|
["narration_txt", `${segment}-narration.txt`, joinedNarration(parts)],
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { basename } from "node:path";
|
|
3
|
+
import { outputPath } from "../storage/layout.js";
|
|
4
|
+
// The file behind a picture the article names (``): an image uploaded
|
|
5
|
+
// to the project under that file name. An article's pictures are otherwise web addresses
|
|
6
|
+
// Slopify never fetches, so a figure without its upload is described and drawn from its
|
|
7
|
+
// caption alone.
|
|
8
|
+
export function articlePicture(deps, view, named) {
|
|
9
|
+
if (named === undefined)
|
|
10
|
+
return undefined;
|
|
11
|
+
const name = basename((named.split(/[?#]/)[0] ?? "").replace(/\\/g, "/")).toLowerCase();
|
|
12
|
+
if (name === "")
|
|
13
|
+
return undefined;
|
|
14
|
+
for (const image of Object.values(view.revision.content.imageDefinitions)) {
|
|
15
|
+
if (image.source !== "provide" || image.assetId === null)
|
|
16
|
+
continue;
|
|
17
|
+
const row = deps.db
|
|
18
|
+
.prepare("SELECT path FROM project_assets WHERE id=? AND project_id=?")
|
|
19
|
+
.get(image.assetId, view.revision.projectId);
|
|
20
|
+
const path = typeof row?.path === "string" ? row.path : undefined;
|
|
21
|
+
if (path === undefined || basename(path).toLowerCase() !== name)
|
|
22
|
+
continue;
|
|
23
|
+
const file = outputPath(deps.paths, view.revision.projectId, path);
|
|
24
|
+
if (existsSync(file))
|
|
25
|
+
return file;
|
|
26
|
+
}
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
@@ -4,6 +4,9 @@ import { executionPlan, executionView, savedCatalogue } from "./runtime-plan.js"
|
|
|
4
4
|
// Undefined for steps that name their own place (captions, export) or have no single one.
|
|
5
5
|
export function pieceLabel(deps, context, piece) {
|
|
6
6
|
const { key } = piece;
|
|
7
|
+
const variant = /^thumbnail:image:([23])$/.exec(key);
|
|
8
|
+
if (variant !== null)
|
|
9
|
+
return `Thumbnail ${variant[1] ?? ""}`;
|
|
7
10
|
if (key === "thumbnail:image" || key.startsWith("thumbnail:"))
|
|
8
11
|
return "Thumbnail";
|
|
9
12
|
if (key === "reference:image")
|
|
@@ -14,6 +17,30 @@ export function pieceLabel(deps, context, piece) {
|
|
|
14
17
|
return "Document";
|
|
15
18
|
if (key === "youtube:description")
|
|
16
19
|
return "YouTube description";
|
|
20
|
+
if (key.startsWith("review:")) {
|
|
21
|
+
const item = key.slice("review:".length);
|
|
22
|
+
const itemLabel = item === "narration"
|
|
23
|
+
? "Narration"
|
|
24
|
+
: /^shorts:\d+$/.test(item)
|
|
25
|
+
? `Short ${item.slice("shorts:".length)}`
|
|
26
|
+
: pieceLabel(deps, context, { key: item });
|
|
27
|
+
return `Review of ${itemLabel ?? "an item"}`;
|
|
28
|
+
}
|
|
29
|
+
if (key === "voices:files")
|
|
30
|
+
return "MP3 and M4B files";
|
|
31
|
+
if (key === "script:attribute")
|
|
32
|
+
return "Speaker split";
|
|
33
|
+
if (key === "narration:describe:future")
|
|
34
|
+
return "Narration descriptions";
|
|
35
|
+
const card = /^figure:card:(\d+)$/.exec(key);
|
|
36
|
+
if (card !== null)
|
|
37
|
+
return `On-screen card ${card[1] ?? ""}`;
|
|
38
|
+
const described = /^narration:describe:(\d+)$/.exec(key);
|
|
39
|
+
if (described !== null)
|
|
40
|
+
return `Narration description ${described[1] ?? ""}`;
|
|
41
|
+
const turn = /^audio:body:turn:(\d+):\d+$/.exec(key);
|
|
42
|
+
if (turn !== null)
|
|
43
|
+
return `Turn ${turn[1] ?? ""}`;
|
|
17
44
|
if (key === "shorts:pick" || key === "shorts:future")
|
|
18
45
|
return "Shorts";
|
|
19
46
|
if (key === "animate:future")
|