@gentbajko/slopify 3.25.9 → 4.1.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/dist/adapters/account-limits.js +30 -0
- package/dist/adapters/alignment/audio.js +1 -1
- package/dist/adapters/alignment/cache.js +4 -6
- package/dist/adapters/alignment/ctc.js +1 -2
- package/dist/adapters/alignment/lock.js +4 -3
- package/dist/adapters/alignment/multilingual.js +2 -4
- package/dist/adapters/alignment/prefetch.js +2 -1
- package/dist/adapters/alignment/runner.js +1 -1
- package/dist/adapters/alignment/worker.js +3 -2
- package/dist/adapters/explain.js +10 -12
- package/dist/adapters/ffmpeg.js +19 -1
- package/dist/adapters/host-cli/index.js +21 -4
- package/dist/adapters/host-cli/transport.js +2 -1
- package/dist/adapters/http-failure.js +53 -0
- package/dist/adapters/image/bytes.js +2 -1
- package/dist/adapters/image/codex.js +42 -9
- package/dist/adapters/image/fal.js +21 -53
- package/dist/adapters/image/google.js +33 -56
- package/dist/adapters/image/models.js +2 -2
- package/dist/adapters/image/openai.js +22 -48
- package/dist/adapters/image/reference.js +36 -0
- package/dist/adapters/image/replicate.js +21 -49
- package/dist/adapters/image/video.js +2 -1
- package/dist/adapters/interest/search-interest.js +113 -0
- package/dist/adapters/key-probes.js +54 -3
- package/dist/adapters/llm/antigravity-models.js +1 -1
- package/dist/adapters/llm/antigravity.js +11 -6
- package/dist/adapters/llm/assistant-cli.js +388 -0
- package/dist/adapters/llm/child-session.js +83 -0
- package/dist/adapters/llm/claude-code-models.js +52 -99
- package/dist/adapters/llm/claude-code.js +16 -4
- package/dist/adapters/llm/codex-limits.js +27 -68
- package/dist/adapters/llm/codex-models.js +1 -1
- package/dist/adapters/llm/codex-server-models.js +61 -109
- package/dist/adapters/llm/codex.js +12 -1
- package/dist/adapters/llm/document-workspace.js +3 -2
- package/dist/adapters/llm/image-workspace.js +3 -2
- package/dist/adapters/llm/openrouter.js +12 -48
- package/dist/adapters/llm/run-cli.js +9 -24
- package/dist/adapters/llm/sse-lines.js +5 -1
- package/dist/adapters/model-access.js +37 -0
- package/dist/adapters/tts/cartesia.js +15 -46
- package/dist/adapters/tts/elevenlabs.js +20 -52
- package/dist/adapters/tts/gemini.js +21 -41
- package/dist/adapters/tts/inworld-async.js +6 -2
- package/dist/adapters/tts/inworld.js +6 -2
- package/dist/adapters/tts/openai.js +20 -50
- package/dist/adapters/tts/system.js +2 -1
- package/dist/assets/models-lifecycle.yaml +6 -0
- package/dist/assistant-wiring.js +60 -0
- package/dist/autopilot-timers.js +59 -0
- package/dist/catalog/lifecycle.js +52 -0
- package/dist/catalog/propose.js +204 -0
- package/dist/catalog/registry.js +23 -14
- package/dist/catalog/schema.js +4 -1
- package/dist/catalog/store.js +39 -0
- package/dist/catalog/validate.js +5 -2
- package/dist/edge/autostart/native.js +3 -5
- package/dist/edge/autostart/prompt.js +2 -1
- package/dist/edge/autostart/service.js +3 -4
- package/dist/edge/cli-args.js +3 -4
- package/dist/edge/cli.js +2 -1
- package/dist/edge/docker-install/apply.js +11 -11
- package/dist/edge/docker-install/run.js +2 -1
- package/dist/edge/docker-install/state.js +4 -3
- package/dist/edge/docker-install/tree.js +2 -4
- package/dist/edge/docker-install/volume.js +3 -2
- package/dist/edge/docker.js +5 -4
- package/dist/edge/events/hub.js +5 -5
- package/dist/edge/http/actions.js +11 -32
- package/dist/edge/http/actor.js +5 -3
- package/dist/edge/http/app.js +86 -11
- package/dist/edge/http/assistant.js +129 -0
- package/dist/edge/http/audio-preview.js +4 -11
- package/dist/edge/http/auditions.js +1 -1
- package/dist/edge/http/autostart.js +5 -5
- package/dist/edge/http/backups.js +2 -4
- package/dist/edge/http/channel-memory.js +9 -22
- package/dist/edge/http/channels.js +14 -27
- package/dist/edge/http/checkpoints.js +39 -21
- package/dist/edge/http/design-templates.js +167 -0
- package/dist/edge/http/designs.js +178 -0
- package/dist/edge/http/diagnostics.js +5 -1
- package/dist/edge/http/document-themes.js +5 -14
- package/dist/edge/http/draft-files.js +1 -3
- package/dist/edge/http/draft-problem.js +5 -10
- package/dist/edge/http/drafts.js +3 -3
- package/dist/edge/http/entries.js +6 -9
- package/dist/edge/http/extension-auth.js +27 -0
- package/dist/edge/http/external-reviews.js +12 -11
- package/dist/edge/http/files.js +5 -11
- package/dist/edge/http/folder-location.js +1 -4
- package/dist/edge/http/fonts.js +0 -7
- package/dist/edge/http/home.js +47 -14
- package/dist/edge/http/host-cli.js +18 -8
- package/dist/edge/http/ids.js +8 -0
- package/dist/edge/http/keywords.js +35 -0
- package/dist/edge/http/model-checks.js +132 -0
- package/dist/edge/http/narration-peaks.js +5 -13
- package/dist/edge/http/onboarding.js +30 -26
- package/dist/edge/http/open-folder.js +5 -10
- package/dist/edge/http/outputs.js +7 -14
- package/dist/edge/http/patch-notes.js +6 -12
- package/dist/edge/http/planning.js +3 -7
- package/dist/edge/http/problem.js +2 -4
- package/dist/edge/http/project-create.js +2 -1
- package/dist/edge/http/project-templates.js +31 -19
- package/dist/edge/http/projects.js +24 -16
- package/dist/edge/http/prompts.js +9 -16
- package/dist/edge/http/pronunciations.js +1 -2
- package/dist/edge/http/providers.js +48 -34
- package/dist/edge/http/refusal.js +29 -0
- package/dist/edge/http/remote-access.js +40 -0
- package/dist/edge/http/remote-guard.js +71 -0
- package/dist/edge/http/request-guard.js +91 -0
- package/dist/edge/http/reviews.js +12 -25
- package/dist/edge/http/revision-files.js +6 -11
- package/dist/edge/http/revisions.js +14 -19
- package/dist/edge/http/run-cost.js +1 -2
- package/dist/edge/http/same-page.js +80 -0
- package/dist/edge/http/schedules.js +69 -40
- package/dist/edge/http/settings.js +27 -14
- package/dist/edge/http/social.js +283 -0
- package/dist/edge/http/staging.js +3 -5
- package/dist/edge/http/storage-files.js +5 -7
- package/dist/edge/http/storage.js +33 -25
- package/dist/edge/http/studio.js +94 -69
- package/dist/edge/http/style-preview.js +3 -6
- package/dist/edge/http/subtitles.js +3 -17
- package/dist/edge/http/template-studio.js +436 -0
- package/dist/edge/http/trash.js +11 -12
- package/dist/edge/http/tutorial.js +1 -2
- package/dist/edge/http/tutorials.js +5 -11
- package/dist/edge/http/update.js +7 -13
- package/dist/edge/http/waveform.js +3 -6
- package/dist/edge/http/youtube-edits.js +3 -4
- package/dist/edge/mcp/assistant.js +279 -0
- package/dist/edge/mcp/attribution.js +8 -2
- package/dist/edge/mcp/call-route.js +11 -2
- package/dist/edge/mcp/excluded-routes.js +29 -3
- package/dist/edge/mcp/http.js +11 -2
- package/dist/edge/mcp/instructions.js +6 -4
- package/dist/edge/mcp/library-tools.js +9 -5
- package/dist/edge/mcp/remake-tools.js +7 -4
- package/dist/edge/mcp/route-tool.js +35 -15
- package/dist/edge/mcp/server.js +13 -8
- package/dist/edge/mcp/tools-channels.js +11 -9
- package/dist/edge/mcp/tools-designs.js +165 -0
- package/dist/edge/mcp/tools-library.js +19 -7
- package/dist/edge/mcp/tools-planning.js +40 -21
- package/dist/edge/mcp/tools-projects.js +38 -11
- package/dist/edge/mcp/tools-publishing.js +9 -9
- package/dist/edge/mcp/tools-runs.js +23 -10
- package/dist/edge/mcp/tools-schedules.js +32 -8
- package/dist/edge/mcp/tools-setup.js +75 -11
- package/dist/edge/mcp/tools-social.js +47 -0
- package/dist/edge/mcp/tools-storage.js +11 -3
- package/dist/edge/mcp/tools-template-studio.js +166 -0
- package/dist/edge/mcp/tools.js +6 -0
- package/dist/edge/remote-access/cli.js +71 -0
- package/dist/edge/remote-access/model.js +32 -0
- package/dist/edge/remote-access/rules.js +207 -0
- package/dist/edge/remote-access/service.js +203 -0
- package/dist/edge/remote-access/store.js +44 -0
- package/dist/edge/remote-access/views.js +43 -0
- package/dist/extension/slopify-studio-chrome.zip +0 -0
- package/dist/extension/slopify-studio-firefox.zip +0 -0
- package/dist/host-cli/install.js +2 -2
- package/dist/host-cli/open-folder.js +2 -2
- package/dist/host-cli/paths.js +2 -4
- package/dist/host-cli/server.js +4 -4
- package/dist/host-cli/service.js +3 -3
- package/dist/host-cli/status.js +7 -7
- package/dist/kernel/db/migrations/0063-model-checks.sql +20 -0
- package/dist/kernel/db/migrations/0064-template-studio.sql +46 -0
- package/dist/kernel/db/migrations/0065-promise-and-assistant-reviews.sql +58 -0
- package/dist/kernel/db/migrations/0066-assistant.sql +44 -0
- package/dist/kernel/db/migrations/0067-autopilot.sql +6 -0
- package/dist/kernel/db/migrations/0068-topic-scores.sql +17 -0
- package/dist/kernel/db/migrations/0070-designs.sql +53 -0
- package/dist/kernel/db/migrations/0071-design-usage.sql +33 -0
- package/dist/kernel/db/migrations/0072-design-templates.sql +33 -0
- package/dist/kernel/db/migrations/0073-schedule-design-kind.sql +5 -0
- package/dist/kernel/db/migrations/0074-template-bakeoffs.sql +44 -0
- package/dist/kernel/db/migrations/0075-social-posts.sql +25 -0
- package/dist/kernel/db/migrations/0076-channel-social.sql +5 -0
- package/dist/kernel/db/migrations/0077-post-prompts.sql +17 -0
- package/dist/kernel/db/migrations/0078-uploaded-from-links.sql +7 -0
- package/dist/kernel/db/tx.js +2 -4
- package/dist/kernel/errors.js +27 -1
- package/dist/kernel/json-answer.js +15 -0
- package/dist/kernel/lock.js +4 -9
- package/dist/kernel/paths.js +8 -0
- package/dist/kernel/pipeline.js +12 -0
- package/dist/kernel/ports/assistant-cli.js +4 -0
- package/dist/kernel/ports/host-cli.js +3 -0
- package/dist/kernel/ports/interest.js +4 -0
- package/dist/kernel/ports/languages.js +0 -3
- package/dist/kernel/ports/model.js +2 -0
- package/dist/kernel/ports/settings-places.js +31 -0
- package/dist/kernel/ports/system-speech.js +31 -3
- package/dist/kernel/runner/attempt.js +14 -4
- package/dist/kernel/runner/audition.js +3 -1
- package/dist/kernel/runner/checkpoint-authority.js +2 -1
- package/dist/kernel/runner/index.js +8 -10
- package/dist/kernel/runner/model-access.js +40 -0
- package/dist/kernel/runner/model-check.js +153 -0
- package/dist/kernel/runner/providers.js +18 -12
- package/dist/kernel/runner/standalone.js +11 -7
- package/dist/main.js +338 -21
- package/dist/model-catalog.js +20 -0
- package/dist/model-checks.js +166 -0
- package/dist/patch-notes/4.0.0.md +51 -0
- package/dist/patch-notes/4.1.0.md +42 -0
- package/dist/patch-notes/index.json +12 -0
- package/dist/slices/admission/heads.js +2 -1
- package/dist/slices/admission/listing-facts.js +95 -0
- package/dist/slices/admission/repo.js +11 -7
- package/dist/slices/admission/rules.js +50 -15
- package/dist/slices/admission/schema.js +8 -0
- package/dist/slices/admission/stage-writes.js +0 -3
- package/dist/slices/admission/start.js +48 -31
- package/dist/slices/admission/state-words.js +34 -5
- package/dist/slices/admission/substitute.js +7 -4
- package/dist/slices/agents/attention-posts.js +31 -0
- package/dist/slices/agents/attention-templates.js +33 -0
- package/dist/slices/agents/attention.js +10 -1
- package/dist/slices/agents/connection.js +2 -6
- package/dist/slices/agents/model.js +6 -3
- package/dist/slices/agents/repo.js +15 -7
- package/dist/slices/article/continuation.js +0 -63
- package/dist/slices/article/plain.js +3 -2
- package/dist/slices/article/segments.js +2 -62
- package/dist/slices/assistant/access.js +1 -0
- package/dist/slices/assistant/asks.js +80 -0
- package/dist/slices/assistant/cost.js +54 -0
- package/dist/slices/assistant/gate.js +222 -0
- package/dist/slices/assistant/model.js +86 -0
- package/dist/slices/assistant/names.js +23 -0
- package/dist/slices/assistant/prompt.js +74 -0
- package/dist/slices/assistant/repo.js +74 -0
- package/dist/slices/assistant/tokens.js +18 -0
- package/dist/slices/assistant/turn.js +344 -0
- package/dist/slices/autopilot/buffer.js +76 -0
- package/dist/slices/autopilot/model.js +14 -0
- package/dist/slices/autopilot/notify.js +27 -0
- package/dist/slices/autopilot/repo.js +34 -0
- package/dist/slices/autopilot/signals.js +194 -0
- package/dist/slices/autopilot/warnings.js +85 -0
- package/dist/slices/autopilot/weekly.js +100 -0
- package/dist/slices/backups/files.js +2 -6
- package/dist/slices/backups/repo.js +3 -15
- package/dist/slices/backups/service.js +8 -7
- package/dist/slices/channels/cast-images.js +3 -2
- package/dist/slices/channels/model.js +0 -1
- package/dist/slices/channels/rebrand.js +0 -3
- package/dist/slices/channels/repo.js +4 -0
- package/dist/slices/channels/schema.js +7 -1
- package/dist/slices/channels/service.js +10 -0
- package/dist/slices/channels/videos.js +3 -15
- package/dist/slices/checkpoints/index.js +0 -1
- package/dist/slices/control/providers.js +2 -28
- package/dist/slices/designs/artwork.js +66 -0
- package/dist/slices/designs/budget.js +23 -0
- package/dist/slices/designs/calls.js +124 -0
- package/dist/slices/designs/check-context.js +38 -0
- package/dist/slices/designs/check-text.js +43 -0
- package/dist/slices/designs/fields.js +88 -0
- package/dist/slices/designs/frames.js +34 -0
- package/dist/slices/designs/gate.js +130 -0
- package/dist/slices/designs/layout.js +293 -0
- package/dist/slices/designs/model.js +35 -0
- package/dist/slices/designs/pdf-job.js +23 -0
- package/dist/slices/designs/pdf-pages.js +30 -0
- package/dist/slices/designs/pdf-runner.js +68 -0
- package/dist/slices/designs/pdf-worker.js +24 -0
- package/dist/slices/designs/picture-size.js +79 -0
- package/dist/slices/designs/pictures.js +55 -0
- package/dist/slices/designs/quote.js +24 -0
- package/dist/slices/designs/render-args.js +36 -0
- package/dist/slices/designs/render-input.js +59 -0
- package/dist/slices/designs/render.js +215 -0
- package/dist/slices/designs/repo.js +101 -0
- package/dist/slices/designs/requests.js +80 -0
- package/dist/slices/designs/runner.js +239 -0
- package/dist/slices/designs/sample-repo.js +91 -0
- package/dist/slices/designs/sample.js +223 -0
- package/dist/slices/designs/schema.js +87 -0
- package/dist/slices/designs/sections.js +44 -0
- package/dist/slices/designs/service.js +233 -0
- package/dist/slices/designs/steps-repo.js +102 -0
- package/dist/slices/designs/studio-kind.js +39 -0
- package/dist/slices/designs/template-checks.js +44 -0
- package/dist/slices/designs/template-make.js +118 -0
- package/dist/slices/designs/templates.js +218 -0
- package/dist/slices/document/fonts.js +2 -1
- package/dist/slices/document/model.js +7 -4
- package/dist/slices/document/render.js +2 -1
- package/dist/slices/document/theme-schema.js +1 -1
- package/dist/slices/document/theme.js +49 -3
- package/dist/slices/episodes/summarize.js +6 -2
- package/dist/slices/estimate/index.js +72 -28
- package/dist/slices/estimate/requests.js +4 -4
- package/dist/slices/fixes/rules.js +3 -1
- package/dist/slices/fonts/catalog.js +5 -3
- package/dist/slices/fonts/coverage.js +2 -1
- package/dist/slices/fonts/discovery.js +3 -2
- package/dist/slices/fonts/files.js +2 -6
- package/dist/slices/fonts/index.js +0 -1
- package/dist/slices/fonts/sfnt.js +45 -10
- package/dist/slices/images/appearance.js +8 -16
- package/dist/slices/images/scenes.js +102 -28
- package/dist/slices/keywords/digest.js +85 -0
- package/dist/slices/keywords/facts.js +92 -0
- package/dist/slices/keywords/fill.js +80 -0
- package/dist/slices/keywords/model.js +75 -0
- package/dist/slices/keywords/rules.js +187 -0
- package/dist/slices/keywords/schema.js +28 -0
- package/dist/slices/keywords/specs.js +260 -0
- package/dist/slices/keywords/values.js +74 -0
- package/dist/slices/library/lint.js +29 -12
- package/dist/slices/library/model.js +2 -0
- package/dist/slices/library/photorealistic.js +4 -12
- package/dist/slices/library/rename.js +9 -7
- package/dist/slices/library/save.js +14 -10
- package/dist/slices/library/slots.js +37 -11
- package/dist/slices/library/transfer.js +5 -4
- package/dist/slices/library/used-by.js +25 -10
- package/dist/slices/loudness/loudnorm.js +53 -12
- package/dist/slices/loudness/model.js +3 -2
- package/dist/slices/loudness/sentence-level.js +2 -1
- package/dist/slices/model-checks/cost.js +61 -0
- package/dist/slices/model-checks/listing.js +124 -0
- package/dist/slices/model-checks/model.js +20 -0
- package/dist/slices/model-checks/record.js +32 -0
- package/dist/slices/model-checks/repo.js +120 -0
- package/dist/slices/model-checks/rules.js +125 -0
- package/dist/slices/model-checks/run.js +143 -0
- package/dist/slices/model-checks/service.js +198 -0
- package/dist/slices/model-upkeep/model.js +1 -1
- package/dist/slices/model-upkeep/switch.js +72 -27
- package/dist/slices/model-upkeep/usage.js +39 -13
- package/dist/slices/narration/breaths.js +3 -1
- package/dist/slices/narration/clean-pieces.js +3 -1
- package/dist/slices/narration/concat.js +2 -1
- package/dist/slices/narration/pauses-model.js +3 -2
- package/dist/slices/narration/peaks.js +3 -4
- package/dist/slices/narration/pronunciation-chunks.js +4 -3
- package/dist/slices/narration/pronunciation.js +6 -4
- package/dist/slices/narration/steering.js +4 -3
- package/dist/slices/notifications/notifier.js +4 -3
- package/dist/slices/notifications/rules.js +5 -2
- package/dist/slices/notifications/settings.js +4 -12
- package/dist/slices/notifications/subject.js +4 -3
- package/dist/slices/onboarding/copy.js +21 -3
- package/dist/slices/onboarding/first-run.js +16 -40
- package/dist/slices/onboarding/full-video.js +3 -2
- package/dist/slices/onboarding/install.js +6 -4
- package/dist/slices/onboarding/model.js +26 -37
- package/dist/slices/onboarding/packs.js +22 -22
- package/dist/slices/onboarding/preferred-model.js +14 -0
- package/dist/slices/onboarding/quick-short.js +105 -65
- package/dist/slices/onboarding/sample.js +4 -2
- package/dist/slices/onboarding/state.js +9 -11
- package/dist/slices/onboarding/template.js +8 -6
- package/dist/slices/outputs/add.js +3 -2
- package/dist/slices/outputs/service.js +2 -2
- package/dist/slices/patch-notes/library.js +4 -3
- package/dist/slices/play-drafts/convert.js +4 -0
- package/dist/slices/play-drafts/readiness.js +3 -3
- package/dist/slices/play-drafts/review-inputs.js +9 -3
- package/dist/slices/play-drafts/review.js +5 -4
- package/dist/slices/play-drafts/schema.js +4 -0
- package/dist/slices/play-drafts/service.js +17 -7
- package/dist/slices/project-templates/from-project.js +9 -2
- package/dist/slices/project-templates/next-chapter.js +2 -1
- package/dist/slices/project-templates/provider-defaults.js +22 -0
- package/dist/slices/project-templates/repo.js +6 -1
- package/dist/slices/project-templates/setup-sections.js +26 -0
- package/dist/slices/project-templates/setup.js +14 -1
- package/dist/slices/project-templates/stand-ins.js +22 -0
- package/dist/slices/rebuild/activity.js +2 -0
- package/dist/slices/rebuild/admission-repo.js +3 -2
- package/dist/slices/rebuild/narration-history.js +4 -5
- package/dist/slices/rebuild/narration-retry.js +2 -1
- package/dist/slices/rebuild/preview-details.js +32 -14
- package/dist/slices/rebuild/preview-plan.js +3 -8
- package/dist/slices/rebuild/recipe-build.js +8 -3
- package/dist/slices/rebuild/recipe-describe.js +3 -3
- package/dist/slices/rebuild/recipe-digest.js +58 -0
- package/dist/slices/rebuild/recipe-input-schema.js +10 -0
- package/dist/slices/rebuild/recipe-keywords.js +71 -0
- package/dist/slices/rebuild/recipe-legacy.js +5 -1
- package/dist/slices/rebuild/recipe-model.js +4 -0
- package/dist/slices/rebuild/recipe-narration-text.js +2 -1
- package/dist/slices/rebuild/recipe-preparation.js +4 -4
- package/dist/slices/rebuild/recipe-reference.js +8 -4
- package/dist/slices/rebuild/recipe-reviews.js +27 -7
- package/dist/slices/rebuild/recipe-save.js +8 -5
- package/dist/slices/rebuild/recipe-scenes.js +40 -11
- package/dist/slices/rebuild/recipe-shorts.js +8 -8
- package/dist/slices/rebuild/recipe-social.js +46 -0
- package/dist/slices/rebuild/recipe-text.js +64 -20
- package/dist/slices/rebuild/recipe-validation.js +102 -31
- package/dist/slices/rebuild/recipe-visual.js +55 -16
- package/dist/slices/rebuild/recipe-voices.js +3 -2
- package/dist/slices/rebuild/recipe-work.js +19 -7
- package/dist/slices/rebuild/recipe-youtube.js +4 -3
- package/dist/slices/rebuild/recovery-selection.js +3 -1
- package/dist/slices/rebuild/recovery.js +9 -8
- package/dist/slices/rebuild/remake-outdated.js +5 -5
- package/dist/slices/rebuild/repo.js +4 -3
- package/dist/slices/rebuild/review-promise.js +25 -0
- package/dist/slices/rebuild/review-redo.js +3 -2
- package/dist/slices/rebuild/runtime-admission.js +4 -3
- package/dist/slices/rebuild/runtime-agent-review.js +11 -5
- package/dist/slices/rebuild/runtime-animate.js +10 -9
- package/dist/slices/rebuild/runtime-article.js +7 -5
- package/dist/slices/rebuild/runtime-checkpoints.js +5 -4
- package/dist/slices/rebuild/runtime-document.js +11 -13
- package/dist/slices/rebuild/runtime-export-bed.js +8 -12
- package/dist/slices/rebuild/runtime-export-edit.js +8 -16
- package/dist/slices/rebuild/runtime-export-inputs.js +12 -11
- package/dist/slices/rebuild/runtime-export-short.js +9 -5
- package/dist/slices/rebuild/runtime-export.js +38 -15
- package/dist/slices/rebuild/runtime-figure-card.js +2 -1
- package/dist/slices/rebuild/runtime-image.js +8 -12
- package/dist/slices/rebuild/runtime-instructions.js +2 -0
- package/dist/slices/rebuild/runtime-keywords.js +59 -0
- package/dist/slices/rebuild/runtime-lines.js +2 -11
- package/dist/slices/rebuild/runtime-local.js +17 -22
- package/dist/slices/rebuild/runtime-narration-publication.js +2 -5
- package/dist/slices/rebuild/runtime-narration-reuse.js +9 -13
- package/dist/slices/rebuild/runtime-narration-text.js +13 -17
- package/dist/slices/rebuild/runtime-pictures.js +2 -4
- package/dist/slices/rebuild/runtime-piece-label.js +10 -6
- package/dist/slices/rebuild/runtime-plan.js +31 -5
- package/dist/slices/rebuild/runtime-provider.js +67 -25
- package/dist/slices/rebuild/runtime-publication.js +10 -9
- package/dist/slices/rebuild/runtime-review.js +39 -33
- package/dist/slices/rebuild/runtime-run.js +4 -1
- package/dist/slices/rebuild/runtime-shorts.js +31 -50
- package/dist/slices/rebuild/runtime-store.js +20 -10
- package/dist/slices/rebuild/runtime-subtitles.js +28 -25
- package/dist/slices/rebuild/runtime-voices.js +10 -16
- package/dist/slices/rebuild/runtime-youtube.js +14 -31
- package/dist/slices/rebuild/service-readiness.js +11 -3
- package/dist/slices/rebuild/transition-repo.js +6 -5
- package/dist/slices/rebuild/work-records.js +6 -0
- package/dist/slices/reruns/cascade.js +1 -2
- package/dist/slices/reruns/index.js +6 -9
- package/dist/slices/research/synthesis.js +3 -2
- package/dist/slices/reviews/external-model.js +4 -1
- package/dist/slices/reviews/external-repo.js +2 -1
- package/dist/slices/reviews/external-rows.js +2 -3
- package/dist/slices/reviews/holds.js +5 -4
- package/dist/slices/reviews/model.js +32 -11
- package/dist/slices/reviews/repo.js +2 -1
- package/dist/slices/reviews/rules.js +4 -3
- package/dist/slices/reviews/verdict.js +2 -1
- package/dist/slices/reviews/view.js +5 -2
- package/dist/slices/revisions/downloads.js +4 -5
- package/dist/slices/revisions/image-plan.js +1 -1
- package/dist/slices/revisions/mutation-assets.js +9 -5
- package/dist/slices/revisions/mutation-cues.js +2 -5
- package/dist/slices/revisions/mutations.js +3 -3
- package/dist/slices/revisions/repo.js +3 -1
- package/dist/slices/revisions/schema.js +1 -0
- package/dist/slices/revisions/view.js +20 -0
- package/dist/slices/run-cost/limits.js +5 -2
- package/dist/slices/run-cost/meter.js +12 -5
- package/dist/slices/run-cost/panel.js +8 -74
- package/dist/slices/run-cost/plan-readings.js +127 -0
- package/dist/slices/run-cost/plan-use.js +169 -0
- package/dist/slices/run-cost/pricing.js +3 -3
- package/dist/slices/run-cost/week.js +32 -4
- package/dist/slices/schedules/agenda.js +18 -7
- package/dist/slices/schedules/by-date.js +42 -0
- package/dist/slices/schedules/calendar.js +5 -0
- package/dist/slices/schedules/design-agenda.js +36 -0
- package/dist/slices/schedules/design-check.js +41 -0
- package/dist/slices/schedules/design-run.js +126 -0
- package/dist/slices/schedules/next-run.js +62 -0
- package/dist/slices/schedules/prepare.js +18 -2
- package/dist/slices/schedules/repo.js +30 -21
- package/dist/slices/schedules/rescore.js +35 -0
- package/dist/slices/schedules/scheduler.js +13 -3
- package/dist/slices/schedules/schema.js +57 -3
- package/dist/slices/schedules/score.js +107 -0
- package/dist/slices/schedules/service.js +57 -24
- package/dist/slices/schedules/topic-list.js +15 -0
- package/dist/slices/schedules/topic-scores.js +104 -0
- package/dist/slices/schedules/topics.js +53 -41
- package/dist/slices/settings/cli-paths.js +4 -6
- package/dist/slices/settings/first-run.js +8 -43
- package/dist/slices/settings/health.js +30 -15
- package/dist/slices/settings/key-test.js +1 -1
- package/dist/slices/settings/model-reach.js +3 -17
- package/dist/slices/settings/model.js +14 -2
- package/dist/slices/settings/playback.js +34 -23
- package/dist/slices/settings/probe-read.js +16 -0
- package/dist/slices/settings/repo.js +33 -0
- package/dist/slices/settings/tutorial.js +2 -2
- package/dist/slices/settings/voices.js +4 -12
- package/dist/slices/shorts/keep.js +2 -1
- package/dist/slices/shorts/pick.js +6 -18
- package/dist/slices/shorts/prompts.js +4 -14
- package/dist/slices/shorts/render.js +6 -2
- package/dist/slices/social/channel.js +44 -0
- package/dist/slices/social/defaults.js +17 -0
- package/dist/slices/social/due.js +36 -0
- package/dist/slices/social/extension.js +18 -0
- package/dist/slices/social/fit.js +115 -0
- package/dist/slices/social/limits.js +186 -0
- package/dist/slices/social/links.js +21 -0
- package/dist/slices/social/media.js +132 -0
- package/dist/slices/social/model.js +75 -0
- package/dist/slices/social/notice.js +19 -0
- package/dist/slices/social/pack.js +176 -0
- package/dist/slices/social/post-prompt.js +56 -0
- package/dist/slices/social/queue.js +31 -0
- package/dist/slices/social/repo.js +133 -0
- package/dist/slices/social/reshape.js +97 -0
- package/dist/slices/social/rules.js +5 -0
- package/dist/slices/social/schema.js +70 -0
- package/dist/slices/social/serve.js +51 -0
- package/dist/slices/social/service.js +129 -0
- package/dist/slices/social/settings.js +21 -0
- package/dist/slices/social/sources.js +114 -0
- package/dist/slices/social/state.js +110 -0
- package/dist/slices/social/text.js +149 -0
- package/dist/slices/storage/arrange.js +3 -1
- package/dist/slices/storage/assets.js +8 -10
- package/dist/slices/storage/backup-designs.js +74 -0
- package/dist/slices/storage/backup-export.js +8 -3
- package/dist/slices/storage/backup-format.js +10 -0
- package/dist/slices/storage/backup-import.js +22 -4
- package/dist/slices/storage/delete-project.js +6 -5
- package/dist/slices/storage/files-location.js +6 -8
- package/dist/slices/storage/layout.js +13 -2
- package/dist/slices/storage/model.js +3 -0
- package/dist/slices/storage/portable.js +17 -12
- package/dist/slices/storage/schema.js +2 -0
- package/dist/slices/storage/staging-refs.js +2 -1
- package/dist/slices/storage/staging.js +6 -9
- package/dist/slices/storage/trim.js +26 -9
- package/dist/slices/studio/calendar.js +7 -2
- package/dist/slices/studio/disclosure.js +4 -4
- package/dist/slices/studio/model.js +1 -1
- package/dist/slices/studio/pack.js +9 -8
- package/dist/slices/studio/pick.js +2 -11
- package/dist/slices/studio/plan.js +7 -16
- package/dist/slices/studio/queue.js +3 -14
- package/dist/slices/studio/releases.js +26 -0
- package/dist/slices/studio/settings.js +20 -57
- package/dist/slices/studio/videos.js +35 -4
- package/dist/slices/style-preview/render.js +37 -16
- package/dist/slices/style-preview/schema.js +14 -1
- package/dist/slices/style-preview/service.js +2 -1
- package/dist/slices/style-preview/settings.js +33 -5
- package/dist/slices/subtitles/captions.js +1 -1
- package/dist/slices/telemetry/collector-client.js +2 -4
- package/dist/slices/telemetry/flush.js +2 -4
- package/dist/slices/telemetry/model.js +0 -7
- package/dist/slices/telemetry/record.js +10 -3
- package/dist/slices/template-studio/anatomy-view.js +161 -0
- package/dist/slices/template-studio/anatomy.js +73 -0
- package/dist/slices/template-studio/bakeoff-make.js +225 -0
- package/dist/slices/template-studio/bakeoff-repo.js +83 -0
- package/dist/slices/template-studio/bakeoff.js +333 -0
- package/dist/slices/template-studio/budget.js +106 -0
- package/dist/slices/template-studio/cards.js +78 -0
- package/dist/slices/template-studio/checks.js +322 -0
- package/dist/slices/template-studio/document-sample.js +56 -0
- package/dist/slices/template-studio/edit.js +15 -0
- package/dist/slices/template-studio/for-all.js +161 -0
- package/dist/slices/template-studio/free-video.js +31 -0
- package/dist/slices/template-studio/free.js +176 -0
- package/dist/slices/template-studio/gate.js +40 -0
- package/dist/slices/template-studio/hidden.js +20 -0
- package/dist/slices/template-studio/jobs.js +79 -0
- package/dist/slices/template-studio/loudness.js +111 -0
- package/dist/slices/template-studio/make.js +40 -0
- package/dist/slices/template-studio/model.js +58 -0
- package/dist/slices/template-studio/pack-import.js +253 -0
- package/dist/slices/template-studio/pack-samples.js +60 -0
- package/dist/slices/template-studio/pack.js +207 -0
- package/dist/slices/template-studio/paid-view.js +66 -0
- package/dist/slices/template-studio/paid.js +355 -0
- package/dist/slices/template-studio/quote.js +155 -0
- package/dist/slices/template-studio/ready.js +104 -0
- package/dist/slices/template-studio/repo.js +134 -0
- package/dist/slices/template-studio/reviews-path.js +58 -0
- package/dist/slices/template-studio/runner.js +84 -0
- package/dist/slices/template-studio/save.js +236 -0
- package/dist/slices/template-studio/say-it-as.js +248 -0
- package/dist/slices/template-studio/schedule-gate.js +34 -0
- package/dist/slices/template-studio/schedule-price.js +32 -0
- package/dist/slices/template-studio/schedule-sample.js +91 -0
- package/dist/slices/template-studio/schema.js +243 -0
- package/dist/slices/template-studio/scope.js +124 -0
- package/dist/slices/template-studio/sections.js +176 -0
- package/dist/slices/template-studio/service.js +175 -0
- package/dist/slices/template-studio/status.js +191 -0
- package/dist/slices/template-studio/topic.js +223 -0
- package/dist/slices/thumbnail/by-llm.js +1 -1
- package/dist/slices/trash/designs.js +76 -0
- package/dist/slices/trash/model.js +13 -4
- package/dist/slices/trash/service.js +52 -2
- package/dist/slices/tutorials/library.js +3 -2
- package/dist/slices/uploads/repo.js +37 -1
- package/dist/slices/uploads/rules.js +3 -2
- package/dist/slices/video/ass-text.js +17 -0
- package/dist/slices/video/audio-export.js +2 -64
- package/dist/slices/video/edit-list.js +2 -2
- package/dist/slices/video/edit-settings.js +1 -1
- package/dist/slices/video/encoding.js +169 -0
- package/dist/slices/video/ffmpeg.js +23 -21
- package/dist/slices/video/figure-card.js +1 -15
- package/dist/slices/video/plan.js +2 -1
- package/dist/slices/video/slideshow.js +43 -18
- package/dist/slices/voices/attribution.js +3 -2
- package/dist/slices/voices/model.js +9 -8
- package/dist/slices/voices/palette.js +0 -3
- package/dist/slices/voices/script.js +2 -1
- package/dist/slices/youtube/answer.js +4 -16
- package/dist/slices/youtube/edits-repo.js +3 -12
- package/dist/tutorials/AI-CLIs.md +25 -25
- package/dist/tutorials/Agents-MCP.md +34 -33
- package/dist/tutorials/Assistant.md +42 -0
- package/dist/tutorials/Backups.md +7 -7
- package/dist/tutorials/Calendar.md +17 -25
- package/dist/tutorials/Cast-Library.md +19 -19
- package/dist/tutorials/Channels.md +42 -35
- package/dist/tutorials/Costs-and-Run-Cost.md +38 -40
- package/dist/tutorials/Create-Article.md +114 -0
- package/dist/tutorials/Create-Extras.md +112 -0
- package/dist/tutorials/Create-Images.md +165 -0
- package/dist/tutorials/Create-Narration.md +178 -0
- package/dist/tutorials/Create-Overview.md +244 -0
- package/dist/tutorials/Create-Video-and-Style.md +144 -0
- package/dist/tutorials/Docker.md +3 -3
- package/dist/tutorials/Document-Themes.md +32 -32
- package/dist/tutorials/Editing-a-Project.md +94 -94
- package/dist/tutorials/FAQ.md +18 -18
- package/dist/tutorials/First-Launch-and-Welcome.md +50 -64
- package/dist/tutorials/Home-and-Projects.md +115 -71
- package/dist/tutorials/Home.md +33 -33
- package/dist/tutorials/Install.md +14 -13
- package/dist/tutorials/Intros-and-Outros.md +33 -34
- package/dist/tutorials/Keyboard-Shortcuts-and-Command-Palette.md +62 -58
- package/dist/tutorials/Keywords-Slopify-Fills.md +87 -0
- package/dist/tutorials/Models.md +53 -21
- package/dist/tutorials/Multiple-Voices.md +41 -40
- package/dist/tutorials/Narration-Aliases-and-Glossary.md +14 -14
- package/dist/tutorials/Notifications.md +27 -17
- package/dist/tutorials/Other-Languages.md +15 -15
- package/dist/tutorials/PDF-Documents.md +38 -29
- package/dist/tutorials/Posting-to-Social-Sites.md +121 -0
- package/dist/tutorials/Project-Page.md +118 -120
- package/dist/tutorials/Prompts-and-Templates.md +84 -0
- package/dist/tutorials/Prompts.md +48 -44
- package/dist/tutorials/Providers-and-Keys.md +105 -98
- package/dist/tutorials/Publishing-to-YouTube.md +31 -31
- package/dist/tutorials/Recovery-and-Retries.md +31 -31
- package/dist/tutorials/Remote-Access.md +63 -0
- package/dist/tutorials/Reviews-and-Checkpoints.md +98 -90
- package/dist/tutorials/Schedules.md +46 -27
- package/dist/tutorials/Settings-Reference.md +194 -174
- package/dist/tutorials/Shorts.md +34 -33
- package/dist/tutorials/Start-at-Login.md +3 -3
- package/dist/tutorials/Studio-Extension.md +22 -24
- package/dist/tutorials/Template-Studio.md +128 -0
- package/dist/tutorials/Templates.md +51 -77
- package/dist/tutorials/Trash.md +4 -4
- package/dist/tutorials/Troubleshooting.md +44 -44
- package/dist/tutorials/Uninstalling-and-Moving.md +5 -5
- package/dist/tutorials/Updating-and-Patch-Notes.md +18 -18
- package/dist/tutorials/Video-Editing.md +34 -34
- package/dist/tutorials/Where-Your-Files-Live.md +10 -10
- package/dist/tutorials/YouTube-Description.md +26 -26
- package/dist/tutorials/Your-First-Short.md +35 -35
- package/dist/tutorials/_Sidebar.md +19 -14
- package/dist/updater/work-in-progress.js +42 -12
- package/dist/web/assets/index-BNftKQrA.css +1 -0
- package/dist/web/assets/index-Cpa58woP.js +459 -0
- package/dist/web/assets/pdf-BKh8f7R6.js +56 -0
- package/dist/web/assets/pdf.worker.min-BmVo14Nb.mjs +28 -0
- package/dist/web/assets/pdf.worker.min-D7i8o3wR.js +1 -0
- package/dist/web/index.html +2 -2
- package/package.json +6 -2
- package/dist/slices/article/provided-entries.js +0 -66
- package/dist/slices/article/run.js +0 -145
- package/dist/slices/images/run.js +0 -234
- package/dist/slices/narration/legacy-plan.js +0 -58
- package/dist/slices/narration/run.js +0 -270
- package/dist/slices/rebuild/runtime-actions.js +0 -90
- package/dist/slices/research/run.js +0 -224
- package/dist/slices/subtitles/prepare.js +0 -194
- package/dist/slices/subtitles/transcript.js +0 -34
- package/dist/slices/thumbnail/run.js +0 -219
- package/dist/slices/video/audio-inputs.js +0 -33
- package/dist/slices/video/reuse-audio.js +0 -62
- package/dist/slices/video/run.js +0 -78
- package/dist/slices/video/write-export.js +0 -158
- package/dist/slices/video/write-subtitles.js +0 -59
- package/dist/tutorials/Library-Overview.md +0 -76
- package/dist/tutorials/Play-Images.md +0 -161
- package/dist/tutorials/Play-Narration.md +0 -167
- package/dist/tutorials/Play-Outputs.md +0 -109
- package/dist/tutorials/Play-Overview.md +0 -227
- package/dist/tutorials/Play-Title-and-Article.md +0 -136
- package/dist/tutorials/Play-Video-and-Style.md +0 -143
- package/dist/web/assets/index-BadsoORk.css +0 -1
- package/dist/web/assets/index-D8LqNsCl.js +0 -326
- package/dist/web/assets/pdf-T7MCwjIv.js +0 -54
- package/dist/web/assets/pdf.worker.min-Dswkl-cV.mjs +0 -29
- package/dist/web/assets/pdf.worker.min-zC6nfbxG.js +0 -1
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import { internalError } from "../../kernel/errors.js";
|
|
3
4
|
import { thinkingModes } from "../../kernel/ports/llm.js";
|
|
5
|
+
import { editSettings } from "../../kernel/ports/settings-places.js";
|
|
4
6
|
import { plainText } from "../article/plain.js";
|
|
5
7
|
import { defaultReviewPrompts, narrationItemKey, reviewStages, } from "../reviews/model.js";
|
|
6
8
|
import { nextAttempt, reviewOutcome } from "../reviews/outcome.js";
|
|
7
9
|
import { latestVerdict, saveVerdict } from "../reviews/repo.js";
|
|
8
10
|
import { parseVerdict, reviewMessages } from "../reviews/verdict.js";
|
|
11
|
+
import { isReadyOutput } from "../revisions/view.js";
|
|
9
12
|
import { pickedShortsOf } from "../shorts/clips.js";
|
|
10
13
|
import { outputPath } from "../storage/layout.js";
|
|
14
|
+
import { openingOf, spokenArticle } from "./review-promise.js";
|
|
11
15
|
import { exportSnapshot, revisionTranscript, } from "./runtime-export-inputs.js";
|
|
12
|
-
import {
|
|
16
|
+
import { workLlm } from "./runtime-plan.js";
|
|
13
17
|
import { publishResult } from "./runtime-publication.js";
|
|
14
18
|
import { wordsSchema } from "./runtime-subtitles.js";
|
|
19
|
+
import { pieceAlreadyDone } from "./work-records.js";
|
|
15
20
|
// What `recipe-reviews.ts` saves in the step's values, by position.
|
|
16
21
|
const valuesSchema = z.tuple([
|
|
17
22
|
z.enum(reviewStages),
|
|
@@ -32,9 +37,7 @@ const textMax = 60_000;
|
|
|
32
37
|
// saved as a pending redo, which `review-redo.ts` starts once this step has finished, and
|
|
33
38
|
// until then the item's dependents are held (`reviewHold`).
|
|
34
39
|
export async function executeReviewRecipe(deps, context, providers, piece) {
|
|
35
|
-
if (deps.db
|
|
36
|
-
.prepare("SELECT state FROM revision_work_pieces WHERE id=? AND work_id=?")
|
|
37
|
-
.get(piece.id, context.work.workId)?.state === "done")
|
|
40
|
+
if (pieceAlreadyDone(deps.db, context.work.workId, piece.id))
|
|
38
41
|
return "done";
|
|
39
42
|
if (!context.maySubmit(piece.id))
|
|
40
43
|
return "held";
|
|
@@ -44,33 +47,24 @@ export async function executeReviewRecipe(deps, context, providers, piece) {
|
|
|
44
47
|
? valuesSchema.safeParse(input.values)
|
|
45
48
|
: undefined;
|
|
46
49
|
if (values === undefined || !values.success)
|
|
47
|
-
throw new Error("
|
|
50
|
+
throw new Error(internalError("a review step was set up wrongly"));
|
|
48
51
|
const [stage, itemKey, , provider, model, thinking, prompt, mode, retries] = values.data;
|
|
49
52
|
const snapshot = exportSnapshot(deps, context, piece);
|
|
50
53
|
const material = reviewMaterial(deps, context, snapshot, stage, itemKey, prompt);
|
|
51
54
|
const previous = latestVerdict(deps.db, context.work.projectId, itemKey);
|
|
52
55
|
const attempt = nextAttempt(previous, material.itemFingerprint);
|
|
53
|
-
const row = deps.db
|
|
54
|
-
.prepare("SELECT recipe_context FROM revision_work WHERE id=?")
|
|
55
|
-
.get(context.work.workId);
|
|
56
|
-
const catalogued = savedCatalogue(row?.recipe_context).llm.find((one) => one.provider === provider && one.id === model);
|
|
57
56
|
const level = thinking === null ? undefined : z.enum(thinkingModes).parse(thinking);
|
|
57
|
+
const call = workLlm(deps.db, context.work.workId, { provider, model, thinking: level }, piece.key);
|
|
58
58
|
if (!context.maySubmit(piece.id))
|
|
59
59
|
return "held";
|
|
60
|
-
const answer = await providers.forPiece(piece.id).llm({
|
|
61
|
-
provider,
|
|
62
|
-
model,
|
|
63
|
-
...(level === undefined ? {} : { thinking: level }),
|
|
64
|
-
thinkingConfig: level === undefined ? null : (catalogued?.llm.thinking?.[level] ?? null),
|
|
60
|
+
const answer = await providers.forPiece(piece.id).llm(call({
|
|
65
61
|
messages: reviewMessages(material.request),
|
|
66
|
-
webSearch: false,
|
|
67
62
|
...(material.images.length === 0 ? {} : { images: material.images }),
|
|
68
|
-
previewLabel: piece.key,
|
|
69
63
|
check: (value) => {
|
|
70
64
|
const parsed = parseVerdict(value.text);
|
|
71
65
|
return parsed.ok ? undefined : parsed.reason;
|
|
72
66
|
},
|
|
73
|
-
});
|
|
67
|
+
}));
|
|
74
68
|
if (!answer.ok)
|
|
75
69
|
return "held";
|
|
76
70
|
const verdict = parseVerdict(answer.value.text);
|
|
@@ -126,10 +120,7 @@ export async function executeReviewRecipe(deps, context, providers, piece) {
|
|
|
126
120
|
// and outro, and the prepared or hand-edited spoken text rather than the article as written.
|
|
127
121
|
// Falls back to the article when those texts can't be read.
|
|
128
122
|
function spokenText(deps, snapshot) {
|
|
129
|
-
const present = ["intro", "body", "outro"].filter((kind) => snapshot.view.outputs.some((one) => one.
|
|
130
|
-
one.available &&
|
|
131
|
-
one.state === "ready" &&
|
|
132
|
-
one.output.role === `audio_${kind}`));
|
|
123
|
+
const present = ["intro", "body", "outro"].filter((kind) => snapshot.view.outputs.some((one) => isReadyOutput(one) && one.output.role === `audio_${kind}`));
|
|
133
124
|
try {
|
|
134
125
|
const text = present.map((kind) => revisionTranscript(deps, snapshot, kind)).join("\n\n");
|
|
135
126
|
if (text.trim() !== "")
|
|
@@ -145,9 +136,7 @@ export function reviewMaterial(deps, context, snapshot, stage, itemKey, prompt)
|
|
|
145
136
|
const recipes = snapshot.plan.recipes;
|
|
146
137
|
const config = view.revision.config;
|
|
147
138
|
const output = (workKey, role) => view.outputs.find((row) => row.workKey === workKey &&
|
|
148
|
-
row
|
|
149
|
-
row.available &&
|
|
150
|
-
row.state === "ready" &&
|
|
139
|
+
isReadyOutput(row) &&
|
|
151
140
|
(role === undefined || row.output.role === role));
|
|
152
141
|
const path = (row) => outputPath(deps.paths, context.work.projectId, row.output.path);
|
|
153
142
|
const text = (row) => clip(readFileSync(path(row), "utf8"));
|
|
@@ -178,13 +167,33 @@ export function reviewMaterial(deps, context, snapshot, stage, itemKey, prompt)
|
|
|
178
167
|
itemFingerprint: article.fingerprint,
|
|
179
168
|
};
|
|
180
169
|
}
|
|
170
|
+
// The opening as the viewer will hear it, against what the title and the thumbnail brief
|
|
171
|
+
// promise; the whole article shows whether the promise is kept later on.
|
|
172
|
+
case "promise": {
|
|
173
|
+
const article = output("article:body", "article_md");
|
|
174
|
+
if (article === undefined)
|
|
175
|
+
throw missing("article", "Use More → Write the article again in the Article section");
|
|
176
|
+
const spoken = spokenArticle(readFileSync(path(article), "utf8"));
|
|
177
|
+
const thumbnail = recipes.find((one) => one.key === "thumbnail:image");
|
|
178
|
+
const brief = thumbnail?.input.kind === "image" ? thumbnail.input.prompt.trim() : "";
|
|
179
|
+
return {
|
|
180
|
+
request: request([
|
|
181
|
+
{ label: "Video title", text: config.title },
|
|
182
|
+
...(brief === "" ? [] : [{ label: "Thumbnail brief", text: clip(brief) }]),
|
|
183
|
+
{ label: "Opening (first ~30 s)", text: openingOf(spoken) },
|
|
184
|
+
{ label: "Whole article", text: clip(spoken) },
|
|
185
|
+
]),
|
|
186
|
+
images: [],
|
|
187
|
+
itemFingerprint: article.fingerprint,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
181
190
|
case "images":
|
|
182
191
|
case "thumbnail": {
|
|
183
192
|
const image = output(itemKey);
|
|
184
193
|
if (image === undefined)
|
|
185
194
|
throw missing(stage === "images" ? "image" : "thumbnail", stage === "images"
|
|
186
|
-
? "
|
|
187
|
-
: "
|
|
195
|
+
? "Press Make this image again on it in the project's Images section"
|
|
196
|
+
: "Press Make the thumbnail again in the project's Images section");
|
|
188
197
|
const recipe = recipes.find((one) => one.key === itemKey);
|
|
189
198
|
const brief = recipe?.input.kind === "image" ? recipe.input.prompt : "";
|
|
190
199
|
const reference = stage === "images" ? output("reference:image") : undefined;
|
|
@@ -218,7 +227,7 @@ export function reviewMaterial(deps, context, snapshot, stage, itemKey, prompt)
|
|
|
218
227
|
case "narration": {
|
|
219
228
|
const timing = output("subtitles:timing", "subtitle_words");
|
|
220
229
|
if (timing === undefined || itemKey !== narrationItemKey)
|
|
221
|
-
throw missing("narration's word timing", "
|
|
230
|
+
throw missing("narration's word timing", "Press Make the video again in the project's Video section");
|
|
222
231
|
const { words, omissions } = wordsSchema.parse(JSON.parse(readFileSync(path(timing), "utf8")));
|
|
223
232
|
const unsure = words.filter((word) => (word.confidence ?? 1) < 0.3).length;
|
|
224
233
|
return {
|
|
@@ -246,19 +255,16 @@ export function reviewMaterial(deps, context, snapshot, stage, itemKey, prompt)
|
|
|
246
255
|
case "shorts": {
|
|
247
256
|
const render = output(`${itemKey}:render`);
|
|
248
257
|
if (render === undefined)
|
|
249
|
-
throw missing("short",
|
|
258
|
+
throw missing("short", `Use Make this short again in ${editSettings.shorts}`);
|
|
250
259
|
const number = Number(itemKey.slice("shorts:".length));
|
|
251
260
|
const pick = view.pieces.find((row) => row.key === "shorts:pick" && row.selected && row.piece.state === "done");
|
|
252
261
|
const title = pickedShortsOf(pick?.piece.payload)?.shorts.find((clip) => clip.number === number)?.title ??
|
|
253
262
|
"";
|
|
254
263
|
const stills = view.outputs
|
|
255
|
-
.filter((row) => row.workKey.startsWith(`${itemKey}:image:`) &&
|
|
256
|
-
row.selected &&
|
|
257
|
-
row.available &&
|
|
258
|
-
row.state === "ready")
|
|
264
|
+
.filter((row) => row.workKey.startsWith(`${itemKey}:image:`) && isReadyOutput(row))
|
|
259
265
|
.slice(0, 6);
|
|
260
266
|
if (stills.length === 0)
|
|
261
|
-
throw missing("short's images",
|
|
267
|
+
throw missing("short's images", `Use Make this short again in ${editSettings.shorts}`);
|
|
262
268
|
const names = stills.map((_, index) => `image ${String(index + 1)} of the short`);
|
|
263
269
|
return {
|
|
264
270
|
request: request([{ label: "Title of the short", text: title }], names),
|
|
@@ -2,6 +2,7 @@ import { executeAgentReviewRecipe } from "./runtime-agent-review.js";
|
|
|
2
2
|
import { executeAnimateRecipe } from "./runtime-animate.js";
|
|
3
3
|
import { executeDocumentRecipe } from "./runtime-document.js";
|
|
4
4
|
import { executeExportRecipe } from "./runtime-export.js";
|
|
5
|
+
import { lateKeywordProviders, lateValuesOf } from "./runtime-keywords.js";
|
|
5
6
|
import { executeLocalRecipe } from "./runtime-local.js";
|
|
6
7
|
import { pieceLabel } from "./runtime-piece-label.js";
|
|
7
8
|
import { executeProviderRecipe } from "./runtime-provider.js";
|
|
@@ -11,7 +12,9 @@ import { executeSubtitleRecipe } from "./runtime-subtitles.js";
|
|
|
11
12
|
import { executeVoicesRecipe } from "./runtime-voices.js";
|
|
12
13
|
import { executeYoutubeRecipe } from "./runtime-youtube.js";
|
|
13
14
|
import { workPieces } from "./work-records.js";
|
|
14
|
-
export async function runRevisionInvocation(deps, context,
|
|
15
|
+
export async function runRevisionInvocation(deps, context, sent) {
|
|
16
|
+
// `{{Slopify_Release_Date}}` and `{{Slopify_Duration}}` are filled as a request is sent.
|
|
17
|
+
const providers = lateKeywordProviders(sent, () => lateValuesOf(deps, context));
|
|
15
18
|
const pieces = workPieces(deps.db, context.work.workId).filter((piece) => !piece.key.startsWith("article:continuation:"));
|
|
16
19
|
let waiting = false;
|
|
17
20
|
for (const piece of pieces) {
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { z } from "zod";
|
|
1
|
+
import { errorText, internalError } from "../../kernel/errors.js";
|
|
3
2
|
import { withLanguage, wordTimingUnavailable } from "../../kernel/ports/languages.js";
|
|
3
|
+
import { editSettings } from "../../kernel/ports/settings-places.js";
|
|
4
4
|
import { usesFigureCards, usesShorts } from "../admission/rules.js";
|
|
5
5
|
import { captionBoldFont } from "../fonts/coverage.js";
|
|
6
|
-
import { resolveBoldFont } from "../fonts/index.js";
|
|
7
6
|
import { masterGoal } from "../loudness/model.js";
|
|
7
|
+
import { assetPathOrUndefined } from "../revisions/mutation-assets.js";
|
|
8
|
+
import { readyOutput } from "../revisions/view.js";
|
|
8
9
|
import { clipWords } from "../shorts/captions.js";
|
|
9
10
|
import { effectiveClips, pickedShortsOf } from "../shorts/clips.js";
|
|
10
11
|
import { anchorKept, lostMessage, withNewPicks } from "../shorts/keep.js";
|
|
@@ -18,21 +19,21 @@ import { probeDurationMs } from "../video/ffmpeg.js";
|
|
|
18
19
|
import { usesVoices } from "../voices/model.js";
|
|
19
20
|
import { sentencesText, transcriptSentences } from "../youtube/transcript.js";
|
|
20
21
|
import { shortsPickKey } from "./recipe-shorts.js";
|
|
22
|
+
import { exportMachine, warningsOf } from "./runtime-export.js";
|
|
21
23
|
import { figureShots } from "./runtime-export-edit.js";
|
|
22
24
|
import { exportSnapshot, revisionAudio } from "./runtime-export-inputs.js";
|
|
23
25
|
import { imageCall } from "./runtime-image.js";
|
|
24
|
-
import {
|
|
26
|
+
import { workLlm } from "./runtime-plan.js";
|
|
25
27
|
import { softenIfAsked } from "./runtime-provider.js";
|
|
26
28
|
import { preparedResult, preparedTexts, publishResult } from "./runtime-publication.js";
|
|
27
|
-
import {
|
|
29
|
+
import { readTimingWords } from "./runtime-subtitles.js";
|
|
28
30
|
import { clearSoftening } from "./soften.js";
|
|
31
|
+
import { pieceAlreadyDone } from "./work-records.js";
|
|
29
32
|
// The Shorts step's four kinds of work (`recipe-shorts.ts`): the pick, each clip's image
|
|
30
33
|
// prompts, each image, and each clip's render. Each is one piece of its own, so a failure
|
|
31
34
|
// names the clip it belongs to and Try again redoes only what did not finish.
|
|
32
35
|
export async function executeShortsRecipe(deps, context, providers, piece) {
|
|
33
|
-
if (deps.db
|
|
34
|
-
.prepare("SELECT state FROM revision_work_pieces WHERE id=? AND work_id=?")
|
|
35
|
-
.get(piece.id, context.work.workId)?.state === "done")
|
|
36
|
+
if (pieceAlreadyDone(deps.db, context.work.workId, piece.id))
|
|
36
37
|
return "done";
|
|
37
38
|
if (!context.maySubmit(piece.id))
|
|
38
39
|
return "held";
|
|
@@ -48,7 +49,7 @@ export async function executeShortsRecipe(deps, context, providers, piece) {
|
|
|
48
49
|
throw setupError();
|
|
49
50
|
const clip = savedClips(snapshot).find((one) => one.number === number);
|
|
50
51
|
if (clip === undefined)
|
|
51
|
-
throw new Error(
|
|
52
|
+
throw new Error(`The clip this short was cut from is no longer among the picked shorts. Use Pick different moments in ${editSettings.shorts}, then Try again.`);
|
|
52
53
|
if (matched[2] === "prompts")
|
|
53
54
|
return prompts(deps, context, providers, piece, snapshot, clip);
|
|
54
55
|
if (matched[2] === "render")
|
|
@@ -66,10 +67,10 @@ async function pick(deps, context, providers, piece, snapshot) {
|
|
|
66
67
|
throw setupError();
|
|
67
68
|
const llm = config.llm;
|
|
68
69
|
if (llm === undefined || llm.provider.trim() === "" || llm.model.trim() === "")
|
|
69
|
-
throw new Error(
|
|
70
|
-
const sentences = transcriptSentences(
|
|
70
|
+
throw new Error(`Shorts need an AI model to pick the clips, and none is chosen. Choose a Service and a Model in ${editSettings.writer}, then Try again.`);
|
|
71
|
+
const sentences = transcriptSentences(readTimingWords(deps, context, view, shortsTimingPurpose).words);
|
|
71
72
|
if (sentences.length === 0)
|
|
72
|
-
throw new Error("The narration's word timing holds no words, so there is nothing to cut shorts from.
|
|
73
|
+
throw new Error("The narration's word timing holds no words, so there is nothing to cut shorts from. Press Make the video again in the project's Video section, then Try again.");
|
|
73
74
|
const audio = await revisionAudio(deps, context, view);
|
|
74
75
|
const durationSeconds = audio.reduce((sum, segment) => sum + segment.seconds, 0);
|
|
75
76
|
// Kept shorts are found again in this timing; the model is asked only for the rest.
|
|
@@ -236,12 +237,12 @@ async function render(deps, context, piece, snapshot, clip) {
|
|
|
236
237
|
.filter((recipe) => recipe.key.startsWith(prefix))
|
|
237
238
|
.toSorted((left, right) => imageIndex(left.key) - imageIndex(right.key))
|
|
238
239
|
.map((recipe) => {
|
|
239
|
-
const row = view
|
|
240
|
+
const row = readyOutput(view, { workKey: recipe.key });
|
|
240
241
|
if (row === undefined)
|
|
241
|
-
throw new Error(`Short ${String(clip.number)} is missing one of its images. Try again to make it again; if it keeps happening, use Make this short again in
|
|
242
|
+
throw new Error(`Short ${String(clip.number)} is missing one of its images. Try again to make it again; if it keeps happening, use Make this short again in ${editSettings.shorts}.`);
|
|
242
243
|
return outputPath(deps.paths, context.work.projectId, row.output.path);
|
|
243
244
|
});
|
|
244
|
-
const allWords =
|
|
245
|
+
const allWords = readTimingWords(deps, context, view, shortsTimingPurpose).words;
|
|
245
246
|
const words = clipWords(allWords, clip.start, clip.end);
|
|
246
247
|
// The upright cards whose descriptions fall in the clip, on the clip's own timeline.
|
|
247
248
|
const figures = usesFigureCards(config)
|
|
@@ -268,8 +269,11 @@ async function render(deps, context, piece, snapshot, clip) {
|
|
|
268
269
|
const prepared = [];
|
|
269
270
|
const seconds = (clip.end - clip.start) / speed;
|
|
270
271
|
try {
|
|
272
|
+
const machine = await exportMachine(deps, context.work.projectId);
|
|
271
273
|
const master = await renderShort({
|
|
272
274
|
bin: deps.ffmpeg,
|
|
275
|
+
machine: machine.machine,
|
|
276
|
+
notice: machine.notice,
|
|
273
277
|
master: masterGoal(config, "video"),
|
|
274
278
|
timeline,
|
|
275
279
|
start: clip.start,
|
|
@@ -303,12 +307,13 @@ async function render(deps, context, piece, snapshot, clip) {
|
|
|
303
307
|
prepared.push(preparedResult(deps, context, piece, "short_video", asset, durationMs, {
|
|
304
308
|
short: clip.number,
|
|
305
309
|
sentences: [clip.first, clip.last],
|
|
310
|
+
...warningsOf(machine.notices),
|
|
306
311
|
...(master === undefined ? {} : { master }),
|
|
307
312
|
}));
|
|
308
313
|
await publishResult(deps, context, piece, prepared, { short: clip.number, start: clip.start, end: clip.end, durationMs }, asset);
|
|
309
314
|
// One per rendered short, with no stage: stage "video" would read as a finished video.
|
|
310
315
|
// Its pick and prompt tokens are not counted here.
|
|
311
|
-
deps.count?.("stage.completed", { shorts: 1 });
|
|
316
|
+
deps.count?.("stage.completed", { shorts: 1 }, context.work.projectId);
|
|
312
317
|
return "done";
|
|
313
318
|
}
|
|
314
319
|
finally {
|
|
@@ -340,58 +345,34 @@ async function backgroundMusic(deps, context, view) {
|
|
|
340
345
|
const shorts = view.revision.config.shorts;
|
|
341
346
|
if (assetId === undefined || shorts === undefined)
|
|
342
347
|
return undefined;
|
|
343
|
-
const fix =
|
|
344
|
-
const
|
|
345
|
-
|
|
346
|
-
.safeParse(deps.db
|
|
347
|
-
.prepare("SELECT path FROM project_assets WHERE project_id=? AND id=?")
|
|
348
|
-
.get(context.work.projectId, assetId));
|
|
349
|
-
if (!row.success)
|
|
348
|
+
const fix = `Choose another file under Background music in ${editSettings.shorts}, or remove the music, then Try again.`;
|
|
349
|
+
const saved = assetPathOrUndefined(deps, context.work.projectId, assetId);
|
|
350
|
+
if (saved === undefined)
|
|
350
351
|
throw new Error(`The shorts' background music file is no longer in the project. ${fix}`);
|
|
351
|
-
const path = outputPath(deps.paths, context.work.projectId,
|
|
352
|
+
const path = outputPath(deps.paths, context.work.projectId, saved);
|
|
352
353
|
let durationMs;
|
|
353
354
|
try {
|
|
354
355
|
durationMs = await probeDurationMs(deps.ffmpeg, path, context.signal, deps.log);
|
|
355
356
|
}
|
|
356
357
|
catch (error) {
|
|
357
358
|
context.signal.throwIfAborted();
|
|
358
|
-
throw new Error(`The shorts' background music couldn't be read as audio (${
|
|
359
|
+
throw new Error(`The shorts' background music couldn't be read as audio (${errorText(error)}). ${fix}`);
|
|
359
360
|
}
|
|
360
361
|
if (durationMs <= 0)
|
|
361
362
|
throw new Error(`The shorts' background music file holds no sound. ${fix}`);
|
|
362
363
|
return { path, volume: musicVolumeOf(shorts) };
|
|
363
364
|
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
row.available &&
|
|
367
|
-
row.state === "ready" &&
|
|
368
|
-
row.workKey === "subtitles:timing" &&
|
|
369
|
-
row.output.role === "subtitle_words");
|
|
370
|
-
if (timing === undefined)
|
|
371
|
-
throw new Error("The narration's word timing, which the shorts are cut and captioned from, is missing. Use More → Render the video again in the Video section, then Try again.");
|
|
372
|
-
return wordsSchema.parse(JSON.parse(readFileSync(outputPath(deps.paths, context.work.projectId, timing.output.path), "utf8"))).words;
|
|
373
|
-
}
|
|
365
|
+
// What the shorts read the narration's word timing for, in the sentence a missing one fails with.
|
|
366
|
+
const shortsTimingPurpose = "the shorts are cut and captioned from";
|
|
374
367
|
// The project's text model, with the thinking settings saved for this step's catalogue.
|
|
375
368
|
function llmCall(deps, context, piece, view, check) {
|
|
376
369
|
const llm = view.revision.config.llm ?? { provider: "", model: "" };
|
|
377
|
-
const
|
|
378
|
-
|
|
379
|
-
.get(context.work.workId);
|
|
380
|
-
const model = savedCatalogue(row?.recipe_context).llm.find((one) => one.provider === llm.provider && one.id === llm.model);
|
|
381
|
-
return (messages) => ({
|
|
382
|
-
provider: llm.provider,
|
|
383
|
-
model: llm.model,
|
|
384
|
-
...(llm.thinking === undefined ? {} : { thinking: llm.thinking }),
|
|
385
|
-
thinkingConfig: llm.thinking === undefined ? null : (model?.llm.thinking?.[llm.thinking] ?? null),
|
|
386
|
-
messages,
|
|
387
|
-
webSearch: false,
|
|
388
|
-
previewLabel: piece.key,
|
|
389
|
-
check: (value) => check(value.text),
|
|
390
|
-
});
|
|
370
|
+
const call = workLlm(deps.db, context.work.workId, llm, piece.key);
|
|
371
|
+
return (messages) => call({ messages, check: (value) => check(value.text) });
|
|
391
372
|
}
|
|
392
373
|
function imageIndex(key) {
|
|
393
374
|
return Number(key.split(":").at(-1));
|
|
394
375
|
}
|
|
395
376
|
function setupError() {
|
|
396
|
-
return new Error("
|
|
377
|
+
return new Error(internalError("a Shorts step was set up wrongly"));
|
|
397
378
|
}
|
|
@@ -6,7 +6,7 @@ import { sourceOf } from "../admission/model.js";
|
|
|
6
6
|
import { usesShorts, usesYoutubeDescription } from "../admission/rules.js";
|
|
7
7
|
import { reviewsNarration } from "../reviews/rules.js";
|
|
8
8
|
import { currentRevisionId } from "../revisions/repo.js";
|
|
9
|
-
import { getRevisionView } from "../revisions/view.js";
|
|
9
|
+
import { getRevisionView, isDonePiece, isReadyOutput } from "../revisions/view.js";
|
|
10
10
|
import { settleScheduleRunsForProject } from "../schedules/repo.js";
|
|
11
11
|
import { editNeedsTiming } from "../video/edit-settings.js";
|
|
12
12
|
import { executionPlan, executionView, savedCatalogue } from "./runtime-plan.js";
|
|
@@ -146,14 +146,9 @@ export function executionStandings(deps, projectId, derived) {
|
|
|
146
146
|
.map((entry) => {
|
|
147
147
|
const pieces = workPieces(deps.db, entry.work.workId);
|
|
148
148
|
const retained = pieces.length > 0 &&
|
|
149
|
-
pieces.every((piece) => view.outputs.some((output) => output.workKey === piece.key &&
|
|
150
|
-
output.selected &&
|
|
151
|
-
output.available &&
|
|
152
|
-
output.state === "ready") ||
|
|
149
|
+
pieces.every((piece) => view.outputs.some((output) => output.workKey === piece.key && isReadyOutput(output)) ||
|
|
153
150
|
view.pieces.some((output) => output.key === piece.key &&
|
|
154
|
-
output
|
|
155
|
-
output.available &&
|
|
156
|
-
output.piece.state === "done" &&
|
|
151
|
+
isDonePiece(output) &&
|
|
157
152
|
(output.fingerprint === piece.fingerprint ||
|
|
158
153
|
madeFrom(deps, projectId, piece.key, output.fingerprint, piece.fingerprint))));
|
|
159
154
|
// Save can bind an existing asset without granting its placeholder invocation.
|
|
@@ -228,7 +223,13 @@ export function projectStandings(deps, projectId) {
|
|
|
228
223
|
}, 0);
|
|
229
224
|
// A failed step, or else one waiting to run again, says why on the stage row. A failed
|
|
230
225
|
// stage keeps the moment it failed: needs_attention tells a narration retry started after
|
|
231
|
-
// the failure (still going) from one started before it (spent) by it.
|
|
226
|
+
// the failure (still going) from one started before it (spent) by it. A stage's start is
|
|
227
|
+
// the moment it went running, kept through a wait to retry, between its steps (one done,
|
|
228
|
+
// the next not claimed yet) and once it ends; a pending start is kept only then, so a
|
|
229
|
+
// stage made again after it was done takes a new one when it runs. A done
|
|
230
|
+
// stage keeps the moment it finished: the project's run length, the time left on a running
|
|
231
|
+
// step and the usual time a step takes (`eta/history.ts`) read them, and a later save that
|
|
232
|
+
// changes nothing in the stage moves neither.
|
|
232
233
|
const failed = all.find((work) => work.state === "failed") ??
|
|
233
234
|
(stage.retryAt === undefined || stage.retryAt === null
|
|
234
235
|
? undefined
|
|
@@ -241,7 +242,16 @@ export function projectStandings(deps, projectId) {
|
|
|
241
242
|
deps.db
|
|
242
243
|
.prepare(`UPDATE stages SET state=:state,failure_reason=:reason,failure_kind=:kind,retry_at=:retryAt,
|
|
243
244
|
progress_current=:current,progress_total=:total,
|
|
244
|
-
|
|
245
|
+
started_at=CASE
|
|
246
|
+
WHEN :state='running' THEN CASE
|
|
247
|
+
WHEN started_at IS NOT NULL AND state IN ('running','pending') THEN started_at
|
|
248
|
+
ELSE :now END
|
|
249
|
+
WHEN :state IN ('done','failed') THEN started_at
|
|
250
|
+
WHEN :state='pending' AND :retryAt IS NOT NULL THEN started_at
|
|
251
|
+
WHEN :state='pending' AND state IN ('running','pending') AND :current > 0 THEN started_at
|
|
252
|
+
ELSE NULL END,
|
|
253
|
+
finished_at=CASE
|
|
254
|
+
WHEN :state='done' THEN CASE WHEN state='done' AND finished_at IS NOT NULL THEN finished_at ELSE :now END
|
|
245
255
|
WHEN :state='failed' THEN CASE WHEN state='failed' AND finished_at IS NOT NULL THEN finished_at ELSE :now END
|
|
246
256
|
ELSE NULL END
|
|
247
257
|
WHERE project_id=:projectId AND kind=:stage`)
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import { internalError } from "../../kernel/errors.js";
|
|
3
4
|
import { subtitleModelDir } from "../../kernel/paths.js";
|
|
4
5
|
import { projectLanguage } from "../../kernel/ports/languages.js";
|
|
6
|
+
import { editSettings } from "../../kernel/ports/settings-places.js";
|
|
5
7
|
import { SubtitleMismatch } from "../../kernel/ports/subtitles.js";
|
|
6
8
|
import { narrationAliasesOf } from "../admission/rules.js";
|
|
7
9
|
import { captionFont, captionFontDeps } from "../fonts/coverage.js";
|
|
8
10
|
import { resolveFont } from "../fonts/index.js";
|
|
9
11
|
import { validateCues } from "../revisions/rules.js";
|
|
12
|
+
import { donePiece, isReadyOutput, readyOutput } from "../revisions/view.js";
|
|
10
13
|
import { discardPreparedAssets, writeAsset } from "../storage/assets.js";
|
|
11
14
|
import { outputPath } from "../storage/layout.js";
|
|
12
15
|
import { captionCues, serializeAss, serializeSrt, serializeVtt } from "../subtitles/captions.js";
|
|
@@ -47,6 +50,14 @@ const cuesSchema = z.object({
|
|
|
47
50
|
})),
|
|
48
51
|
omissions: wordsSchema.shape.omissions,
|
|
49
52
|
});
|
|
53
|
+
// The narration's word timing (`subtitles:timing`), read for a step that needs it. `purpose` says
|
|
54
|
+
// what the step uses it for, in the sentence a missing timing fails with.
|
|
55
|
+
export function readTimingWords(deps, context, view, purpose) {
|
|
56
|
+
const timing = readyOutput(view, { workKey: "subtitles:timing", role: "subtitle_words" });
|
|
57
|
+
if (timing === undefined)
|
|
58
|
+
throw new Error(`The narration's word timing, which ${purpose}, is missing. Press Make the video again in the project's Video section, then Try again.`);
|
|
59
|
+
return wordsSchema.parse(JSON.parse(readFileSync(outputPath(deps.paths, context.work.projectId, timing.output.path), "utf8")));
|
|
60
|
+
}
|
|
50
61
|
export async function executeSubtitleRecipe(deps, context, piece) {
|
|
51
62
|
if (!context.maySubmit(piece.id))
|
|
52
63
|
return "held";
|
|
@@ -58,15 +69,15 @@ export async function executeSubtitleRecipe(deps, context, piece) {
|
|
|
58
69
|
return cues(deps, context, piece, snapshot);
|
|
59
70
|
if (piece.key === "subtitles:files")
|
|
60
71
|
return files(deps, context, piece, snapshot);
|
|
61
|
-
throw new Error("
|
|
72
|
+
throw new Error(internalError("unknown kind of caption step"));
|
|
62
73
|
}
|
|
63
74
|
async function timing(deps, context, piece, snapshot) {
|
|
64
75
|
const audio = await revisionAudio(deps, context, snapshot.view);
|
|
65
76
|
if (audio.length === 0)
|
|
66
|
-
throw new Error(
|
|
77
|
+
throw new Error(`Captions and the YouTube description need narration audio, but this project has none. Turn captions off in ${editSettings.captions} and the YouTube description off in ${editSettings.extras}, or turn narration on, then Try again.`);
|
|
67
78
|
const alignSubtitles = deps.alignSubtitles;
|
|
68
79
|
if (alignSubtitles === undefined)
|
|
69
|
-
throw new Error("
|
|
80
|
+
throw new Error(internalError("the caption timing tool is missing from this build"));
|
|
70
81
|
if (!context.maySubmit(piece.id))
|
|
71
82
|
return "held";
|
|
72
83
|
const words = [];
|
|
@@ -111,7 +122,7 @@ async function timing(deps, context, piece, snapshot) {
|
|
|
111
122
|
offset += segment.seconds;
|
|
112
123
|
}
|
|
113
124
|
if (captionCues(words).length === 0)
|
|
114
|
-
throw new Error(
|
|
125
|
+
throw new Error(`None of the narration could be matched to the article text, so captions can't be timed. The narration must be in the project's language (${editSettings.language}); if you uploaded your own audio, make sure it reads the article text, then Try again.`);
|
|
115
126
|
context.signal.throwIfAborted();
|
|
116
127
|
const output = preparedText(deps, context, piece, "subtitle_words", "subtitles.json", JSON.stringify({ key: piece.logicalFingerprint ?? piece.fingerprint, words, omissions }));
|
|
117
128
|
await publishResult(deps, context, piece, [output], { words, omissions }, output.asset);
|
|
@@ -123,7 +134,7 @@ async function cues(deps, context, piece, snapshot) {
|
|
|
123
134
|
let value;
|
|
124
135
|
if (manual !== undefined) {
|
|
125
136
|
if (plan.work.find((one) => one.key === piece.key)?.disposition === "review")
|
|
126
|
-
throw new Error(
|
|
137
|
+
throw new Error(`Your edited captions were written for an older version of the narration. Look them over in ${editSettings.captions} and save, then Try again.`);
|
|
127
138
|
const audio = await revisionAudio(deps, context, view);
|
|
128
139
|
const errors = validateCues(manual.cues, audio.reduce((sum, segment) => sum + segment.seconds, 0));
|
|
129
140
|
if (errors.length > 0)
|
|
@@ -131,12 +142,9 @@ async function cues(deps, context, piece, snapshot) {
|
|
|
131
142
|
value = { cues: [...manual.cues], omissions: [] };
|
|
132
143
|
}
|
|
133
144
|
else {
|
|
134
|
-
const output = view
|
|
135
|
-
one.available &&
|
|
136
|
-
one.state === "ready" &&
|
|
137
|
-
one.workKey === "subtitles:timing");
|
|
145
|
+
const output = readyOutput(view, { workKey: "subtitles:timing" });
|
|
138
146
|
if (output === undefined)
|
|
139
|
-
throw new Error("Caption timing is missing.
|
|
147
|
+
throw new Error("Caption timing is missing. Press Make the video again in the project's Video section, then Try again.");
|
|
140
148
|
const timed = wordsSchema.parse(JSON.parse(readFileSync(outputPath(deps.paths, context.work.projectId, output.output.path), "utf8")));
|
|
141
149
|
value = { cues: [...captionCues(timed.words)], omissions: timed.omissions };
|
|
142
150
|
}
|
|
@@ -150,10 +158,10 @@ async function files(deps, context, piece, snapshot) {
|
|
|
150
158
|
const { view } = snapshot;
|
|
151
159
|
const config = view.revision.config.subtitles;
|
|
152
160
|
if (config === undefined || config.mode === "off")
|
|
153
|
-
throw new Error("
|
|
154
|
-
const row = view
|
|
161
|
+
throw new Error(internalError("captions are off for this version of the project"));
|
|
162
|
+
const row = donePiece(view, "subtitles:cues");
|
|
155
163
|
if (row?.piece.payload === null || row?.piece.payload === undefined)
|
|
156
|
-
throw new Error("The captions haven't been prepared yet.
|
|
164
|
+
throw new Error("The captions haven't been prepared yet. Press Make the video again in the project's Video section, then Try again.");
|
|
157
165
|
const value = cuesSchema.parse(JSON.parse(row.piece.payload));
|
|
158
166
|
const previous = view.outputs.find((one) => one.output.role === "subtitle_font" && one.selected && one.available);
|
|
159
167
|
const previousFiles = view.pieces.find((one) => one.key === "subtitles:files" &&
|
|
@@ -219,9 +227,7 @@ async function files(deps, context, piece, snapshot) {
|
|
|
219
227
|
])
|
|
220
228
|
prepared.push(preparedText(deps, context, piece, role, filename, text));
|
|
221
229
|
prepared.push(preparedResult(deps, context, piece, "subtitle_font", writeAsset(deps, context.work.projectId, `selected${font.extension}`, readFileSync(font.path))));
|
|
222
|
-
const media = view.outputs.find((one) => one
|
|
223
|
-
one.available &&
|
|
224
|
-
one.state === "ready" &&
|
|
230
|
+
const media = view.outputs.find((one) => isReadyOutput(one) &&
|
|
225
231
|
(one.output.role === "audio_export" ||
|
|
226
232
|
(one.output.role === "video" && config.mode === "files")));
|
|
227
233
|
if (media !== undefined) {
|
|
@@ -295,9 +301,6 @@ function mismatchChunks(snapshot, segment) {
|
|
|
295
301
|
return [];
|
|
296
302
|
}
|
|
297
303
|
}
|
|
298
|
-
export function mismatchMessage(snapshot, segment, mismatch) {
|
|
299
|
-
return describeMismatch(mismatchChunks(snapshot, segment), segment, mismatch);
|
|
300
|
-
}
|
|
301
304
|
// The chunk that holds the words the audio stopped matching, by index; -1 when none does.
|
|
302
305
|
export function mismatchChunk(chunks, mismatch) {
|
|
303
306
|
const probe = comparable(mismatch.expected).split(" ").slice(0, 6).join(" ");
|
|
@@ -316,19 +319,19 @@ retry) {
|
|
|
316
319
|
: `${clock(mismatch.at)} into the ${segment} narration, in narration chunk ${String(at + 1)} of ${String(chunks.length)} (it starts "${opening(chunk.spokenText)}")`;
|
|
317
320
|
const expected = mismatch.expected === "" ? "the end of the text" : `"${mismatch.expected}…"`;
|
|
318
321
|
const heard = mismatch.heard === "" ? "no more speech" : `"${mismatch.heard.toLowerCase()}…"`;
|
|
319
|
-
const stopped = `
|
|
322
|
+
const stopped = `Captions stopped matching the audio at ${where}. The text expected ${expected} but the audio has ${heard}`;
|
|
320
323
|
if (retry !== undefined) {
|
|
321
324
|
const n = String(retry.chunk);
|
|
322
325
|
return retry.try === undefined
|
|
323
|
-
? `${stopped} Slopify already recorded narration chunk ${n} again ${String(narrationRetryLimit)} times and the audio still doesn't match there, so the voice probably reads something its own way (a year, an abbreviation or a name). Listen at ${clock(mismatch.at)}: if words are missing or wrong, reword that sentence in
|
|
324
|
-
: `${stopped} The voice probably skipped or garbled words there, so Slopify is recording narration chunk ${n} again by itself (try ${String(retry.try)} of ${String(narrationRetryLimit)}) and carries on with the video when it's done. If this message is still here in a few minutes,
|
|
326
|
+
? `${stopped} Slopify already recorded narration chunk ${n} again ${String(narrationRetryLimit)} times and the audio still doesn't match there, so the voice probably reads something its own way (a year, an abbreviation or a name). Listen at ${clock(mismatch.at)}: if words are missing or wrong, reword that sentence in narration chunk ${n} in ${editSettings.narration}, save, then Continue the run. If it says them right, use Download diagnostics in Settings and report it.`
|
|
327
|
+
: `${stopped} The voice probably skipped or garbled words there, so Slopify is recording narration chunk ${n} again by itself (try ${String(retry.try)} of ${String(narrationRetryLimit)}) and carries on with the video when it's done. If this message is still here in a few minutes, make narration chunk ${n} again in ${editSettings.narration}, then Continue the run.`;
|
|
325
328
|
}
|
|
326
329
|
const fix = chunk === undefined
|
|
327
|
-
? `Listen at ${clock(mismatch.at)}: if words are missing or wrong,
|
|
328
|
-
: `Listen at ${clock(mismatch.at)}: if words are missing or wrong,
|
|
330
|
+
? `Listen at ${clock(mismatch.at)}: if words are missing or wrong, make that part again in ${editSettings.narration}, then Continue the run.`
|
|
331
|
+
: `Listen at ${clock(mismatch.at)}: if words are missing or wrong, make narration chunk ${String(at + 1)} again in ${editSettings.narration}, then Continue the run.`;
|
|
329
332
|
// A voice that reads a year, an abbreviation or a name its own way reads it the same way
|
|
330
333
|
// again, so a remake would cost a chunk and fail here once more.
|
|
331
|
-
const same = "If it says them right, only another way (a year, an abbreviation or a name read its own way),
|
|
334
|
+
const same = "If it says them right, only another way (a year, an abbreviation or a name read its own way), making it again gives the same reading: use Download diagnostics in Settings and report it.";
|
|
332
335
|
return `${stopped} The recording there probably skips or changes words. ${fix} ${same}`;
|
|
333
336
|
}
|
|
334
337
|
function comparable(text) {
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { mkdtempSync,
|
|
1
|
+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
+
import { internalError } from "../../kernel/errors.js";
|
|
5
|
+
import { createRows, editSettings } from "../../kernel/ports/settings-places.js";
|
|
4
6
|
import { masterFile, masterReport } from "../loudness/loudnorm.js";
|
|
5
7
|
import { masterGoal } from "../loudness/model.js";
|
|
6
8
|
import { allocateAsset, discardPreparedAssets, sealAsset } from "../storage/assets.js";
|
|
7
|
-
import {
|
|
9
|
+
import { projectDir } from "../storage/layout.js";
|
|
8
10
|
import { audioExportArgs } from "../video/audio-export-args.js";
|
|
9
11
|
import { runFfmpeg } from "../video/ffmpeg.js";
|
|
10
12
|
import { audioChapters, audioFileArgs, ffmetadata } from "../voices/audio-files.js";
|
|
@@ -12,18 +14,17 @@ import { turnStarts } from "../voices/timing.js";
|
|
|
12
14
|
import { exportSnapshot, revisionAudio } from "./runtime-export-inputs.js";
|
|
13
15
|
import { lineLevelledAudio } from "./runtime-lines.js";
|
|
14
16
|
import { preparedResult, publishResult } from "./runtime-publication.js";
|
|
15
|
-
import {
|
|
17
|
+
import { readTimingWords } from "./runtime-subtitles.js";
|
|
18
|
+
import { pieceAlreadyDone } from "./work-records.js";
|
|
16
19
|
// The listening files of a multi-voice run: the MP3 and the M4B of the narration timeline,
|
|
17
20
|
// with a chapter at each section of the script where its first turn is heard.
|
|
18
21
|
export async function executeVoicesRecipe(deps, context, piece) {
|
|
19
|
-
if (deps.db
|
|
20
|
-
.prepare("SELECT state FROM revision_work_pieces WHERE id=? AND work_id=?")
|
|
21
|
-
.get(piece.id, context.work.workId)?.state === "done")
|
|
22
|
+
if (pieceAlreadyDone(deps.db, context.work.workId, piece.id))
|
|
22
23
|
return "done";
|
|
23
24
|
if (!context.maySubmit(piece.id))
|
|
24
25
|
return "held";
|
|
25
26
|
if (piece.key !== "voices:files" || piece.input.kind !== "local")
|
|
26
|
-
throw new Error("
|
|
27
|
+
throw new Error(internalError("unknown kind of listening-file step"));
|
|
27
28
|
context.signal.throwIfAborted();
|
|
28
29
|
const { view } = exportSnapshot(deps, context, piece);
|
|
29
30
|
const sections = z
|
|
@@ -32,17 +33,10 @@ export async function executeVoicesRecipe(deps, context, piece) {
|
|
|
32
33
|
.rest(z.unknown())
|
|
33
34
|
.parse(piece.input.values)[2]
|
|
34
35
|
.map(([title, firstTurn]) => ({ title, firstTurn }));
|
|
35
|
-
const
|
|
36
|
-
row.available &&
|
|
37
|
-
row.state === "ready" &&
|
|
38
|
-
row.workKey === "subtitles:timing" &&
|
|
39
|
-
row.output.role === "subtitle_words");
|
|
40
|
-
if (timing === undefined)
|
|
41
|
-
throw new Error("The narration's word timing, which the chapter markers come from, is missing. Use More → Render the video again in the Video section, then Try again.");
|
|
42
|
-
const { words } = wordsSchema.parse(JSON.parse(readFileSync(outputPath(deps.paths, context.work.projectId, timing.output.path), "utf8")));
|
|
36
|
+
const { words } = readTimingWords(deps, context, view, "the chapter markers come from");
|
|
43
37
|
const audio = await revisionAudio(deps, context, view, { levelled: true });
|
|
44
38
|
if (audio.length === 0)
|
|
45
|
-
throw new Error(
|
|
39
|
+
throw new Error(`The MP3 and M4B files need narration audio, but this project has none. Turn narration on in ${editSettings.narration}, or untick Also make MP3 and M4B files under Speakers (${createRows.narration}, or ${editSettings.narration}), then Try again.`);
|
|
46
40
|
const totalSeconds = audio.reduce((sum, segment) => sum + segment.seconds, 0);
|
|
47
41
|
const chapters = audioChapters({
|
|
48
42
|
title: view.revision.config.title,
|