@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
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { constants, setPriority } from "node:os";
|
|
2
|
+
import { errorText } from "../../kernel/errors.js";
|
|
3
|
+
import { lookEncoding } from "./look.js";
|
|
4
|
+
// Settings → Production defaults → Rendering: how this computer encodes a video's picture.
|
|
5
|
+
// They change how a video is made, never what it is, so no recipe fingerprint carries them and
|
|
6
|
+
// a render reads them once as it starts (`renderMachine`).
|
|
7
|
+
export const videoEncoders = ["cpu", "nvidia"];
|
|
8
|
+
export const clipEncodeSpeeds = ["thorough", "fast"];
|
|
9
|
+
export const renderPriorities = ["normal", "background"];
|
|
10
|
+
// Today's render: x264 at its default preset, at normal priority.
|
|
11
|
+
export const defaultEncode = { kind: "x264", clipSpeed: "thorough" };
|
|
12
|
+
// Clips that are joined by copying get the final encode's settings. Clips that will be decoded
|
|
13
|
+
// again under burned-in captions are kept closer to the source, so the second encode is not a
|
|
14
|
+
// visible second generation.
|
|
15
|
+
const intermediateCrf = "16";
|
|
16
|
+
// measured on two minutes of a real project (changelog.d/2026-10-13-render-settings.md): 20
|
|
17
|
+
// matches CRF 16's quality for the clips under captions; 28.5 makes the finished video about the
|
|
18
|
+
// size CRF 23 does, where NVENC scores about 2.5 VMAF below x264 (no NVENC setting tried closed
|
|
19
|
+
// that gap at that size).
|
|
20
|
+
export const nvencQuality = { intermediate: 20, final: 28.5 };
|
|
21
|
+
// `final` is a picture someone watches (a clip joined by copying, a burned-in part, the join);
|
|
22
|
+
// `intermediate` is a clip decoded again under burned-in captions.
|
|
23
|
+
export function codecArgs(encode, role, look) {
|
|
24
|
+
if (encode.kind === "nvenc")
|
|
25
|
+
return [
|
|
26
|
+
"-c:v",
|
|
27
|
+
"h264_nvenc",
|
|
28
|
+
// The slowest, best preset, constant quality with no bitrate cap. No `-multipass`: measured,
|
|
29
|
+
// a second full-resolution pass gave no better picture for its size on these videos.
|
|
30
|
+
"-preset",
|
|
31
|
+
"p7",
|
|
32
|
+
"-tune",
|
|
33
|
+
"hq",
|
|
34
|
+
"-rc",
|
|
35
|
+
"vbr",
|
|
36
|
+
"-cq",
|
|
37
|
+
String(nvencQuality[role]),
|
|
38
|
+
"-b:v",
|
|
39
|
+
"0",
|
|
40
|
+
// x264's own keyframe interval, so a clip opens on its one keyframe as x264's do and the
|
|
41
|
+
// clips still join by copying.
|
|
42
|
+
"-g",
|
|
43
|
+
"250",
|
|
44
|
+
"-pix_fmt",
|
|
45
|
+
"yuv420p",
|
|
46
|
+
];
|
|
47
|
+
return [
|
|
48
|
+
"-c:v",
|
|
49
|
+
"libx264",
|
|
50
|
+
"-pix_fmt",
|
|
51
|
+
"yuv420p",
|
|
52
|
+
...lookEncoding(look),
|
|
53
|
+
...(role === "intermediate"
|
|
54
|
+
? [...(encode.clipSpeed === "fast" ? ["-preset", "veryfast"] : []), "-crf", intermediateCrf]
|
|
55
|
+
: []),
|
|
56
|
+
];
|
|
57
|
+
}
|
|
58
|
+
// Said to a person who may have no NVIDIA card at all: stock ffmpeg builds list the NVIDIA
|
|
59
|
+
// encoder everywhere, so a failed test usually means there is no card, not a broken one. What
|
|
60
|
+
// the test printed goes under Details (`detail`), never into these sentences.
|
|
61
|
+
export const noNvenc = "No NVIDIA graphics card was found on this computer.";
|
|
62
|
+
export const failedNvenc = "No working NVIDIA graphics card was found on this computer.";
|
|
63
|
+
// The reason with the test's output, for the log and a run's notice.
|
|
64
|
+
export function nvencWhy(status) {
|
|
65
|
+
return status.detail === undefined ? status.reason : `${status.reason} (${status.detail})`;
|
|
66
|
+
}
|
|
67
|
+
// The renders' own settings on a tiny picture: a driver or card that refuses any of them fails
|
|
68
|
+
// here rather than in a render. NVENC refuses anything under about 145 pixels across.
|
|
69
|
+
const testArgs = [
|
|
70
|
+
"-hide_banner",
|
|
71
|
+
"-nostdin",
|
|
72
|
+
"-loglevel",
|
|
73
|
+
"error",
|
|
74
|
+
"-f",
|
|
75
|
+
"lavfi",
|
|
76
|
+
"-i",
|
|
77
|
+
"color=c=black:s=256x256:r=30:d=0.1",
|
|
78
|
+
"-frames:v",
|
|
79
|
+
"1",
|
|
80
|
+
...codecArgs({ kind: "nvenc", bin: "", clipSpeed: "thorough" }, "final", undefined),
|
|
81
|
+
"-f",
|
|
82
|
+
"null",
|
|
83
|
+
"-",
|
|
84
|
+
];
|
|
85
|
+
// The filters a render runs in the NVENC ffmpeg besides the encoder.
|
|
86
|
+
const neededFilters = ["zoompan", "xfade", "ass"];
|
|
87
|
+
// Which ffmpeg can encode on an NVIDIA card, asked once and remembered until Slopify restarts.
|
|
88
|
+
// The bundled ffmpeg first, then the computer's own: unlike everything else, NVENC is the
|
|
89
|
+
// computer's (its driver and its ffmpeg), and only used when chosen in Settings.
|
|
90
|
+
export function createNvencProbe(deps) {
|
|
91
|
+
let answer;
|
|
92
|
+
return () => {
|
|
93
|
+
answer ??= probe(deps).catch((error) => ({
|
|
94
|
+
available: false,
|
|
95
|
+
reason: noNvenc,
|
|
96
|
+
detail: errorText(error),
|
|
97
|
+
}));
|
|
98
|
+
return answer;
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
async function probe(deps) {
|
|
102
|
+
let failed;
|
|
103
|
+
for (const bin of new Set(deps.candidates)) {
|
|
104
|
+
const encoders = await deps.exec(bin, ["-hide_banner", "-encoders"]).catch(() => "");
|
|
105
|
+
if (!/^\s*V\S*\s+h264_nvenc\s/m.test(encoders))
|
|
106
|
+
continue;
|
|
107
|
+
const filters = await deps.exec(bin, ["-hide_banner", "-filters"]).catch(() => "");
|
|
108
|
+
const missing = neededFilters.filter((name) => !new RegExp(`^\\s*\\S+\\s+${name}\\s`, "m").test(filters));
|
|
109
|
+
if (missing.length > 0) {
|
|
110
|
+
failed = `the ffmpeg at ${bin} has NVENC but not the ${missing.join(", ")} filter a video needs`;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
await deps.exec(bin, testArgs);
|
|
115
|
+
deps.log.write("info", "video.encoder", { detail: `NVENC works through ${bin}` });
|
|
116
|
+
return { available: true, bin };
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
failed = `the ffmpeg at ${bin} lists NVENC but its test encode failed: ${errorText(error)}`;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (failed !== undefined) {
|
|
123
|
+
deps.log.write("info", "video.encoder", { detail: failed });
|
|
124
|
+
return { available: false, reason: failedNvenc, detail: failed };
|
|
125
|
+
}
|
|
126
|
+
return { available: false, reason: noNvenc };
|
|
127
|
+
}
|
|
128
|
+
// The render's machine settings, read as it starts. A card chosen but not working is a notice,
|
|
129
|
+
// never a failed render: the processor encodes it instead.
|
|
130
|
+
export async function renderMachine(deps) {
|
|
131
|
+
const { settings } = deps;
|
|
132
|
+
const cpu = { kind: "x264", clipSpeed: settings.clipEncodeSpeed };
|
|
133
|
+
if (settings.videoEncoder !== "nvidia")
|
|
134
|
+
return { machine: { encode: cpu, priority: settings.renderPriority } };
|
|
135
|
+
const status = await deps.nvenc();
|
|
136
|
+
if (status.available)
|
|
137
|
+
return {
|
|
138
|
+
machine: {
|
|
139
|
+
encode: { kind: "nvenc", bin: status.bin, clipSpeed: settings.clipEncodeSpeed },
|
|
140
|
+
priority: settings.renderPriority,
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
deps.log.write("warn", "video.encoder", {
|
|
144
|
+
projectId: deps.projectId,
|
|
145
|
+
detail: `NVIDIA encoder chosen but unavailable; encoding on the processor. ${nvencWhy(status)}`,
|
|
146
|
+
});
|
|
147
|
+
return {
|
|
148
|
+
machine: { encode: cpu, priority: settings.renderPriority },
|
|
149
|
+
notice: nvencFallbackNotice(nvencWhy(status)),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
export function nvencFallbackNotice(reason) {
|
|
153
|
+
return `This video was encoded on the processor because the NVIDIA graphics card encoder didn't work (${reason.replace(/\.$/, "")}). Check the NVIDIA driver, or choose Processor (x264) in Settings → Production defaults → Video encoder.`;
|
|
154
|
+
}
|
|
155
|
+
// Background renders run below normal priority: nice 10 on Linux and macOS, the below-normal
|
|
156
|
+
// class on Windows. Threads ffmpeg starts afterwards inherit it. Advisory: a system that
|
|
157
|
+
// refuses is logged and the render carries on.
|
|
158
|
+
export function setRenderPriority(pid, priority, log, set = setPriority) {
|
|
159
|
+
if (priority !== "background")
|
|
160
|
+
return;
|
|
161
|
+
try {
|
|
162
|
+
set(pid, constants.priority.PRIORITY_BELOW_NORMAL);
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
log.write("warn", "video.priority", {
|
|
166
|
+
detail: `could not lower the render's priority: ${errorText(error)}`,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { isAbsolute, resolve as resolvePath } from "node:path";
|
|
3
|
+
import { errorText, internalError } from "../../kernel/errors.js";
|
|
3
4
|
import { bedChains, bedInputs } from "./ambient-mix.js";
|
|
4
|
-
import {
|
|
5
|
+
import { codecArgs, defaultEncode, setRenderPriority, } from "./encoding.js";
|
|
6
|
+
import { gradeFilter, lookChain, placed } from "./look.js";
|
|
5
7
|
import { decimal, zoomRange } from "./plan.js";
|
|
6
8
|
import { segments, transitionHalves } from "./transitions.js";
|
|
7
9
|
// Hand-rolled per the standards: the filtergraph is the load-bearing part of this slice
|
|
@@ -44,7 +46,7 @@ export function slideshowClips(edit) {
|
|
|
44
46
|
named.set(key, name);
|
|
45
47
|
const shot = edit.shots[segment.kind === "shot" ? segment.shot : segment.to];
|
|
46
48
|
if (shot === undefined)
|
|
47
|
-
throw new Error("
|
|
49
|
+
throw new Error(internalError("the video's edit list lost a shot", "Press Make the video again in the project's Video section"));
|
|
48
50
|
clips.push({ name, segment, shot, frames: segment.count });
|
|
49
51
|
}
|
|
50
52
|
return name;
|
|
@@ -77,10 +79,6 @@ function clipKey(edit, segment) {
|
|
|
77
79
|
? [shot?.source, shot?.motion, shot?.frames]
|
|
78
80
|
: [shot?.source, shot?.motion, shot?.frames, segment.from, segment.count, at]);
|
|
79
81
|
}
|
|
80
|
-
// Clips that are joined by copying get the final encode's settings. Clips that will be
|
|
81
|
-
// decoded again under burned-in captions are kept closer to the source, so the second
|
|
82
|
-
// encode is not a visible second generation.
|
|
83
|
-
const intermediateCrf = "16";
|
|
84
82
|
// A whole shot as one clip.
|
|
85
83
|
export function clipArgs(frame, shot, output, intermediate = false) {
|
|
86
84
|
return segmentArgs({ ...frame, shots: [shot] }, { kind: "shot", shot: 0, from: 0, count: shot.frames, start: 0 }, output, intermediate);
|
|
@@ -88,12 +86,14 @@ export function clipArgs(frame, shot, output, intermediate = false) {
|
|
|
88
86
|
// One clip of the slideshow: part of a shot, or a transition between two, with the Look.
|
|
89
87
|
export function segmentArgs(edit, segment, output, intermediate = false,
|
|
90
88
|
// The clip's own chapter-card script, beside it in the working folder (`cards.ts`).
|
|
91
|
-
cardsFile = "cards.ass"
|
|
89
|
+
cardsFile = "cards.ass",
|
|
90
|
+
// Settings → Production defaults → Rendering (`encoding.ts`).
|
|
91
|
+
encode = defaultEncode) {
|
|
92
92
|
const inputs = [];
|
|
93
93
|
const chains = [];
|
|
94
94
|
const picture = (shot, from, label) => {
|
|
95
95
|
if (shot === undefined)
|
|
96
|
-
throw new Error("
|
|
96
|
+
throw new Error(internalError("the video's edit list lost a shot", "Press Make the video again in the project's Video section"));
|
|
97
97
|
const at = inputs.filter((one) => one === "-i").length;
|
|
98
98
|
const built = pictureChain(edit, shot, from, segment.count, at, label, gradeFilter(edit.look));
|
|
99
99
|
inputs.push(...built.inputs);
|
|
@@ -125,9 +125,7 @@ cardsFile = "cards.ass") {
|
|
|
125
125
|
...(segment.kind === "shot" && segment.from === 0 && look === undefined
|
|
126
126
|
? []
|
|
127
127
|
: ["-frames:v", String(segment.count)]),
|
|
128
|
-
...
|
|
129
|
-
...lookEncoding(edit.look),
|
|
130
|
-
...(intermediate ? ["-crf", intermediateCrf] : []),
|
|
128
|
+
...codecArgs(encode, intermediate ? "intermediate" : "final", edit.look),
|
|
131
129
|
"-an",
|
|
132
130
|
output,
|
|
133
131
|
];
|
|
@@ -257,7 +255,7 @@ export function audioMixArgs(edit, output, codec = "wav") {
|
|
|
257
255
|
}
|
|
258
256
|
// `sound`, when given, is the finished AAC (`audioMixArgs`), copied in; without it the join
|
|
259
257
|
// mixes and encodes the edit list's audio itself.
|
|
260
|
-
export function joinArgs(edit, output, list, burnSubtitles = false, portraits = [], sound) {
|
|
258
|
+
export function joinArgs(edit, output, list, burnSubtitles = false, portraits = [], sound, encode = defaultEncode) {
|
|
261
259
|
const mix = sound === undefined ? audioMix(edit, 1) : { inputs: ["-i", sound], chains: [] };
|
|
262
260
|
const inputs = ["-f", "concat", "-i", list, ...mix.inputs];
|
|
263
261
|
const chains = burnSubtitles ? burnIn(inputs, portraits) : [];
|
|
@@ -270,7 +268,7 @@ export function joinArgs(edit, output, list, burnSubtitles = false, portraits =
|
|
|
270
268
|
"-map",
|
|
271
269
|
burnSubtitles ? "[v]" : "0:v",
|
|
272
270
|
...(audio ? ["-map", sound === undefined ? "[a]" : "1:a"] : []),
|
|
273
|
-
...(burnSubtitles ?
|
|
271
|
+
...(burnSubtitles ? codecArgs(encode, "final", edit.look) : ["-c:v", "copy"]),
|
|
274
272
|
...(audio ? ["-c:a", sound === undefined ? "aac" : "copy"] : ["-an"]),
|
|
275
273
|
"-movflags",
|
|
276
274
|
"+faststart",
|
|
@@ -280,7 +278,7 @@ export function joinArgs(edit, output, list, burnSubtitles = false, portraits =
|
|
|
280
278
|
// One part of a long video's picture with its captions burned in, from frame `start` of the
|
|
281
279
|
// video (`slideshow.ts` encodes the parts side by side, then joins them by copying). The part's
|
|
282
280
|
// frames are moved to their place in the video for the captions and back to zero after them.
|
|
283
|
-
export function burnPartArgs(edit, list, output, start, portraits = []) {
|
|
281
|
+
export function burnPartArgs(edit, list, output, start, portraits = [], encode = defaultEncode) {
|
|
284
282
|
const inputs = ["-f", "concat", "-i", list];
|
|
285
283
|
const shifted = start === 0
|
|
286
284
|
? { before: "", after: "" }
|
|
@@ -298,8 +296,7 @@ export function burnPartArgs(edit, list, output, start, portraits = []) {
|
|
|
298
296
|
chains.join(";"),
|
|
299
297
|
"-map",
|
|
300
298
|
"[v]",
|
|
301
|
-
...
|
|
302
|
-
...lookEncoding(edit.look),
|
|
299
|
+
...codecArgs(encode, "final", edit.look),
|
|
303
300
|
"-an",
|
|
304
301
|
output,
|
|
305
302
|
];
|
|
@@ -333,7 +330,6 @@ const progressArgs = [
|
|
|
333
330
|
"-nostats",
|
|
334
331
|
"-y",
|
|
335
332
|
];
|
|
336
|
-
const videoCodec = ["-c:v", "libx264", "-pix_fmt", "yuv420p"];
|
|
337
333
|
const centredX = "iw/2-(iw/zoom/2)";
|
|
338
334
|
const centredY = "ih/2-(ih/zoom/2)";
|
|
339
335
|
// zoompan's expressions for a motion, or undefined for one that does not move. `on` is
|
|
@@ -403,6 +399,9 @@ export function runFfmpeg(run) {
|
|
|
403
399
|
windowsHide: true,
|
|
404
400
|
...(run.cwd === undefined ? {} : { cwd: run.cwd }),
|
|
405
401
|
});
|
|
402
|
+
// Before ffmpeg starts its threads, which inherit the priority.
|
|
403
|
+
if (child.pid !== undefined && run.priority !== undefined)
|
|
404
|
+
setRenderPriority(child.pid, run.priority, run.log);
|
|
406
405
|
const errors = [];
|
|
407
406
|
let pending = "";
|
|
408
407
|
// A throw out of a stream listener is an uncaughtException, not a rejection: it would
|
|
@@ -413,7 +412,7 @@ export function runFfmpeg(run) {
|
|
|
413
412
|
work();
|
|
414
413
|
}
|
|
415
414
|
catch (error) {
|
|
416
|
-
run.log.write("warn", what, { detail:
|
|
415
|
+
run.log.write("warn", what, { detail: errorText(error) });
|
|
417
416
|
}
|
|
418
417
|
};
|
|
419
418
|
const report = (line) => {
|
|
@@ -508,9 +507,6 @@ export async function probeDurationMs(bin, file, signal, log) {
|
|
|
508
507
|
});
|
|
509
508
|
return last;
|
|
510
509
|
}
|
|
511
|
-
function messageOf(error) {
|
|
512
|
-
return error instanceof Error ? error.message : String(error);
|
|
513
|
-
}
|
|
514
510
|
function failure(code, signal, errors) {
|
|
515
511
|
const ended = code === null ? `killed by ${signal ?? "a signal"}` : `exited with code ${code}`;
|
|
516
512
|
const said = errors.length === 0
|
|
@@ -518,3 +514,9 @@ function failure(code, signal, errors) {
|
|
|
518
514
|
: `ffmpeg ${ended}: ${errors.join(" / ")}`;
|
|
519
515
|
return `The audio/video export failed (${said}). Try again; if it fails again, make sure your disk has free space (see Settings → Backup & storage), then use Download diagnostics in Settings and report it.`;
|
|
520
516
|
}
|
|
517
|
+
// `runFfmpeg` at Settings → Production defaults → Render priority, read as each call starts: for
|
|
518
|
+
// the work outside a video's render that still runs ffmpeg on this computer (a design's drawing,
|
|
519
|
+
// a post's picture, Template Studio's free samples). A priority the caller set itself is kept.
|
|
520
|
+
export function runFfmpegAt(priority, run = runFfmpeg) {
|
|
521
|
+
return (call) => run({ ...call, priority: call.priority ?? priority() });
|
|
522
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { remark } from "remark";
|
|
2
2
|
import remarkGfm from "remark-gfm";
|
|
3
|
+
import { assText, assColour as colour } from "./ass-text.js";
|
|
3
4
|
import { runFfmpeg } from "./ffmpeg.js";
|
|
4
5
|
// The 3.0 design tokens (`packages/web/src/styles/index.css`): the surface graphite, ink,
|
|
5
6
|
// the second ink, Slopify lime and the line colour, in the bundled Barlow.
|
|
@@ -449,21 +450,6 @@ function wrap(textValue, width, most) {
|
|
|
449
450
|
function clip(value, room) {
|
|
450
451
|
return value.length <= room ? value : `${value.slice(0, Math.max(1, room - 1))}…`;
|
|
451
452
|
}
|
|
452
|
-
// Text as libass draws it literally: braces and backslashes escaped rather than read as
|
|
453
|
-
// override tags; code keeps its spaces, prose has them collapsed.
|
|
454
|
-
function assText(value, keepSpaces = false) {
|
|
455
|
-
const escaped = value.replace(/\\/g, "\\\\").replace(/\{/g, "\\{").replace(/\}/g, "\\}");
|
|
456
|
-
return keepSpaces
|
|
457
|
-
? escaped.replace(/ {2,}/g, (run) => "\\h".repeat(run.length))
|
|
458
|
-
: escaped.replace(/\s+/g, " ");
|
|
459
|
-
}
|
|
460
|
-
function colour(hex) {
|
|
461
|
-
const match = /^#([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})$/.exec(hex);
|
|
462
|
-
if (match === null)
|
|
463
|
-
return "&HFFFFFF&";
|
|
464
|
-
const [, r, g, b] = match;
|
|
465
|
-
return `&H${(b ?? "").toUpperCase()}${(g ?? "").toUpperCase()}${(r ?? "").toUpperCase()}&`;
|
|
466
|
-
}
|
|
467
453
|
function text(x, y, align, body) {
|
|
468
454
|
return `Dialogue: 0,0:00:00.00,0:00:10.00,Card,,0,0,0,,{\\an${String(align)}\\pos(${String(Math.round(x))},${String(Math.round(y))})}${body}`;
|
|
469
455
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { editSettings } from "../../kernel/ports/settings-places.js";
|
|
1
2
|
import { chapterCuts, narrationShotFrames } from "./cuts.js";
|
|
2
3
|
import { editListVersion, } from "./edit-list.js";
|
|
3
4
|
import { chapterCardSeconds, endScreenSeconds } from "./edit-settings.js";
|
|
@@ -41,7 +42,7 @@ const figureGapSeconds = 1;
|
|
|
41
42
|
export function planRender(input) {
|
|
42
43
|
if (input.images.length === 0) {
|
|
43
44
|
// Admission permits Video only with an image source, so an empty set is a bug upstream.
|
|
44
|
-
throw new Error(
|
|
45
|
+
throw new Error(`The video needs at least one image, but there are none. Check ${editSettings.images}, then Try again.`);
|
|
45
46
|
}
|
|
46
47
|
const frame = frames[input.format];
|
|
47
48
|
const bed = input.body === undefined ? undefined : input.bed;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { copyFileSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { extname, join } from "node:path";
|
|
3
|
+
import { errorText, internalError } from "../../kernel/errors.js";
|
|
3
4
|
import { masterFile, masterReport } from "../loudness/loudnorm.js";
|
|
4
5
|
import { cardsAss } from "./cards.js";
|
|
5
6
|
import { cacheKey, fileIdentity, keepClip, pruneClips, reuseClip } from "./clip-cache.js";
|
|
7
|
+
import { defaultEncode, nvencFallbackNotice, } from "./encoding.js";
|
|
6
8
|
import { audioMixArgs, burnPartArgs, concatList, joinArgs, runFfmpeg, segmentArgs, slideshowClips, } from "./ffmpeg.js";
|
|
7
9
|
import { cardsIn } from "./look.js";
|
|
8
10
|
import { inPool, renderJobs } from "./pool.js";
|
|
@@ -14,9 +16,31 @@ const copyWeight = 0.05;
|
|
|
14
16
|
const leastPartFrames = 60 * 30;
|
|
15
17
|
// The sound the join plays, as the join itself brings every segment to it.
|
|
16
18
|
const masterFormat = { sampleRate: 44100, channels: 2 };
|
|
17
|
-
// What the master measured, when the run asked for one.
|
|
19
|
+
// What the master measured, when the run asked for one. A render on the NVIDIA card that fails
|
|
20
|
+
// for any reason but a cancel is made again on the processor: the card never fails a render.
|
|
18
21
|
export async function renderSlideshow(run) {
|
|
22
|
+
const encode = run.machine?.encode ?? defaultEncode;
|
|
23
|
+
if (encode.kind !== "nvenc")
|
|
24
|
+
return renderWith(run, encode);
|
|
25
|
+
try {
|
|
26
|
+
return await renderWith(run, encode);
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
if (run.signal.aborted)
|
|
30
|
+
throw error;
|
|
31
|
+
const detail = errorText(error);
|
|
32
|
+
run.log.write("warn", "video.encoder", {
|
|
33
|
+
detail: `the NVENC render failed; rendering again on the processor: ${detail}`,
|
|
34
|
+
});
|
|
35
|
+
run.notice?.(nvencFallbackNotice(`it stopped the render: ${detail}`));
|
|
36
|
+
return renderWith(run, { kind: "x264", clipSpeed: encode.clipSpeed });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async function renderWith(run, encode) {
|
|
19
40
|
const { edit } = run;
|
|
41
|
+
const priority = run.machine?.priority;
|
|
42
|
+
// The ffmpeg that encodes the picture; copying, sound and probing stay with the bundled one.
|
|
43
|
+
const encoder = encode.kind === "nvenc" ? encode.bin : run.bin;
|
|
20
44
|
const { clips, order } = slideshowClips(edit);
|
|
21
45
|
const msOf = (frames) => (frames / edit.fps) * 1000;
|
|
22
46
|
const joinMs = msOf(edit.shots.reduce((sum, shot) => sum + shot.frames, 0));
|
|
@@ -38,7 +62,7 @@ export async function renderSlideshow(run) {
|
|
|
38
62
|
const font = edit.cardFont;
|
|
39
63
|
if (cards.length > 0) {
|
|
40
64
|
if (font === undefined)
|
|
41
|
-
throw new Error("
|
|
65
|
+
throw new Error(internalError("the chapter cards have no font", "Press Make the video again in the project's Video section"));
|
|
42
66
|
mkdirSync(join(workspace, "fonts"), { mode: 0o700 });
|
|
43
67
|
copyFileSync(font.path, join(workspace, "fonts", `card${extname(font.path)}`));
|
|
44
68
|
}
|
|
@@ -81,7 +105,7 @@ export async function renderSlideshow(run) {
|
|
|
81
105
|
if (drawn !== undefined)
|
|
82
106
|
writeFileSync(join(workspace, script), drawn, { mode: 0o600 });
|
|
83
107
|
const output = join(workspace, clip.name);
|
|
84
|
-
const args = segmentArgs(edit, clip.segment, output, run.burnSubtitles, script);
|
|
108
|
+
const args = segmentArgs(edit, clip.segment, output, run.burnSubtitles, script, encode);
|
|
85
109
|
const weight = msOf(clip.frames) * clipWeight;
|
|
86
110
|
const cache = run.cache;
|
|
87
111
|
let key;
|
|
@@ -89,7 +113,7 @@ export async function renderSlideshow(run) {
|
|
|
89
113
|
// let it go a moment ago) is encoded as if there were none.
|
|
90
114
|
try {
|
|
91
115
|
if (cache !== undefined) {
|
|
92
|
-
key = cacheKey(
|
|
116
|
+
key = cacheKey(encoder, args.map((value) => drawn === undefined ? value : value.replaceAll(script, "\u0000cards")), output, drawn === undefined || font === undefined ? [] : [drawn, fileIdentity(font.path)]);
|
|
93
117
|
keys.add(key);
|
|
94
118
|
if (reuseClip(cache, key, output)) {
|
|
95
119
|
rendered += weight;
|
|
@@ -99,15 +123,16 @@ export async function renderSlideshow(run) {
|
|
|
99
123
|
}
|
|
100
124
|
}
|
|
101
125
|
catch (error) {
|
|
102
|
-
run.log.write("warn", "video.cache", { detail:
|
|
126
|
+
run.log.write("warn", "video.cache", { detail: errorText(error) });
|
|
103
127
|
rmSync(output, { force: true });
|
|
104
128
|
}
|
|
105
129
|
await runFfmpeg({
|
|
106
|
-
bin:
|
|
130
|
+
bin: encoder,
|
|
107
131
|
...(cards.length > 0 ? { cwd: workspace } : {}),
|
|
108
132
|
args,
|
|
109
133
|
signal,
|
|
110
134
|
log: run.log,
|
|
135
|
+
priority,
|
|
111
136
|
onProgress: (elapsedMs) => {
|
|
112
137
|
running.set(clip.name, Math.min(elapsedMs * clipWeight, weight));
|
|
113
138
|
tell();
|
|
@@ -120,7 +145,7 @@ export async function renderSlideshow(run) {
|
|
|
120
145
|
keepClip(cache, key, output);
|
|
121
146
|
}
|
|
122
147
|
catch (error) {
|
|
123
|
-
run.log.write("warn", "video.cache", { detail:
|
|
148
|
+
run.log.write("warn", "video.cache", { detail: errorText(error) });
|
|
124
149
|
}
|
|
125
150
|
}
|
|
126
151
|
});
|
|
@@ -138,11 +163,12 @@ export async function renderSlideshow(run) {
|
|
|
138
163
|
const partList = join(workspace, `${part.name}.ffconcat`);
|
|
139
164
|
writeFileSync(partList, concatList(part.names), { mode: 0o600 });
|
|
140
165
|
await runFfmpeg({
|
|
141
|
-
bin:
|
|
166
|
+
bin: encoder,
|
|
142
167
|
cwd: run.cwd,
|
|
143
|
-
args: burnPartArgs(edit, partList, join(workspace, `${part.name}.mp4`), part.start, run.portraits),
|
|
168
|
+
args: burnPartArgs(edit, partList, join(workspace, `${part.name}.mp4`), part.start, run.portraits, encode),
|
|
144
169
|
signal,
|
|
145
170
|
log: run.log,
|
|
171
|
+
priority,
|
|
146
172
|
onProgress: (elapsedMs) => {
|
|
147
173
|
done.set(part.name, Math.min(elapsedMs, msOf(part.frames)));
|
|
148
174
|
burned();
|
|
@@ -167,11 +193,12 @@ export async function renderSlideshow(run) {
|
|
|
167
193
|
throw new Error("the render was canceled");
|
|
168
194
|
const burnsHere = run.burnSubtitles && parts.length <= 1;
|
|
169
195
|
await runFfmpeg({
|
|
170
|
-
bin: run.bin,
|
|
196
|
+
bin: burnsHere ? encoder : run.bin,
|
|
171
197
|
cwd: run.cwd,
|
|
172
|
-
args: joinArgs(edit, run.output, played, burnsHere, run.portraits, sound),
|
|
198
|
+
args: joinArgs(edit, run.output, played, burnsHere, run.portraits, sound, encode),
|
|
173
199
|
signal: run.signal,
|
|
174
200
|
log: run.log,
|
|
201
|
+
priority,
|
|
175
202
|
onProgress: (elapsedMs) => report(rendered + elapsedMs * (burnsHere ? 1 : copyWeight)),
|
|
176
203
|
});
|
|
177
204
|
if (run.cache !== undefined) {
|
|
@@ -179,12 +206,12 @@ export async function renderSlideshow(run) {
|
|
|
179
206
|
pruneClips(run.cache, keys);
|
|
180
207
|
}
|
|
181
208
|
catch (error) {
|
|
182
|
-
run.log.write("warn", "video.cache", { detail:
|
|
209
|
+
run.log.write("warn", "video.cache", { detail: errorText(error) });
|
|
183
210
|
}
|
|
184
211
|
}
|
|
185
212
|
// What the finished video measures, after its AAC encode.
|
|
186
213
|
return run.master !== undefined && edit.audio.length > 0
|
|
187
|
-
? await masterReport(run, run.output, run.master)
|
|
214
|
+
? await masterReport({ ...run, priority }, run.output, run.master)
|
|
188
215
|
: undefined;
|
|
189
216
|
}
|
|
190
217
|
finally {
|
|
@@ -194,13 +221,14 @@ export async function renderSlideshow(run) {
|
|
|
194
221
|
// The video's finished sound as AAC in the working folder: the narration, silences and bed
|
|
195
222
|
// mixed, and with Level the volume mastered first, alone and then as one file.
|
|
196
223
|
async function renderSound(run, edit, workspace, signal) {
|
|
197
|
-
const
|
|
224
|
+
const priority = run.machine?.priority;
|
|
225
|
+
const quiet = { bin: run.bin, signal, log: run.log, priority, onProgress: () => { } };
|
|
198
226
|
let played = edit;
|
|
199
227
|
if (run.master !== undefined) {
|
|
200
228
|
const mixed = join(workspace, "mix.wav");
|
|
201
229
|
await runFfmpeg({ ...quiet, args: audioMixArgs(edit, mixed) });
|
|
202
230
|
const mastered = join(workspace, "master.wav");
|
|
203
|
-
await masterFile({ ...run, signal }, mixed, mastered, run.master, masterFormat);
|
|
231
|
+
await masterFile({ ...run, signal, priority }, mixed, mastered, run.master, masterFormat);
|
|
204
232
|
played = {
|
|
205
233
|
...edit,
|
|
206
234
|
audio: [
|
|
@@ -249,6 +277,3 @@ export function burnParts(order, clips, jobs) {
|
|
|
249
277
|
parts.push({ name: `p${String(parts.length + 1)}`, names, start, frames: at - start });
|
|
250
278
|
return parts;
|
|
251
279
|
}
|
|
252
|
-
function messageOf(error) {
|
|
253
|
-
return error instanceof Error ? error.message : String(error);
|
|
254
|
-
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { editSettings } from "../../kernel/ports/settings-places.js";
|
|
1
2
|
import { parseScript } from "./script.js";
|
|
2
3
|
// Audiobooks from an existing text: the text model hands each passage to a speaker - the
|
|
3
4
|
// narrator's prose to the narrator, each quoted line to the character who says it - and
|
|
@@ -35,12 +36,12 @@ export function parseAttribution(answer, source, speakers) {
|
|
|
35
36
|
if (expected.length > 0 && kept / expected.length < 0.9)
|
|
36
37
|
return {
|
|
37
38
|
ok: false,
|
|
38
|
-
reason: `The speaker split left out ${String(expected.length - kept)} of the text's ${String(expected.length)} words. The
|
|
39
|
+
reason: `The speaker split left out ${String(expected.length - kept)} of the text's ${String(expected.length)} words. The AI model must hand every word to a speaker; Try again, or choose another model in ${editSettings.writer}.`,
|
|
39
40
|
};
|
|
40
41
|
if (got.length - kept > Math.max(3, expected.length * 0.1))
|
|
41
42
|
return {
|
|
42
43
|
ok: false,
|
|
43
|
-
reason: `The speaker split added ${String(got.length - kept)} words that are not in the text. The
|
|
44
|
+
reason: `The speaker split added ${String(got.length - kept)} words that are not in the text. The AI model must not write anything of its own; Try again, or choose another model in ${editSettings.writer}.`,
|
|
44
45
|
};
|
|
45
46
|
return parsed;
|
|
46
47
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { createRows, editSettings } from "../../kernel/ports/settings-places.js";
|
|
2
3
|
// Multiple voices: a run whose narration is a script of speaker turns rather than one voice
|
|
3
4
|
// reading an article. Absent on a run is the Narration format, which is every project saved
|
|
4
5
|
// before this existed, and nothing about such a run reads anything in this slice.
|
|
@@ -136,23 +137,23 @@ export function voicesProblems(settings) {
|
|
|
136
137
|
if (speakers.length === 0)
|
|
137
138
|
problems.push({
|
|
138
139
|
field: "voices.speakers",
|
|
139
|
-
message:
|
|
140
|
+
message: `Add at least one speaker under Speakers (${createRows.narration}, or ${editSettings.narration}).`,
|
|
140
141
|
});
|
|
141
142
|
if (settings.source === "attribute" && settings.format !== "audiobook")
|
|
142
143
|
problems.push({
|
|
143
144
|
field: "voices.source",
|
|
144
|
-
message:
|
|
145
|
+
message: `Only audiobooks can hand an existing text's dialogue to speakers. Choose Write a script under Speakers (${createRows.narration}, or ${editSettings.narration}).`,
|
|
145
146
|
});
|
|
146
147
|
if ((settings.format === "podcast" || settings.format === "interview") && speakers.length < 2)
|
|
147
148
|
problems.push({
|
|
148
149
|
field: "voices.speakers",
|
|
149
|
-
message: `A ${voiceFormatLabels[settings.format].toLowerCase()} needs at least two speakers. Add one under Speakers (
|
|
150
|
+
message: `A ${voiceFormatLabels[settings.format].toLowerCase()} needs at least two speakers. Add one under Speakers (${createRows.narration}, or ${editSettings.narration}).`,
|
|
150
151
|
});
|
|
151
152
|
if ((settings.format === "audiobook" || settings.format === "drama") &&
|
|
152
153
|
!speakers.some((speaker) => speaker.role === "narrator"))
|
|
153
154
|
problems.push({
|
|
154
155
|
field: "voices.speakers",
|
|
155
|
-
message: `An ${settings.format === "drama" ? "radio drama" : "audiobook"} needs a narrator. Set one speaker's role to Narrator under Speakers (
|
|
156
|
+
message: `An ${settings.format === "drama" ? "radio drama" : "audiobook"} needs a narrator. Set one speaker's role to Narrator under Speakers (${createRows.narration}, or ${editSettings.narration}).`,
|
|
156
157
|
});
|
|
157
158
|
const names = new Set();
|
|
158
159
|
const ids = new Set();
|
|
@@ -181,7 +182,7 @@ export function voicesProblems(settings) {
|
|
|
181
182
|
if (speaker.voice.provider.trim() === "" || speaker.voice.model.trim() === "")
|
|
182
183
|
problems.push({
|
|
183
184
|
field: `${field}.voice`,
|
|
184
|
-
message: `Choose a voice
|
|
185
|
+
message: `Choose a voice service and model for ${name || "this speaker"}.`,
|
|
185
186
|
});
|
|
186
187
|
else if (speaker.voice.voice.trim() === "")
|
|
187
188
|
problems.push({
|
|
@@ -198,20 +199,20 @@ export function voicesProblems(settings) {
|
|
|
198
199
|
if (settings.book.title.trim() === "")
|
|
199
200
|
problems.push({
|
|
200
201
|
field: "voices.book.title",
|
|
201
|
-
message:
|
|
202
|
+
message: `Enter the book's title under Speakers (${createRows.narration}, or ${editSettings.narration}), or remove the book to make a project on its own.`,
|
|
202
203
|
});
|
|
203
204
|
if (!Number.isInteger(settings.book.chapter) ||
|
|
204
205
|
settings.book.chapter < 1 ||
|
|
205
206
|
settings.book.chapter > bookChapterMax)
|
|
206
207
|
problems.push({
|
|
207
208
|
field: "voices.book.chapter",
|
|
208
|
-
message: `Enter a chapter number from 1 to ${String(bookChapterMax)} under Speakers (
|
|
209
|
+
message: `Enter a chapter number from 1 to ${String(bookChapterMax)} under Speakers (${createRows.narration}, or ${editSettings.narration}).`,
|
|
209
210
|
});
|
|
210
211
|
}
|
|
211
212
|
if (!turnGapSteps.includes(settings.turnGapSeconds))
|
|
212
213
|
problems.push({
|
|
213
214
|
field: "voices.turnGapSeconds",
|
|
214
|
-
message:
|
|
215
|
+
message: `Pick a gap between turns from the list under Speakers (${createRows.narration}, or ${editSettings.narration}).`,
|
|
215
216
|
});
|
|
216
217
|
return problems;
|
|
217
218
|
}
|
|
@@ -14,9 +14,6 @@ export function speakerColour(index) {
|
|
|
14
14
|
}
|
|
15
15
|
// ASS writes colours blue first: &HAABBGGRR in a style (alpha 00 is opaque) and &HBBGGRR& in
|
|
16
16
|
// an inline override.
|
|
17
|
-
export function assColour(hex, alpha = 0) {
|
|
18
|
-
return `&H${alpha.toString(16).padStart(2, "0")}${bgr(hex)}`.toUpperCase();
|
|
19
|
-
}
|
|
20
17
|
export function assInlineColour(hex) {
|
|
21
18
|
return `&H${bgr(hex)}&`.toUpperCase();
|
|
22
19
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createRows, editSettings } from "../../kernel/ports/settings-places.js";
|
|
1
2
|
import { voiceFormatLabels } from "./model.js";
|
|
2
3
|
const heading = /^#{1,6}\s+(.+?)\s*#*\s*$/;
|
|
3
4
|
const turnLine = /^\*{0,2}([^:*\n]{1,60}?)\*{0,2}\s*:\s*\*{0,2}\s*(.*)$/;
|
|
@@ -42,7 +43,7 @@ export function parseScript(markdown, speakers, keepMarkdown = false) {
|
|
|
42
43
|
ok: false,
|
|
43
44
|
reason: turn === null
|
|
44
45
|
? `Line ${String(number)} of the script is not a turn. Start every turn with a speaker's name and a colon (${names}), one turn per paragraph.`
|
|
45
|
-
: `Line ${String(number)} of the script is spoken by "${(turn[1] ?? "").trim()}", who is not one of the speakers (${names}). Use only these names, or add the speaker under Speakers (
|
|
46
|
+
: `Line ${String(number)} of the script is spoken by "${(turn[1] ?? "").trim()}", who is not one of the speakers (${names}). Use only these names, or add the speaker under Speakers (${createRows.narration}, or ${editSettings.narration}).`,
|
|
46
47
|
};
|
|
47
48
|
}
|
|
48
49
|
const spoken = [];
|