@gentbajko/slopify 2.4.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -21
- package/NOTICE +12 -0
- package/README.md +21 -40
- package/SUBTITLES.md +1 -1
- package/dist/adapter-registry.js +11 -1
- package/dist/adapters/alignment/cache.js +4 -1
- package/dist/adapters/alignment/ctc.js +25 -23
- package/dist/adapters/alignment/index.js +37 -6
- package/dist/adapters/alignment/multilingual.js +267 -0
- package/dist/adapters/alignment/protocol.js +11 -0
- package/dist/adapters/alignment/quality.js +4 -3
- package/dist/adapters/alignment/sentences.js +95 -0
- package/dist/adapters/alignment/spec.js +24 -0
- package/dist/adapters/alignment/spell.js +769 -0
- package/dist/adapters/alignment/text.js +56 -9
- package/dist/adapters/alignment/window.js +34 -30
- package/dist/adapters/alignment/worker.js +23 -21
- package/dist/adapters/explain.js +11 -11
- package/dist/adapters/host-cli/index.js +55 -5
- package/dist/adapters/host-cli/transport.js +13 -6
- package/dist/adapters/image/bytes.js +2 -2
- package/dist/adapters/image/codex-output.js +60 -18
- package/dist/adapters/image/codex.js +166 -25
- package/dist/adapters/image/fal.js +31 -3
- package/dist/adapters/image/google.js +17 -2
- package/dist/adapters/image/openai.js +45 -16
- package/dist/adapters/image/reference.js +41 -0
- package/dist/adapters/image/replicate.js +14 -2
- package/dist/adapters/image/video.js +2 -2
- package/dist/adapters/key-probes.js +36 -0
- package/dist/adapters/llm/claude-code.js +168 -32
- package/dist/adapters/llm/cli-login-error.js +1 -1
- package/dist/adapters/llm/codex-limits.js +157 -0
- package/dist/adapters/llm/codex-models.js +2 -1
- package/dist/adapters/llm/codex-server-models.js +2 -1
- package/dist/adapters/llm/codex.js +99 -12
- package/dist/adapters/llm/document-workspace.js +2 -2
- package/dist/adapters/llm/gemini-workspace.js +1 -1
- package/dist/adapters/llm/gemini.js +16 -3
- package/dist/adapters/llm/image-workspace.js +58 -0
- package/dist/adapters/llm/openrouter.js +11 -3
- package/dist/adapters/llm/run-cli.js +26 -9
- package/dist/adapters/tts/cartesia.js +10 -0
- package/dist/adapters/tts/elevenlabs.js +31 -9
- package/dist/adapters/tts/inworld-async.js +9 -5
- package/dist/adapters/tts/inworld.js +17 -3
- package/dist/adapters/tts/openai.js +3 -0
- package/dist/adapters/tts/voice-languages.js +80 -0
- package/dist/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/NotoSansArabic-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/NotoSansDevanagari-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/NotoSansHebrew-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/NotoSansThai-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/OFL.txt +93 -0
- package/dist/assets/fonts/noto/SOURCE.txt +18 -0
- package/dist/assets/models.yaml +7 -0
- package/dist/assets/sample/sample-audiobook.tar +0 -0
- package/dist/assets/sample/sample-podcast.tar +0 -0
- package/dist/assets/sample/sample-project.tar +0 -0
- package/dist/catalog/merge.js +132 -0
- package/dist/catalog/registry.js +33 -12
- package/dist/catalog/runtime-models.js +1 -1
- package/dist/catalog/schema.js +12 -1
- package/dist/catalog/store.js +147 -8
- package/dist/catalog/validate.js +17 -2
- package/dist/compose.yaml +71 -0
- package/dist/edge/autostart/docker-record.js +32 -0
- package/dist/edge/autostart/docker.js +50 -0
- package/dist/edge/autostart/index.js +78 -0
- package/dist/edge/autostart/launcher.js +176 -0
- package/dist/edge/autostart/model.js +3 -0
- package/dist/edge/autostart/native.js +210 -0
- package/dist/edge/autostart/prompt.js +66 -0
- package/dist/edge/autostart/service.js +81 -0
- package/dist/edge/cli.js +57 -51
- package/dist/edge/docker-install/activation.js +56 -0
- package/dist/edge/docker-install/apply.js +458 -0
- package/dist/edge/docker-install/engine.js +362 -0
- package/dist/edge/docker-install/run.js +154 -0
- package/dist/edge/docker-install/state.js +146 -0
- package/dist/edge/{docker-projects → docker-install}/volume.js +1 -5
- package/dist/edge/docker.js +14 -22
- package/dist/edge/events/hub.js +11 -0
- package/dist/edge/host-cli.js +1 -1
- package/dist/edge/http/actions.js +32 -10
- package/dist/edge/http/app.js +60 -2
- package/dist/edge/http/auditions.js +70 -0
- package/dist/edge/http/autostart.js +37 -0
- package/dist/edge/http/backups.js +40 -0
- package/dist/edge/http/channel-memory.js +92 -0
- package/dist/edge/http/channels.js +128 -0
- package/dist/edge/http/entries.js +22 -3
- package/dist/edge/http/files.js +1 -1
- package/dist/edge/http/folder-location.js +1 -1
- package/dist/edge/http/home.js +39 -0
- package/dist/edge/http/host-cli.js +45 -6
- package/dist/edge/http/narration-peaks.js +76 -0
- package/dist/edge/http/onboarding.js +152 -0
- package/dist/edge/http/open-folder.js +1 -1
- package/dist/edge/http/patch-notes.js +45 -0
- package/dist/edge/http/planning.js +4 -2
- package/dist/edge/http/problem.js +1 -0
- package/dist/edge/http/project-create.js +87 -0
- package/dist/edge/http/projects.js +31 -99
- package/dist/edge/http/prompts.js +40 -3
- package/dist/edge/http/pronunciations.js +23 -4
- package/dist/edge/http/providers.js +86 -0
- package/dist/edge/http/reviews.js +121 -0
- package/dist/edge/http/revision-files.js +1 -1
- package/dist/edge/http/revisions.js +1 -1
- package/dist/edge/http/run-cost.js +25 -0
- package/dist/edge/http/schedules.js +111 -6
- package/dist/edge/http/settings.js +128 -6
- package/dist/edge/http/storage-files.js +50 -0
- package/dist/edge/http/storage.js +29 -2
- package/dist/edge/http/studio.js +207 -0
- package/dist/edge/http/style-preview.js +100 -0
- package/dist/edge/http/subtitles.js +1 -1
- package/dist/edge/http/trash.js +69 -0
- package/dist/edge/http/update.js +24 -3
- package/dist/edge/http/whats-new.js +15 -0
- package/dist/edge/http/youtube-edits.js +83 -0
- package/dist/edge/native-update.js +62 -0
- package/dist/host-cli/open-folder.js +10 -10
- package/dist/host-cli/runtime.js +12 -3
- package/dist/kernel/db/migrations/0022-drop-duplicate-held-work.sql +56 -0
- package/dist/kernel/db/migrations/0024-reference-attachments.sql +18 -0
- package/dist/kernel/db/migrations/0025-automatic-reviews.sql +40 -0
- package/dist/kernel/db/migrations/0026-schedule-topic-generation.sql +23 -0
- package/dist/kernel/db/migrations/0027-run-cost.sql +58 -0
- package/dist/kernel/db/migrations/0030-automatic-retries.sql +18 -0
- package/dist/kernel/db/migrations/0031-prompt-history-and-description-edits.sql +33 -0
- package/dist/kernel/db/migrations/0032-channels.sql +65 -0
- package/dist/kernel/db/migrations/0034-script-prompts.sql +23 -0
- package/dist/kernel/db/migrations/0035-narration-aliases.sql +13 -0
- package/dist/kernel/db/migrations/0037-project-uploads.sql +8 -0
- package/dist/kernel/db/migrations/0038-voice-languages.sql +7 -0
- package/dist/kernel/db/migrations/0039-channel-essentials.sql +64 -0
- package/dist/kernel/db/migrations/0040-standalone-usage.sql +35 -0
- package/dist/kernel/paths.js +27 -6
- package/dist/kernel/pipeline.js +4 -1
- package/dist/kernel/ports/host-cli.js +139 -16
- package/dist/kernel/ports/image.js +3 -1
- package/dist/kernel/ports/key-probe.js +1 -0
- package/dist/kernel/ports/languages.js +157 -0
- package/dist/kernel/ports/llm.js +3 -1
- package/dist/kernel/ports/model.js +5 -2
- package/dist/kernel/ports/narration-aliases.js +60 -0
- package/dist/kernel/ports/plan-limits.js +36 -0
- package/dist/kernel/runner/attempt.js +18 -9
- package/dist/kernel/runner/audition.js +36 -0
- package/dist/kernel/runner/graph.js +24 -5
- package/dist/kernel/runner/index.js +35 -5
- package/dist/kernel/runner/meter.js +1 -0
- package/dist/kernel/runner/providers.js +164 -9
- package/dist/kernel/runner/retry-policy.js +43 -0
- package/dist/kernel/runner/standalone.js +134 -0
- package/dist/kernel/runner/work-authority.js +28 -2
- package/dist/main.js +321 -20
- package/dist/patch-notes/0.4.2-to-1.0.md +93 -0
- package/dist/patch-notes/1.0-to-2.0.md +74 -0
- package/dist/patch-notes/2.0-to-3.0.md +257 -0
- package/dist/patch-notes/3.0.0.md +70 -0
- package/dist/patch-notes/index.json +26 -0
- package/dist/slices/admission/model.js +35 -0
- package/dist/slices/admission/repo.js +24 -4
- package/dist/slices/admission/rules.js +126 -1
- package/dist/slices/admission/schema.js +68 -1
- package/dist/slices/admission/short-mode.js +47 -0
- package/dist/slices/admission/start.js +90 -16
- package/dist/slices/article/continuation.js +2 -2
- package/dist/slices/article/provided-entries.js +2 -2
- package/dist/slices/article/run.js +6 -2
- package/dist/slices/article/segments.js +3 -3
- package/dist/slices/backups/files.js +135 -0
- package/dist/slices/backups/folder.js +43 -0
- package/dist/slices/backups/model.js +65 -0
- package/dist/slices/backups/repo.js +28 -0
- package/dist/slices/backups/schedule.js +60 -0
- package/dist/slices/backups/service.js +311 -0
- package/dist/slices/batch/index.js +9 -2
- package/dist/slices/cancel/index.js +1 -1
- package/dist/slices/channels/cast-images.js +110 -0
- package/dist/slices/channels/cast-match.js +32 -0
- package/dist/slices/channels/images.js +19 -0
- package/dist/slices/channels/model.js +5 -0
- package/dist/slices/channels/rebrand.js +100 -0
- package/dist/slices/channels/repo.js +218 -0
- package/dist/slices/channels/runs.js +129 -0
- package/dist/slices/channels/schema.js +129 -0
- package/dist/slices/channels/service.js +180 -0
- package/dist/slices/channels/studio-csv.js +110 -0
- package/dist/slices/channels/videos.js +120 -0
- package/dist/slices/checkpoints/recovery.js +10 -0
- package/dist/slices/control/index.js +2 -1
- package/dist/slices/document/fonts.js +1 -1
- package/dist/slices/document/render.js +1 -1
- package/dist/slices/episodes/related.js +90 -0
- package/dist/slices/episodes/repo.js +60 -0
- package/dist/slices/episodes/service.js +58 -0
- package/dist/slices/episodes/summarize.js +170 -0
- package/dist/slices/estimate/index.js +113 -24
- package/dist/slices/estimate/requests.js +60 -2
- package/dist/slices/fixes/rules.js +73 -0
- package/dist/slices/fonts/coverage.js +82 -0
- package/dist/slices/fonts/sfnt.js +74 -0
- package/dist/slices/images/run.js +8 -5
- package/dist/slices/images/scale.js +84 -0
- package/dist/slices/library/diff.js +122 -0
- package/dist/slices/library/history.js +100 -0
- package/dist/slices/library/model.js +3 -0
- package/dist/slices/library/photorealistic.js +49 -0
- package/dist/slices/library/repo.js +19 -14
- package/dist/slices/library/save.js +62 -12
- package/dist/slices/library/slots.js +15 -3
- package/dist/slices/library/snapshot.js +8 -2
- package/dist/slices/library/used-by.js +153 -0
- package/dist/slices/loudness/level-pieces.js +41 -0
- package/dist/slices/loudness/loudnorm.js +164 -0
- package/dist/slices/loudness/model.js +153 -0
- package/dist/slices/model-upkeep/model.js +10 -0
- package/dist/slices/model-upkeep/switch.js +217 -0
- package/dist/slices/model-upkeep/usage.js +183 -0
- package/dist/slices/narration/aliases-library.js +31 -0
- package/dist/slices/narration/aliases-schema.js +53 -0
- package/dist/slices/narration/aliases.js +49 -0
- package/dist/slices/narration/blocks.js +352 -0
- package/dist/slices/narration/chunk.js +19 -7
- package/dist/slices/narration/concat.js +2 -2
- package/dist/slices/narration/describe.js +45 -0
- package/dist/slices/narration/pauses-model.js +63 -0
- package/dist/slices/narration/pauses.js +269 -0
- package/dist/slices/narration/peaks-model.js +1 -0
- package/dist/slices/narration/peaks.js +94 -0
- package/dist/slices/narration/preparation.js +21 -3
- package/dist/slices/narration/pronunciation-chunks.js +3 -3
- package/dist/slices/narration/pronunciation.js +72 -25
- package/dist/slices/narration/run.js +6 -6
- package/dist/slices/narration/shared-glossary.js +1 -1
- package/dist/slices/narration/steering.js +15 -4
- package/dist/slices/notifications/notifier.js +94 -0
- package/dist/slices/notifications/rules.js +102 -0
- package/dist/slices/notifications/send.js +40 -0
- package/dist/slices/notifications/settings.js +34 -0
- package/dist/slices/onboarding/copy.js +143 -0
- package/dist/slices/onboarding/first-run.js +46 -0
- package/dist/slices/onboarding/install.js +119 -0
- package/dist/slices/onboarding/model.js +64 -0
- package/dist/slices/onboarding/packs.js +491 -0
- package/dist/slices/onboarding/quick-short.js +129 -0
- package/dist/slices/onboarding/sample.js +132 -0
- package/dist/slices/onboarding/state.js +68 -0
- package/dist/slices/onboarding/template.js +73 -0
- package/dist/slices/patch-notes/library.js +73 -0
- package/dist/slices/patch-notes/seen.js +77 -0
- package/dist/slices/play-drafts/convert.js +160 -9
- package/dist/slices/play-drafts/readiness.js +3 -0
- package/dist/slices/play-drafts/repo.js +9 -1
- package/dist/slices/play-drafts/review-inputs.js +22 -3
- package/dist/slices/play-drafts/schema.js +109 -2
- package/dist/slices/play-drafts/service.js +10 -1
- package/dist/slices/play-drafts/start.js +43 -18
- package/dist/slices/play-drafts/uploads.js +2 -1
- package/dist/slices/project-templates/from-project.js +70 -4
- package/dist/slices/project-templates/one-off.js +26 -0
- package/dist/slices/project-templates/repo.js +2 -1
- package/dist/slices/project-templates/service.js +15 -4
- package/dist/slices/project-templates/setup.js +29 -2
- package/dist/slices/rebuild/admission-repo.js +1 -1
- package/dist/slices/rebuild/model.js +6 -0
- package/dist/slices/rebuild/preview-details.js +67 -2
- package/dist/slices/rebuild/preview-plan.js +13 -6
- package/dist/slices/rebuild/recipe-audio-parts.js +40 -7
- package/dist/slices/rebuild/recipe-audio.js +66 -12
- package/dist/slices/rebuild/recipe-build.js +15 -5
- package/dist/slices/rebuild/recipe-cast.js +18 -0
- package/dist/slices/rebuild/recipe-checkpoints.js +11 -9
- package/dist/slices/rebuild/recipe-describe.js +93 -0
- package/dist/slices/rebuild/recipe-edit.js +13 -1
- package/dist/slices/rebuild/recipe-exports.js +62 -8
- package/dist/slices/rebuild/recipe-input-schema.js +43 -1
- package/dist/slices/rebuild/recipe-legacy.js +12 -0
- package/dist/slices/rebuild/recipe-loudness.js +45 -0
- package/dist/slices/rebuild/recipe-model.js +25 -6
- package/dist/slices/rebuild/recipe-pauses.js +7 -0
- package/dist/slices/rebuild/recipe-preparation.js +3 -3
- package/dist/slices/rebuild/recipe-provider-choice.js +11 -1
- package/dist/slices/rebuild/recipe-reference.js +58 -0
- package/dist/slices/rebuild/recipe-reviews.js +117 -0
- package/dist/slices/rebuild/recipe-save.js +4 -1
- package/dist/slices/rebuild/recipe-shorts.js +32 -5
- package/dist/slices/rebuild/recipe-text.js +114 -19
- package/dist/slices/rebuild/recipe-validation.js +29 -3
- package/dist/slices/rebuild/recipe-visual.js +104 -29
- package/dist/slices/rebuild/recipe-voices.js +286 -0
- package/dist/slices/rebuild/recipe-work.js +77 -6
- package/dist/slices/rebuild/recipe-youtube.js +2 -0
- package/dist/slices/rebuild/recovery-conflict.js +7 -3
- package/dist/slices/rebuild/recovery-model.js +3 -0
- package/dist/slices/rebuild/recovery-selection.js +28 -2
- package/dist/slices/rebuild/recovery.js +35 -17
- package/dist/slices/rebuild/repo.js +3 -3
- package/dist/slices/rebuild/retry.js +18 -0
- package/dist/slices/rebuild/review-redo.js +110 -0
- package/dist/slices/rebuild/runtime-actions.js +14 -2
- package/dist/slices/rebuild/runtime-admission.js +4 -1
- package/dist/slices/rebuild/runtime-animate.js +4 -4
- package/dist/slices/rebuild/runtime-article.js +5 -5
- package/dist/slices/rebuild/runtime-document.js +4 -4
- package/dist/slices/rebuild/runtime-export-bed.js +43 -0
- package/dist/slices/rebuild/runtime-export-edit.js +70 -11
- package/dist/slices/rebuild/runtime-export-inputs.js +52 -10
- package/dist/slices/rebuild/runtime-export-short.js +85 -0
- package/dist/slices/rebuild/runtime-export.js +84 -19
- package/dist/slices/rebuild/runtime-figure-card.js +87 -0
- package/dist/slices/rebuild/runtime-image.js +69 -0
- package/dist/slices/rebuild/runtime-local.js +154 -27
- package/dist/slices/rebuild/runtime-materialize.js +8 -0
- package/dist/slices/rebuild/runtime-narration-reuse.js +2 -1
- package/dist/slices/rebuild/runtime-narration-text.js +7 -7
- package/dist/slices/rebuild/runtime-pictures.js +28 -0
- package/dist/slices/rebuild/runtime-piece-label.js +29 -0
- package/dist/slices/rebuild/runtime-provider.js +134 -19
- package/dist/slices/rebuild/runtime-publication.js +21 -5
- package/dist/slices/rebuild/runtime-review.js +237 -0
- package/dist/slices/rebuild/runtime-run.js +17 -11
- package/dist/slices/rebuild/runtime-shorts.js +48 -23
- package/dist/slices/rebuild/runtime-store.js +39 -10
- package/dist/slices/rebuild/runtime-subtitles.js +72 -19
- package/dist/slices/rebuild/runtime-voices.js +103 -0
- package/dist/slices/rebuild/runtime-youtube.js +7 -6
- package/dist/slices/rebuild/service-readiness.js +15 -1
- package/dist/slices/rebuild/soften.js +49 -0
- package/dist/slices/rebuild/transition-repo.js +10 -3
- package/dist/slices/research/run.js +4 -4
- package/dist/slices/research/synthesis.js +2 -2
- package/dist/slices/reviews/model.js +105 -0
- package/dist/slices/reviews/outcome.js +22 -0
- package/dist/slices/reviews/repo.js +106 -0
- package/dist/slices/reviews/rules.js +63 -0
- package/dist/slices/reviews/verdict.js +71 -0
- package/dist/slices/reviews/view.js +30 -0
- package/dist/slices/revisions/adopt-content.js +12 -2
- package/dist/slices/revisions/adopt.js +9 -2
- package/dist/slices/revisions/downloads.js +7 -1
- package/dist/slices/revisions/image-plan.js +183 -0
- package/dist/slices/revisions/mutation-assets.js +43 -9
- package/dist/slices/revisions/mutation-cues.js +71 -0
- package/dist/slices/revisions/mutation-prepare.js +8 -10
- package/dist/slices/revisions/mutation-request.js +2 -1
- package/dist/slices/revisions/mutations.js +97 -3
- package/dist/slices/revisions/schema.js +16 -2
- package/dist/slices/run-cost/limits.js +128 -0
- package/dist/slices/run-cost/meter.js +47 -0
- package/dist/slices/run-cost/panel.js +234 -0
- package/dist/slices/run-cost/pricing.js +92 -0
- package/dist/slices/run-cost/week.js +108 -0
- package/dist/slices/schedules/agenda.js +158 -0
- package/dist/slices/schedules/repo.js +58 -18
- package/dist/slices/schedules/scheduler.js +67 -16
- package/dist/slices/schedules/schema.js +138 -1
- package/dist/slices/schedules/service.js +48 -4
- package/dist/slices/schedules/similar.js +90 -0
- package/dist/slices/schedules/topic-list.js +163 -0
- package/dist/slices/schedules/topics.js +422 -0
- package/dist/slices/settings/first-run.js +102 -0
- package/dist/slices/settings/health.js +190 -0
- package/dist/slices/settings/key-guides.js +128 -0
- package/dist/slices/settings/key-test.js +85 -0
- package/dist/slices/settings/model.js +11 -1
- package/dist/slices/settings/playback.js +15 -0
- package/dist/slices/settings/repo.js +17 -3
- package/dist/slices/settings/voices.js +64 -4
- package/dist/slices/settings/whats-new.js +64 -0
- package/dist/slices/shorts/captions.js +9 -0
- package/dist/slices/shorts/pick.js +3 -3
- package/dist/slices/shorts/prompts.js +1 -1
- package/dist/slices/shorts/render.js +54 -5
- package/dist/slices/storage/asset-name.js +3 -0
- package/dist/slices/storage/backup-export.js +48 -9
- package/dist/slices/storage/backup-format.js +38 -1
- package/dist/slices/storage/backup-import.js +191 -19
- package/dist/slices/storage/delete-project.js +7 -4
- package/dist/slices/storage/documents.js +79 -0
- package/dist/slices/storage/files-location.js +531 -0
- package/dist/slices/storage/layout.js +20 -0
- package/dist/slices/storage/model.js +14 -0
- package/dist/slices/storage/portable.js +95 -12
- package/dist/slices/storage/reconcile.js +19 -2
- package/dist/slices/storage/repo.js +4 -1
- package/dist/slices/storage/schema.js +11 -0
- package/dist/slices/storage/trim.js +146 -0
- package/dist/slices/studio/disclosure.js +74 -0
- package/dist/slices/studio/model.js +28 -0
- package/dist/slices/studio/pack.js +209 -0
- package/dist/slices/studio/settings.js +133 -0
- package/dist/slices/style-preview/images.js +61 -0
- package/dist/slices/style-preview/render.js +161 -0
- package/dist/slices/style-preview/schema.js +45 -0
- package/dist/slices/style-preview/service.js +93 -0
- package/dist/slices/style-preview/settings.js +89 -0
- package/dist/slices/subtitles/captions.js +42 -9
- package/dist/slices/subtitles/model.js +22 -0
- package/dist/slices/subtitles/prepare.js +27 -16
- package/dist/slices/subtitles/transcript.js +1 -1
- package/dist/slices/thumbnail/run.js +7 -7
- package/dist/slices/trash/model.js +25 -0
- package/dist/slices/trash/service.js +273 -0
- package/dist/slices/uploads/repo.js +20 -0
- package/dist/slices/video/ambient-bed-schema.js +15 -0
- package/dist/slices/video/ambient-bed.js +95 -0
- package/dist/slices/video/ambient-mix.js +72 -0
- package/dist/slices/video/audio-inputs.js +2 -2
- package/dist/slices/video/cards.js +5 -2
- package/dist/slices/video/edit-list.js +23 -2
- package/dist/slices/video/edit-settings.js +13 -3
- package/dist/slices/video/ffmpeg.js +63 -9
- package/dist/slices/video/figure-card.js +492 -0
- package/dist/slices/video/figure-spans.js +51 -0
- package/dist/slices/video/plan.js +125 -13
- package/dist/slices/video/run.js +14 -12
- package/dist/slices/video/slideshow.js +39 -4
- package/dist/slices/voices/attribution.js +69 -0
- package/dist/slices/voices/audio-files.js +82 -0
- package/dist/slices/voices/audition.js +25 -0
- package/dist/slices/voices/cast.js +56 -0
- package/dist/slices/voices/grouping.js +49 -0
- package/dist/slices/voices/join.js +35 -0
- package/dist/slices/voices/languages.js +25 -0
- package/dist/slices/voices/model.js +179 -0
- package/dist/slices/voices/palette.js +26 -0
- package/dist/slices/voices/panel.js +91 -0
- package/dist/slices/voices/portraits.js +55 -0
- package/dist/slices/voices/script.js +136 -0
- package/dist/slices/voices/timing.js +58 -0
- package/dist/slices/youtube/answer.js +1 -1
- package/dist/slices/youtube/chapters.js +126 -0
- package/dist/slices/youtube/edits-repo.js +69 -0
- package/dist/slices/youtube/edits.js +89 -0
- package/dist/slices/youtube/placeholders.js +98 -0
- package/dist/slices/youtube/transcript.js +11 -4
- package/dist/updater/service.js +66 -21
- package/dist/web/assets/index-CUA2sUxU.css +1 -0
- package/dist/web/assets/index-CX590kE-.js +303 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
- package/dist/web/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
- package/dist/web/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
- package/dist/web/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
- package/dist/web/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
- package/dist/web/assets/{pdf-CAJNT254.js → pdf-BG8n_uc5.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +4 -4
- package/dist/edge/docker-launch.js +0 -81
- package/dist/edge/docker-projects/activation.js +0 -35
- package/dist/edge/docker-projects/claims.js +0 -85
- package/dist/edge/docker-projects/committed.js +0 -40
- package/dist/edge/docker-projects/engine.js +0 -461
- package/dist/edge/docker-projects/install.js +0 -254
- package/dist/edge/docker-projects/prune.js +0 -66
- package/dist/edge/docker-projects/recover.js +0 -100
- package/dist/edge/docker-projects/state.js +0 -225
- package/dist/web/assets/index-BGgYMm2u.css +0 -1
- package/dist/web/assets/index-BVVgtntL.js +0 -171
- package/scripts/docker-run.sh +0 -3
- /package/dist/edge/{docker-projects → docker-install}/tree.js +0 -0
|
@@ -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,14 +4,43 @@ 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";
|
|
12
|
+
if (key === "reference:image")
|
|
13
|
+
return "Establishing image";
|
|
9
14
|
if (key === "article:body")
|
|
10
15
|
return "Article";
|
|
11
16
|
if (key === "document:pdf")
|
|
12
17
|
return "Document";
|
|
13
18
|
if (key === "youtube:description")
|
|
14
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] ?? ""}`;
|
|
15
44
|
if (key === "shorts:pick" || key === "shorts:future")
|
|
16
45
|
return "Shorts";
|
|
17
46
|
if (key === "animate:future")
|
|
@@ -1,17 +1,26 @@
|
|
|
1
|
+
import { basename } from "node:path";
|
|
2
|
+
import { referenceKey, thumbnailVariant } from "../admission/model.js";
|
|
1
3
|
import { plainText } from "../article/plain.js";
|
|
2
4
|
import { splitEndMatter } from "../article/split.js";
|
|
5
|
+
import { spokenPassage } from "../narration/describe.js";
|
|
3
6
|
import { observeNarration } from "../narration/live.js";
|
|
4
7
|
import { validatePreparation } from "../narration/preparation.js";
|
|
5
8
|
import { prepareRequests } from "../narration/steering.js";
|
|
6
9
|
import { chaptersFrom } from "../research/planner.js";
|
|
7
10
|
import { sourcedAnswer } from "../research/synthesis.js";
|
|
11
|
+
import { imageReviewers } from "../reviews/model.js";
|
|
8
12
|
import { discardPreparedAssets, writeAsset } from "../storage/assets.js";
|
|
9
13
|
import { outputPath } from "../storage/layout.js";
|
|
10
14
|
import { probeDurationMs } from "../video/ffmpeg.js";
|
|
15
|
+
import { parseAttribution } from "../voices/attribution.js";
|
|
16
|
+
import { parseScript } from "../voices/script.js";
|
|
11
17
|
import { executeArticleRequests } from "./runtime-article.js";
|
|
18
|
+
import { imageCall } from "./runtime-image.js";
|
|
12
19
|
import { frozenInstructions } from "./runtime-instructions.js";
|
|
20
|
+
import { articlePicture } from "./runtime-pictures.js";
|
|
13
21
|
import { executionPlan, executionView, savedCatalogue } from "./runtime-plan.js";
|
|
14
22
|
import { preparedResult, preparedTexts, publishResult } from "./runtime-publication.js";
|
|
23
|
+
import { clearSoftening, softenedPrompt, softeningRequested, softenMessages } from "./soften.js";
|
|
15
24
|
export async function executeProviderRecipe(deps, context, providers, piece) {
|
|
16
25
|
if (deps.db
|
|
17
26
|
.prepare("SELECT state FROM revision_work_pieces WHERE id=? AND work_id=?")
|
|
@@ -20,6 +29,8 @@ export async function executeProviderRecipe(deps, context, providers, piece) {
|
|
|
20
29
|
const input = piece.input;
|
|
21
30
|
const wrapped = providers.forPiece(piece.id);
|
|
22
31
|
if (input.kind === "llm") {
|
|
32
|
+
// A described figure's own picture, when the project has it and the model can look.
|
|
33
|
+
const picture = describedPicture(deps, context, input);
|
|
23
34
|
const answer = piece.key === "article:body"
|
|
24
35
|
? await executeArticleRequests(deps, context, providers, piece)
|
|
25
36
|
: await wrapped.llm({
|
|
@@ -27,7 +38,8 @@ export async function executeProviderRecipe(deps, context, providers, piece) {
|
|
|
27
38
|
model: input.model,
|
|
28
39
|
...(input.thinking === null ? {} : { thinking: input.thinking }),
|
|
29
40
|
thinkingConfig: input.thinkingConfig,
|
|
30
|
-
messages: input.messages,
|
|
41
|
+
messages: picture === undefined ? input.messages : withPicture(input.messages),
|
|
42
|
+
...(picture === undefined ? {} : { images: [picture] }),
|
|
31
43
|
documents: input.documents,
|
|
32
44
|
webSearch: input.webSearch,
|
|
33
45
|
previewLabel: piece.key,
|
|
@@ -52,7 +64,17 @@ export async function executeProviderRecipe(deps, context, providers, piece) {
|
|
|
52
64
|
return "done";
|
|
53
65
|
}
|
|
54
66
|
if (input.kind === "tts") {
|
|
55
|
-
const spoken = await wrapped.tts({
|
|
67
|
+
const spoken = await wrapped.tts({
|
|
68
|
+
provider: input.provider,
|
|
69
|
+
model: input.model,
|
|
70
|
+
voiceId: input.voice,
|
|
71
|
+
text: input.text,
|
|
72
|
+
...(input.dialogue === undefined
|
|
73
|
+
? {}
|
|
74
|
+
: {
|
|
75
|
+
dialogue: input.dialogue.map((line) => ({ voiceId: line.voice, text: line.text })),
|
|
76
|
+
}),
|
|
77
|
+
}, observeNarration(deps.audioPreviews, context.work.projectId, piece.id, narrationLabel(deps, context, piece), { revisionId: context.work.revisionId, workId: context.work.workId, workPieceId: piece.id }));
|
|
56
78
|
if (!spoken.ok)
|
|
57
79
|
return "held";
|
|
58
80
|
const asset = writeAsset(deps, context.work.projectId, "narration.mp3", spoken.value.bytes);
|
|
@@ -75,6 +97,15 @@ export async function executeProviderRecipe(deps, context, providers, piece) {
|
|
|
75
97
|
voice: input.voice,
|
|
76
98
|
...(durationMs === undefined ? {} : { durationMs }),
|
|
77
99
|
}, asset);
|
|
100
|
+
context.emit({
|
|
101
|
+
type: "narration.piece",
|
|
102
|
+
projectId: context.work.projectId,
|
|
103
|
+
key: piece.key,
|
|
104
|
+
durationMs: durationMs ?? null,
|
|
105
|
+
revisionId: context.work.revisionId,
|
|
106
|
+
workId: context.work.workId,
|
|
107
|
+
workPieceId: piece.id,
|
|
108
|
+
});
|
|
78
109
|
deps.count?.("stage.completed", {
|
|
79
110
|
stage: "audio",
|
|
80
111
|
segment: input.segment,
|
|
@@ -89,47 +120,95 @@ export async function executeProviderRecipe(deps, context, providers, piece) {
|
|
|
89
120
|
return "done";
|
|
90
121
|
}
|
|
91
122
|
if (input.kind === "image") {
|
|
92
|
-
const image = await wrapped.image({
|
|
93
|
-
provider: input.provider,
|
|
94
|
-
model: input.model,
|
|
95
|
-
prompt: input.prompt,
|
|
96
|
-
aspect: input.aspect,
|
|
97
|
-
});
|
|
98
|
-
if (!image.ok)
|
|
99
|
-
return "held";
|
|
100
|
-
const asset = writeAsset(deps, context.work.projectId, image.value.mime === "image/jpeg" ? "image.jpg" : "image.png", image.value.bytes);
|
|
101
123
|
const view = executionView(deps, context.work.projectId, context.work.revisionId);
|
|
102
124
|
const index = piece.key.startsWith("image:")
|
|
103
125
|
? (view?.revision.content.imageOrder.indexOf(piece.key.slice(6)) ?? 0) + 1
|
|
104
126
|
: undefined;
|
|
105
|
-
|
|
106
|
-
|
|
127
|
+
// Which thumbnail this is (1-3), or undefined for any other image.
|
|
128
|
+
const variant = thumbnailVariant(piece.key);
|
|
129
|
+
const label = variant !== undefined
|
|
130
|
+
? variant === 1
|
|
131
|
+
? "Thumbnail"
|
|
132
|
+
: `Thumbnail ${String(variant)}`
|
|
133
|
+
: piece.key === referenceKey
|
|
134
|
+
? "Establishing image"
|
|
135
|
+
: index === undefined
|
|
136
|
+
? "Image"
|
|
137
|
+
: `Image ${String(index)}`;
|
|
138
|
+
const softened = await softenIfAsked(deps, context, wrapped, piece, input.prompt, label);
|
|
139
|
+
if (softened === "held")
|
|
140
|
+
return "held";
|
|
141
|
+
const prompt = softened ?? input.prompt;
|
|
142
|
+
const image = await wrapped.image(imageCall(deps, context.work.projectId, { ...input, prompt }, label));
|
|
143
|
+
if (!image.ok)
|
|
144
|
+
return "held";
|
|
145
|
+
const asset = writeAsset(deps, context.work.projectId, image.value.mime === "image/jpeg" ? "image.jpg" : "image.png", image.value.bytes);
|
|
146
|
+
const output = preparedResult(deps, context, piece, variant !== undefined ? "thumbnail" : piece.key === referenceKey ? "reference" : "image", asset, null, {
|
|
147
|
+
prompt,
|
|
107
148
|
provider: input.provider,
|
|
108
149
|
model: input.model,
|
|
109
150
|
...(index === undefined ? {} : { index }),
|
|
151
|
+
// The second and third thumbnails carry their number; the first keeps the meta a
|
|
152
|
+
// thumbnail always had.
|
|
153
|
+
...(variant === undefined || variant === 1 ? {} : { index: variant }),
|
|
110
154
|
});
|
|
111
|
-
await publishResult(deps, context, piece, [output], { prompt
|
|
155
|
+
await publishResult(deps, context, piece, [output], { prompt }, asset);
|
|
156
|
+
if (softened !== undefined)
|
|
157
|
+
clearSoftening(deps.db, context.work.projectId, [piece.key]);
|
|
112
158
|
deps.count?.("stage.completed", {
|
|
113
159
|
stage: context.work.kind,
|
|
114
160
|
provider: input.provider,
|
|
115
161
|
model: input.model,
|
|
116
|
-
...(
|
|
162
|
+
...(variant !== undefined ? { thumbnails: 1 } : { images: 1 }),
|
|
117
163
|
});
|
|
118
164
|
return "done";
|
|
119
165
|
}
|
|
120
|
-
throw new Error("Slopify hit an internal error (this step has no provider request to run). Use
|
|
166
|
+
throw new Error("Slopify hit an internal error (this step has no provider request to run). Use Try again; if it happens again, use Download diagnostics in Settings and report it.");
|
|
167
|
+
}
|
|
168
|
+
// Soften and retry (`soften.ts`): the project's AI model rewords a refused image prompt
|
|
169
|
+
// before the image is drawn again. Undefined when nobody asked, or the project has no model.
|
|
170
|
+
async function softenIfAsked(deps, context, providers, piece, prompt, label) {
|
|
171
|
+
if (!softeningRequested(deps.db, context.work.projectId, piece.key))
|
|
172
|
+
return undefined;
|
|
173
|
+
const llm = executionView(deps, context.work.projectId, context.work.revisionId)?.revision.config
|
|
174
|
+
.llm;
|
|
175
|
+
if (llm === undefined)
|
|
176
|
+
return undefined;
|
|
177
|
+
const answer = await providers.llm({
|
|
178
|
+
provider: llm.provider,
|
|
179
|
+
model: llm.model,
|
|
180
|
+
...(llm.thinking === undefined ? {} : { thinking: llm.thinking }),
|
|
181
|
+
messages: softenMessages(prompt),
|
|
182
|
+
previewLabel: `${label}: softened prompt`,
|
|
183
|
+
check: (value) => softenedPrompt(value.text) === ""
|
|
184
|
+
? "The AI model sent back an empty prompt while softening it. Use Soften and retry again, or reword the prompt yourself in Edit project → Images."
|
|
185
|
+
: undefined,
|
|
186
|
+
});
|
|
187
|
+
return answer.ok ? softenedPrompt(answer.value.text) : "held";
|
|
121
188
|
}
|
|
122
189
|
function checkAnswer(piece, answer) {
|
|
123
190
|
if (answer.text.trim() === "")
|
|
124
|
-
return "The AI model sent back an empty answer. Use
|
|
191
|
+
return "The AI model sent back an empty answer. Use Try again; if it keeps happening, choose another model in the Providers section of Edit project.";
|
|
192
|
+
if (piece.input.kind === "llm" && piece.input.describe !== undefined)
|
|
193
|
+
return spokenPassage(answer.text) === ""
|
|
194
|
+
? "The AI model's description had no words a narrator could say. Use Try again; if it keeps happening, choose another model in the Providers section of Edit project, or turn off Describe tables and figures there."
|
|
195
|
+
: undefined;
|
|
125
196
|
if (piece.key === "research:planner")
|
|
126
197
|
return chaptersFrom(answer.text).length === 0
|
|
127
|
-
? "The AI model's research plan listed no chapters, so research could not go on. Use
|
|
198
|
+
? "The AI model's research plan listed no chapters, so research could not go on. Use Try again; if it keeps happening, choose another model in the Providers section of Edit project."
|
|
128
199
|
: undefined;
|
|
129
200
|
if (piece.key === "research:notes" || piece.key.startsWith("research:chapter:"))
|
|
130
201
|
return sourcedAnswer(piece.key, answer.text);
|
|
202
|
+
// A speaker split that loses or adds words is asked for again rather than narrated.
|
|
203
|
+
if (piece.input.kind === "llm" && piece.input.script?.attribute === true) {
|
|
204
|
+
const checked = parseAttribution(answer.text, attributionSource(piece.input.messages), piece.input.script.speakers);
|
|
205
|
+
return checked.ok ? undefined : checked.reason;
|
|
206
|
+
}
|
|
131
207
|
return undefined;
|
|
132
208
|
}
|
|
209
|
+
function attributionSource(messages) {
|
|
210
|
+
return messages.at(-1)?.content ?? "";
|
|
211
|
+
}
|
|
133
212
|
async function publishText(deps, context, piece, answer) {
|
|
134
213
|
if (piece.input.kind === "llm" && piece.input.preparation !== undefined) {
|
|
135
214
|
const checked = validatePreparation(answer.text, piece.input.preparation.source);
|
|
@@ -143,8 +222,20 @@ async function publishText(deps, context, piece, answer) {
|
|
|
143
222
|
});
|
|
144
223
|
return;
|
|
145
224
|
}
|
|
225
|
+
if (piece.input.kind === "llm" && piece.input.describe !== undefined) {
|
|
226
|
+
// The passage the narration says, as it says it; nothing to download.
|
|
227
|
+
await publishResult(deps, context, piece, [], { text: spokenPassage(answer.text) });
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
146
230
|
if (piece.key === "article:body") {
|
|
147
231
|
const parts = splitEndMatter(answer.text);
|
|
232
|
+
// A script is read before it is kept: one the narration could not speak fails here, with
|
|
233
|
+
// the line to fix, rather than at the narration.
|
|
234
|
+
if (piece.input.kind === "llm" && piece.input.script !== undefined) {
|
|
235
|
+
const checked = parseScript(parts.body, piece.input.script.speakers);
|
|
236
|
+
if (!checked.ok)
|
|
237
|
+
throw new Error(`The text model's script can't be read: ${checked.reason} Try again to have it written again, or fix it in Edit project → Article.`);
|
|
238
|
+
}
|
|
148
239
|
await publishResult(deps, context, piece, preparedTexts(deps, context, piece, [
|
|
149
240
|
["instructions", "instructions.md", frozenInstructions(deps, context, piece)],
|
|
150
241
|
["article_md", "article.md", answer.text],
|
|
@@ -154,6 +245,10 @@ async function publishText(deps, context, piece, answer) {
|
|
|
154
245
|
]), { text: answer.text });
|
|
155
246
|
return;
|
|
156
247
|
}
|
|
248
|
+
if (piece.key === "script:attribute") {
|
|
249
|
+
await publishResult(deps, context, piece, preparedTexts(deps, context, piece, [["script_md", "script.md", answer.text]]), { text: answer.text });
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
157
252
|
if (piece.key === "research:notes") {
|
|
158
253
|
await publishResult(deps, context, piece, preparedTexts(deps, context, piece, [
|
|
159
254
|
["notes", "notes.md", answer.text],
|
|
@@ -177,7 +272,7 @@ async function publishText(deps, context, piece, answer) {
|
|
|
177
272
|
: [];
|
|
178
273
|
const title = titles[Number(piece.key.split(":").at(-1)) - 1];
|
|
179
274
|
if (typeof title !== "string")
|
|
180
|
-
throw new Error("Slopify hit an internal error (a research chapter has no title in the research plan). Use
|
|
275
|
+
throw new Error("Slopify hit an internal error (a research chapter has no title in the research plan). Use Try again; if it happens again, use Download diagnostics in Settings and report it.");
|
|
181
276
|
const asset = writeAsset(deps, context.work.projectId, `research-${piece.key.split(":").at(-1)}.md`, Buffer.from(answer.text));
|
|
182
277
|
await publishResult(deps, context, piece, [], { title, notes: answer.text }, asset);
|
|
183
278
|
return;
|
|
@@ -215,3 +310,23 @@ function checkPreparation(deps, context, piece, answer) {
|
|
|
215
310
|
const prepared = prepareRequests(source, checked.cues, model?.tts.maxCharacters ?? 4000);
|
|
216
311
|
return prepared.ok ? undefined : prepared.reason;
|
|
217
312
|
}
|
|
313
|
+
// A described figure's own picture (`runtime-pictures.ts`), sent only to a text provider that
|
|
314
|
+
// looks at pictures (`reviews/model.ts`). Otherwise the figure is described from its caption
|
|
315
|
+
// and alt text alone.
|
|
316
|
+
function describedPicture(deps, context, input) {
|
|
317
|
+
const named = input.describe?.image;
|
|
318
|
+
if (named === undefined || !imageReviewers.includes(input.provider))
|
|
319
|
+
return undefined;
|
|
320
|
+
const view = executionView(deps, context.work.projectId, context.work.revisionId);
|
|
321
|
+
const path = view === undefined ? undefined : articlePicture(deps, view, named);
|
|
322
|
+
return path === undefined ? undefined : { path, name: basename(path) };
|
|
323
|
+
}
|
|
324
|
+
function withPicture(messages) {
|
|
325
|
+
const last = messages.findLastIndex((message) => message.role === "user");
|
|
326
|
+
return messages.map((message, index) => index === last
|
|
327
|
+
? {
|
|
328
|
+
...message,
|
|
329
|
+
content: `${message.content}\n\nThe picture itself is attached: look at it before you answer.`,
|
|
330
|
+
}
|
|
331
|
+
: message);
|
|
332
|
+
}
|
|
@@ -12,7 +12,11 @@ export function preparedResult(deps, context, piece, role, asset, durationMs = n
|
|
|
12
12
|
slot: role === "image" ||
|
|
13
13
|
role === "short_image" ||
|
|
14
14
|
role === "short_video" ||
|
|
15
|
-
role === "animated_image"
|
|
15
|
+
role === "animated_image" ||
|
|
16
|
+
// Level the volume: one levelled narration per segment.
|
|
17
|
+
role === "audio_levelled" ||
|
|
18
|
+
// The second and third thumbnails; the first keeps the slot it always had.
|
|
19
|
+
(role === "thumbnail" && piece.key !== "thumbnail:image")
|
|
16
20
|
? piece.key
|
|
17
21
|
: `${context.work.kind}:${role}`,
|
|
18
22
|
workKey: piece.key,
|
|
@@ -35,7 +39,7 @@ export function preparedResult(deps, context, piece, role, asset, durationMs = n
|
|
|
35
39
|
export function preparedText(deps, context, piece, role, filename, text) {
|
|
36
40
|
const output = preparedTexts(deps, context, piece, [[role, filename, text]])[0];
|
|
37
41
|
if (output === undefined)
|
|
38
|
-
throw new Error("Slopify hit an internal error (a text file could not be prepared).
|
|
42
|
+
throw new Error("Slopify hit an internal error (a text file could not be prepared). Try again; if it happens again, use Download diagnostics in Settings and report it.");
|
|
39
43
|
return output;
|
|
40
44
|
}
|
|
41
45
|
export function preparedTexts(deps, context, piece, texts) {
|
|
@@ -93,13 +97,17 @@ export async function publishResult(deps, context, piece, outputs, payload, asse
|
|
|
93
97
|
function pieceKind(piece) {
|
|
94
98
|
if (piece.input.kind === "llm" && piece.input.preparation !== undefined)
|
|
95
99
|
return "prompt_written";
|
|
100
|
+
if (piece.input.kind === "llm" && piece.input.describe !== undefined)
|
|
101
|
+
return "prompt_written";
|
|
96
102
|
if (piece.input.kind === "tts")
|
|
97
103
|
return piece.input.segment === "body" ? "chunk" : "segment";
|
|
98
104
|
if (piece.input.kind === "provided" && /^audio:body:.+:\d+$/.test(piece.key))
|
|
99
105
|
return "chunk";
|
|
100
106
|
if (piece.input.kind === "provided" && /^audio:(intro|outro):\d+$/.test(piece.key))
|
|
101
107
|
return "segment";
|
|
102
|
-
if (piece.key.startsWith("image:") ||
|
|
108
|
+
if (piece.key.startsWith("image:") ||
|
|
109
|
+
piece.key.startsWith("animate:") ||
|
|
110
|
+
piece.key.startsWith("figure:card:"))
|
|
103
111
|
return "image";
|
|
104
112
|
if (piece.key.startsWith("research:chapter:"))
|
|
105
113
|
return "chapter";
|
|
@@ -123,6 +131,13 @@ function pieceIndex(deps, context, piece) {
|
|
|
123
131
|
return 1;
|
|
124
132
|
if (piece.key === "entry:outro:text")
|
|
125
133
|
return 2;
|
|
134
|
+
// The cards take places of their own, past the animated images'.
|
|
135
|
+
const card = /^figure:card:(\d+)$/.exec(piece.key);
|
|
136
|
+
if (card !== null)
|
|
137
|
+
return 800000 + Number(card[1]);
|
|
138
|
+
// The narration's descriptions take places of their own, past the preparation steps'.
|
|
139
|
+
if (piece.input.kind === "llm" && piece.input.describe !== undefined)
|
|
140
|
+
return 500000 + piece.input.describe.index;
|
|
126
141
|
// The shorts' steps take places of their own, past every other step of the Video stage:
|
|
127
142
|
// the pick, then per short its prompts, its images and its render.
|
|
128
143
|
const short = /^shorts:(?:(\d+):(prompts|image:(\d+)|render)|pick)$/.exec(piece.key);
|
|
@@ -152,6 +167,7 @@ function pieceIndex(deps, context, piece) {
|
|
|
152
167
|
"narration:files:body",
|
|
153
168
|
"narration:files:intro",
|
|
154
169
|
"narration:files:outro",
|
|
170
|
+
"voices:files",
|
|
155
171
|
];
|
|
156
172
|
const localIndex = localOrder.indexOf(piece.key);
|
|
157
173
|
if (localIndex >= 0)
|
|
@@ -190,10 +206,10 @@ export function retainPartialArticle(deps, context, piece, text) {
|
|
|
190
206
|
if (authority === undefined)
|
|
191
207
|
return;
|
|
192
208
|
if (authority.key !== piece.key || authority.fingerprint !== piece.fingerprint)
|
|
193
|
-
throw new Error("Slopify hit an internal error (a partly written article doesn't match its saved step).
|
|
209
|
+
throw new Error("Slopify hit an internal error (a partly written article doesn't match its saved step). Try again; if it happens again, use Download diagnostics in Settings and report it.");
|
|
194
210
|
const revision = revisionById(deps.db, context.work.projectId, context.work.revisionId);
|
|
195
211
|
if (revision === undefined)
|
|
196
|
-
throw new Error("Slopify hit an internal error (the project version for a partly written article is missing).
|
|
212
|
+
throw new Error("Slopify hit an internal error (the project version for a partly written article is missing). Try again; if it happens again, use Download diagnostics in Settings and report it.");
|
|
197
213
|
insertAsset(deps.db, result.asset);
|
|
198
214
|
insertManifestOutput(deps.db, revision, {
|
|
199
215
|
slot: `article:partial:${piece.id}`,
|