@gentbajko/slopify 3.25.8 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/account-limits.js +30 -0
- package/dist/adapters/alignment/cache.js +3 -5
- package/dist/adapters/alignment/ctc.js +1 -2
- package/dist/adapters/alignment/lock.js +4 -3
- package/dist/adapters/alignment/multilingual.js +0 -3
- package/dist/adapters/alignment/prefetch.js +2 -1
- package/dist/adapters/alignment/worker.js +2 -1
- package/dist/adapters/explain.js +0 -3
- package/dist/adapters/ffmpeg.js +19 -1
- package/dist/adapters/host-cli/index.js +19 -3
- package/dist/adapters/http-failure.js +53 -0
- package/dist/adapters/image/codex.js +36 -4
- package/dist/adapters/image/fal.js +17 -50
- package/dist/adapters/image/google.js +31 -55
- package/dist/adapters/image/openai.js +20 -47
- package/dist/adapters/image/reference.js +36 -0
- package/dist/adapters/image/replicate.js +18 -47
- package/dist/adapters/interest/search-interest.js +113 -0
- package/dist/adapters/key-probes.js +53 -2
- package/dist/adapters/llm/antigravity.js +4 -0
- 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 +51 -98
- package/dist/adapters/llm/claude-code.js +13 -2
- package/dist/adapters/llm/codex-limits.js +27 -68
- package/dist/adapters/llm/codex-server-models.js +61 -109
- package/dist/adapters/llm/codex.js +12 -1
- package/dist/adapters/llm/openrouter.js +10 -47
- package/dist/adapters/llm/run-cli.js +6 -22
- 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 +19 -40
- package/dist/adapters/tts/inworld-async.js +4 -1
- package/dist/adapters/tts/inworld.js +3 -0
- package/dist/adapters/tts/openai.js +20 -50
- 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 +3 -1
- package/dist/catalog/schema.js +2 -0
- package/dist/catalog/store.js +39 -0
- 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 +4 -25
- package/dist/edge/http/actor.js +5 -3
- package/dist/edge/http/app.js +85 -10
- package/dist/edge/http/assistant.js +127 -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 +1 -3
- package/dist/edge/http/channel-memory.js +7 -20
- package/dist/edge/http/channels.js +13 -26
- package/dist/edge/http/checkpoints.js +33 -17
- 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 +2 -11
- package/dist/edge/http/draft-files.js +1 -3
- package/dist/edge/http/draft-problem.js +4 -9
- 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 +38 -13
- 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 +1 -14
- package/dist/edge/http/open-folder.js +5 -10
- package/dist/edge/http/outputs.js +4 -12
- package/dist/edge/http/patch-notes.js +4 -10
- package/dist/edge/http/planning.js +2 -6
- package/dist/edge/http/problem.js +2 -4
- package/dist/edge/http/project-create.js +1 -0
- package/dist/edge/http/project-templates.js +29 -17
- package/dist/edge/http/projects.js +2 -12
- package/dist/edge/http/prompts.js +7 -14
- package/dist/edge/http/pronunciations.js +1 -2
- package/dist/edge/http/providers.js +39 -24
- 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 +11 -24
- package/dist/edge/http/revision-files.js +5 -10
- package/dist/edge/http/revisions.js +10 -15
- package/dist/edge/http/run-cost.js +1 -2
- package/dist/edge/http/same-page.js +80 -0
- package/dist/edge/http/schedules.js +59 -32
- package/dist/edge/http/settings.js +22 -13
- package/dist/edge/http/social.js +282 -0
- package/dist/edge/http/staging.js +2 -4
- package/dist/edge/http/storage-files.js +5 -7
- package/dist/edge/http/storage.js +23 -23
- package/dist/edge/http/studio.js +23 -50
- package/dist/edge/http/style-preview.js +1 -4
- package/dist/edge/http/subtitles.js +3 -17
- package/dist/edge/http/template-studio.js +433 -0
- package/dist/edge/http/trash.js +10 -11
- 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 +2 -3
- 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 +25 -0
- package/dist/edge/mcp/http.js +11 -2
- package/dist/edge/mcp/instructions.js +5 -3
- package/dist/edge/mcp/library-tools.js +1 -0
- package/dist/edge/mcp/route-tool.js +35 -15
- package/dist/edge/mcp/server.js +13 -8
- package/dist/edge/mcp/tools-channels.js +2 -0
- package/dist/edge/mcp/tools-designs.js +165 -0
- package/dist/edge/mcp/tools-library.js +14 -2
- package/dist/edge/mcp/tools-planning.js +23 -4
- package/dist/edge/mcp/tools-projects.js +28 -1
- package/dist/edge/mcp/tools-runs.js +14 -1
- package/dist/edge/mcp/tools-schedules.js +28 -4
- package/dist/edge/mcp/tools-setup.js +61 -3
- package/dist/edge/mcp/tools-social.js +46 -0
- package/dist/edge/mcp/tools-storage.js +8 -0
- 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/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/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/runner/attempt.js +3 -1
- 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 +15 -10
- package/dist/kernel/runner/standalone.js +9 -5
- package/dist/main.js +334 -19
- package/dist/model-checks.js +166 -0
- package/dist/patch-notes/3.25.9.md +5 -0
- package/dist/patch-notes/4.0.0.md +51 -0
- package/dist/patch-notes/index.json +12 -0
- package/dist/slices/admission/heads.js +2 -1
- package/dist/slices/admission/repo.js +2 -1
- package/dist/slices/admission/rules.js +43 -8
- 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/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 +79 -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 +342 -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 +4 -3
- package/dist/slices/channels/cast-images.js +2 -1
- 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 +6 -0
- package/dist/slices/channels/service.js +10 -0
- package/dist/slices/channels/videos.js +2 -14
- package/dist/slices/checkpoints/index.js +0 -1
- package/dist/slices/control/providers.js +0 -26
- 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 +128 -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 +222 -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/episodes/summarize.js +5 -1
- package/dist/slices/estimate/index.js +45 -7
- package/dist/slices/fonts/catalog.js +3 -2
- 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 +6 -15
- package/dist/slices/images/scenes.js +100 -27
- package/dist/slices/keywords/digest.js +84 -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 +2 -1
- package/dist/slices/library/used-by.js +25 -10
- package/dist/slices/loudness/loudnorm.js +51 -11
- 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 +112 -0
- package/dist/slices/model-checks/run.js +143 -0
- package/dist/slices/model-checks/service.js +198 -0
- package/dist/slices/model-upkeep/switch.js +70 -25
- package/dist/slices/model-upkeep/usage.js +26 -11
- package/dist/slices/narration/breaths.js +2 -1
- package/dist/slices/narration/clean-pieces.js +2 -1
- package/dist/slices/narration/concat.js +2 -1
- package/dist/slices/narration/peaks.js +3 -4
- package/dist/slices/narration/pronunciation-chunks.js +4 -3
- package/dist/slices/narration/pronunciation.js +3 -2
- 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 +2 -1
- package/dist/slices/onboarding/copy.js +2 -1
- package/dist/slices/onboarding/first-run.js +3 -2
- package/dist/slices/onboarding/full-video.js +3 -2
- package/dist/slices/onboarding/install.js +4 -3
- package/dist/slices/onboarding/model.js +0 -16
- package/dist/slices/onboarding/packs.js +22 -22
- package/dist/slices/onboarding/quick-short.js +2 -1
- package/dist/slices/onboarding/sample.js +0 -1
- package/dist/slices/onboarding/state.js +2 -11
- package/dist/slices/onboarding/template.js +2 -1
- package/dist/slices/patch-notes/library.js +3 -2
- package/dist/slices/play-drafts/convert.js +4 -0
- package/dist/slices/play-drafts/review-inputs.js +8 -2
- package/dist/slices/play-drafts/review.js +5 -4
- package/dist/slices/play-drafts/schema.js +4 -0
- package/dist/slices/project-templates/from-project.js +9 -2
- package/dist/slices/project-templates/repo.js +6 -1
- package/dist/slices/project-templates/setup.js +8 -0
- 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 +27 -9
- 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 +3 -3
- 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 +6 -3
- 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 +98 -28
- package/dist/slices/rebuild/recipe-visual.js +55 -16
- package/dist/slices/rebuild/recipe-work.js +15 -3
- package/dist/slices/rebuild/recipe-youtube.js +4 -3
- package/dist/slices/rebuild/recovery-selection.js +3 -1
- package/dist/slices/rebuild/recovery.js +3 -2
- package/dist/slices/rebuild/repo.js +4 -3
- package/dist/slices/rebuild/review-promise.js +25 -0
- package/dist/slices/rebuild/review-redo.js +2 -1
- 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 +9 -8
- package/dist/slices/rebuild/runtime-article.js +3 -2
- package/dist/slices/rebuild/runtime-checkpoints.js +5 -4
- package/dist/slices/rebuild/runtime-document.js +8 -11
- package/dist/slices/rebuild/runtime-export-bed.js +6 -11
- package/dist/slices/rebuild/runtime-export-edit.js +6 -15
- package/dist/slices/rebuild/runtime-export-inputs.js +7 -7
- package/dist/slices/rebuild/runtime-export-short.js +8 -4
- package/dist/slices/rebuild/runtime-export.js +34 -12
- package/dist/slices/rebuild/runtime-figure-card.js +2 -1
- package/dist/slices/rebuild/runtime-image.js +4 -9
- 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 +14 -20
- 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 +10 -15
- 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 +60 -19
- package/dist/slices/rebuild/runtime-publication.js +10 -9
- package/dist/slices/rebuild/runtime-review.js +33 -28
- package/dist/slices/rebuild/runtime-run.js +4 -1
- package/dist/slices/rebuild/runtime-shorts.js +34 -50
- package/dist/slices/rebuild/runtime-store.js +3 -8
- package/dist/slices/rebuild/runtime-subtitles.js +16 -14
- package/dist/slices/rebuild/runtime-voices.js +8 -15
- package/dist/slices/rebuild/runtime-youtube.js +11 -29
- 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/reviews/external-model.js +3 -0
- package/dist/slices/reviews/external-repo.js +2 -1
- package/dist/slices/reviews/external-rows.js +2 -3
- package/dist/slices/reviews/model.js +26 -8
- package/dist/slices/reviews/repo.js +2 -1
- package/dist/slices/reviews/rules.js +1 -0
- package/dist/slices/reviews/view.js +5 -2
- package/dist/slices/revisions/downloads.js +4 -5
- package/dist/slices/revisions/mutation-assets.js +7 -4
- package/dist/slices/revisions/mutation-cues.js +2 -5
- package/dist/slices/revisions/mutations.js +0 -1
- 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 +11 -2
- 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 +24 -16
- package/dist/slices/schedules/rescore.js +35 -0
- package/dist/slices/schedules/scheduler.js +13 -3
- package/dist/slices/schedules/schema.js +54 -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 +48 -36
- package/dist/slices/settings/cli-paths.js +4 -6
- package/dist/slices/settings/first-run.js +2 -11
- package/dist/slices/settings/model-reach.js +3 -17
- package/dist/slices/settings/model.js +10 -0
- package/dist/slices/settings/playback.js +33 -22
- 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/pick.js +2 -15
- package/dist/slices/shorts/prompts.js +2 -13
- package/dist/slices/shorts/render.js +3 -0
- 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 +131 -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 +55 -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 +128 -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 +20 -2
- 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 +16 -11
- 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 +10 -3
- package/dist/slices/studio/calendar.js +4 -1
- package/dist/slices/studio/pick.js +2 -11
- package/dist/slices/studio/plan.js +6 -15
- package/dist/slices/studio/queue.js +3 -14
- package/dist/slices/studio/releases.js +26 -0
- package/dist/slices/studio/settings.js +14 -51
- package/dist/slices/studio/videos.js +12 -0
- package/dist/slices/style-preview/render.js +34 -13
- package/dist/slices/style-preview/schema.js +13 -0
- package/dist/slices/style-preview/service.js +2 -1
- package/dist/slices/style-preview/settings.js +33 -5
- 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 +118 -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 +103 -0
- package/dist/slices/template-studio/checks.js +273 -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 +54 -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 +205 -0
- package/dist/slices/template-studio/paid-view.js +66 -0
- package/dist/slices/template-studio/paid.js +348 -0
- package/dist/slices/template-studio/quote.js +148 -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 +30 -0
- package/dist/slices/template-studio/schedule-sample.js +87 -0
- package/dist/slices/template-studio/schema.js +218 -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 +173 -0
- package/dist/slices/template-studio/status.js +174 -0
- package/dist/slices/template-studio/topic.js +223 -0
- 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/video/ass-text.js +17 -0
- package/dist/slices/video/audio-export.js +2 -64
- package/dist/slices/video/encoding.js +160 -0
- package/dist/slices/video/ffmpeg.js +23 -21
- package/dist/slices/video/figure-card.js +1 -15
- package/dist/slices/video/slideshow.js +43 -18
- package/dist/slices/voices/palette.js +0 -3
- package/dist/slices/youtube/answer.js +2 -15
- package/dist/slices/youtube/edits-repo.js +2 -11
- package/dist/tutorials/Agents-MCP.md +2 -1
- package/dist/tutorials/Assistant.md +42 -0
- package/dist/tutorials/Channels.md +2 -0
- package/dist/tutorials/Home-and-Projects.md +19 -0
- package/dist/tutorials/Install.md +1 -0
- package/dist/tutorials/Keywords-Slopify-Fills.md +87 -0
- package/dist/tutorials/Models.md +33 -1
- package/dist/tutorials/Notifications.md +6 -0
- package/dist/tutorials/Play-Images.md +4 -0
- package/dist/tutorials/Posting-to-Social-Sites.md +121 -0
- package/dist/tutorials/Prompts.md +10 -6
- package/dist/tutorials/Remote-Access.md +63 -0
- package/dist/tutorials/Reviews-and-Checkpoints.md +13 -2
- package/dist/tutorials/Schedules.md +21 -1
- package/dist/tutorials/Settings-Reference.md +50 -8
- package/dist/tutorials/Template-Studio.md +125 -0
- package/dist/tutorials/Templates.md +3 -0
- package/dist/tutorials/_Sidebar.md +5 -0
- package/dist/updater/work-in-progress.js +42 -12
- package/dist/web/assets/index-CkAI_2E7.css +1 -0
- package/dist/web/assets/index-DcD0cdfo.js +334 -0
- package/dist/web/assets/{pdf-T7MCwjIv.js → pdf-TODRwF31.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +5 -1
- 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/web/assets/index-BadsoORk.css +0 -1
- package/dist/web/assets/index-D8LqNsCl.js +0 -326
|
@@ -0,0 +1,160 @@
|
|
|
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
|
+
export const noNvenc = "No NVIDIA encoder found on this computer.";
|
|
59
|
+
// The renders' own settings on a tiny picture: a driver or card that refuses any of them fails
|
|
60
|
+
// here rather than in a render. NVENC refuses anything under about 145 pixels across.
|
|
61
|
+
const testArgs = [
|
|
62
|
+
"-hide_banner",
|
|
63
|
+
"-nostdin",
|
|
64
|
+
"-loglevel",
|
|
65
|
+
"error",
|
|
66
|
+
"-f",
|
|
67
|
+
"lavfi",
|
|
68
|
+
"-i",
|
|
69
|
+
"color=c=black:s=256x256:r=30:d=0.1",
|
|
70
|
+
"-frames:v",
|
|
71
|
+
"1",
|
|
72
|
+
...codecArgs({ kind: "nvenc", bin: "", clipSpeed: "thorough" }, "final", undefined),
|
|
73
|
+
"-f",
|
|
74
|
+
"null",
|
|
75
|
+
"-",
|
|
76
|
+
];
|
|
77
|
+
// The filters a render runs in the NVENC ffmpeg besides the encoder.
|
|
78
|
+
const neededFilters = ["zoompan", "xfade", "ass"];
|
|
79
|
+
// Which ffmpeg can encode on an NVIDIA card, asked once and remembered until Slopify restarts.
|
|
80
|
+
// The bundled ffmpeg first, then the computer's own: unlike everything else, NVENC is the
|
|
81
|
+
// computer's (its driver and its ffmpeg), and only used when chosen in Settings.
|
|
82
|
+
export function createNvencProbe(deps) {
|
|
83
|
+
let answer;
|
|
84
|
+
return () => {
|
|
85
|
+
answer ??= probe(deps).catch((error) => ({
|
|
86
|
+
available: false,
|
|
87
|
+
reason: `${noNvenc} (${errorText(error)})`,
|
|
88
|
+
}));
|
|
89
|
+
return answer;
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
async function probe(deps) {
|
|
93
|
+
let failed;
|
|
94
|
+
for (const bin of new Set(deps.candidates)) {
|
|
95
|
+
const encoders = await deps.exec(bin, ["-hide_banner", "-encoders"]).catch(() => "");
|
|
96
|
+
if (!/^\s*V\S*\s+h264_nvenc\s/m.test(encoders))
|
|
97
|
+
continue;
|
|
98
|
+
const filters = await deps.exec(bin, ["-hide_banner", "-filters"]).catch(() => "");
|
|
99
|
+
const missing = neededFilters.filter((name) => !new RegExp(`^\\s*\\S+\\s+${name}\\s`, "m").test(filters));
|
|
100
|
+
if (missing.length > 0) {
|
|
101
|
+
failed = `the ffmpeg at ${bin} has NVENC but not the ${missing.join(", ")} filter a video needs`;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
await deps.exec(bin, testArgs);
|
|
106
|
+
deps.log.write("info", "video.encoder", { detail: `NVENC works through ${bin}` });
|
|
107
|
+
return { available: true, bin };
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
failed = `the ffmpeg at ${bin} lists NVENC but its test encode failed: ${errorText(error)}`;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (failed !== undefined) {
|
|
114
|
+
deps.log.write("info", "video.encoder", { detail: failed });
|
|
115
|
+
return { available: false, reason: `${noNvenc} (${failed})` };
|
|
116
|
+
}
|
|
117
|
+
return { available: false, reason: noNvenc };
|
|
118
|
+
}
|
|
119
|
+
// The render's machine settings, read as it starts. A card chosen but not working is a notice,
|
|
120
|
+
// never a failed render: the processor encodes it instead.
|
|
121
|
+
export async function renderMachine(deps) {
|
|
122
|
+
const { settings } = deps;
|
|
123
|
+
const cpu = { kind: "x264", clipSpeed: settings.clipEncodeSpeed };
|
|
124
|
+
if (settings.videoEncoder !== "nvidia")
|
|
125
|
+
return { machine: { encode: cpu, priority: settings.renderPriority } };
|
|
126
|
+
const status = await deps.nvenc();
|
|
127
|
+
if (status.available)
|
|
128
|
+
return {
|
|
129
|
+
machine: {
|
|
130
|
+
encode: { kind: "nvenc", bin: status.bin, clipSpeed: settings.clipEncodeSpeed },
|
|
131
|
+
priority: settings.renderPriority,
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
deps.log.write("warn", "video.encoder", {
|
|
135
|
+
projectId: deps.projectId,
|
|
136
|
+
detail: `NVIDIA encoder chosen but unavailable; encoding on the processor. ${status.reason}`,
|
|
137
|
+
});
|
|
138
|
+
return {
|
|
139
|
+
machine: { encode: cpu, priority: settings.renderPriority },
|
|
140
|
+
notice: nvencFallbackNotice(status.reason),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
export function nvencFallbackNotice(reason) {
|
|
144
|
+
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.`;
|
|
145
|
+
}
|
|
146
|
+
// Background renders run below normal priority: nice 10 on Linux and macOS, the below-normal
|
|
147
|
+
// class on Windows. Threads ffmpeg starts afterwards inherit it. Advisory: a system that
|
|
148
|
+
// refuses is logged and the render carries on.
|
|
149
|
+
export function setRenderPriority(pid, priority, log, set = setPriority) {
|
|
150
|
+
if (priority !== "background")
|
|
151
|
+
return;
|
|
152
|
+
try {
|
|
153
|
+
set(pid, constants.priority.PRIORITY_BELOW_NORMAL);
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
log.write("warn", "video.priority", {
|
|
157
|
+
detail: `could not lower the render's priority: ${errorText(error)}`,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
@@ -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", "Use More → Render the video again in the 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", "Use More → Render the video again in the 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,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", "Use More → Render the video again in the 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
|
-
}
|
|
@@ -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,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { jsonBetween } from "../../kernel/json-answer.js";
|
|
2
3
|
import { alternativeTitles, descriptionMaxCharacters, hashtagsMax, minChapterSeconds, minChapters, pinnedCommentMaxCharacters, tagMaxCharacters, tagsMaxCharacters, titleMaxCharacters, } from "./model.js";
|
|
3
4
|
import { parseTimestamp, youtubeTimestamp } from "./timestamps.js";
|
|
4
5
|
import { keepsShape, titleShapeRule } from "./titles.js";
|
|
@@ -60,7 +61,7 @@ export function checkDescriptionAnswer(text, durationSeconds,
|
|
|
60
61
|
title = "",
|
|
61
62
|
// Its pattern, whose fixed wording the other titles keep.
|
|
62
63
|
shape) {
|
|
63
|
-
const parsed = answerSchema.safeParse(
|
|
64
|
+
const parsed = answerSchema.safeParse(jsonBetween(text, "{", "}"));
|
|
64
65
|
if (!parsed.success)
|
|
65
66
|
return {
|
|
66
67
|
ok: false,
|
|
@@ -239,17 +240,3 @@ function checkTags(raw) {
|
|
|
239
240
|
return broke(`the tags come to ${String(length)} characters, over YouTube's ${String(tagsMaxCharacters)}`);
|
|
240
241
|
return { ok: true, value: tags };
|
|
241
242
|
}
|
|
242
|
-
// The object in the answer, wherever the model put it: bare, in a ```json fence, or after a
|
|
243
|
-
// sentence of its own.
|
|
244
|
-
function jsonOf(text) {
|
|
245
|
-
const start = text.indexOf("{");
|
|
246
|
-
const end = text.lastIndexOf("}");
|
|
247
|
-
if (start === -1 || end <= start)
|
|
248
|
-
return undefined;
|
|
249
|
-
try {
|
|
250
|
-
return JSON.parse(text.slice(start, end + 1));
|
|
251
|
-
}
|
|
252
|
-
catch {
|
|
253
|
-
return undefined;
|
|
254
|
-
}
|
|
255
|
-
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { defaultChannelId } from "../channels/model.js";
|
|
6
6
|
import { channelById, projectChannelId } from "../channels/repo.js";
|
|
7
|
-
import {
|
|
7
|
+
import { readJsonSetting, writeSetting } from "../settings/repo.js";
|
|
8
8
|
import { chapterNotice, fitChapters } from "./chapters.js";
|
|
9
9
|
import { composeDescription, descriptionFields, resolveFields, shownFields, splitDescription, } from "./edits.js";
|
|
10
10
|
import { fillPlaceholders, mergeLinks } from "./placeholders.js";
|
|
@@ -36,18 +36,9 @@ export function writeDescriptionEdits(db, projectId, edits, updatedAt) {
|
|
|
36
36
|
ON CONFLICT(project_id) DO UPDATE SET fields_json=excluded.fields_json,links_json=excluded.links_json,updated_at=excluded.updated_at`).run(projectId, JSON.stringify(edits.fields), JSON.stringify(edits.links), updatedAt);
|
|
37
37
|
}
|
|
38
38
|
export function readChannelLinks(db) {
|
|
39
|
-
const value = readSetting(db, channelLinksKey);
|
|
40
|
-
if (value === undefined)
|
|
41
|
-
return [];
|
|
42
39
|
// A hand-edited row that no longer parses reads as no links rather than breaking the page;
|
|
43
40
|
// saving the list in Settings writes a good one again.
|
|
44
|
-
|
|
45
|
-
const parsed = linksSchema.safeParse(JSON.parse(value));
|
|
46
|
-
return parsed.success ? parsed.data : [];
|
|
47
|
-
}
|
|
48
|
-
catch {
|
|
49
|
-
return [];
|
|
50
|
-
}
|
|
41
|
+
return readJsonSetting(db, channelLinksKey, linksSchema, []);
|
|
51
42
|
}
|
|
52
43
|
export function writeChannelLinks(db, links) {
|
|
53
44
|
writeSetting(db, channelLinksKey, JSON.stringify(links.map((link) => ({ name: link.name.trim(), url: link.url.trim() }))));
|
|
@@ -144,7 +144,8 @@ Slopify cannot wake an agent; the agent looks. Its work list is the `needs_atten
|
|
|
144
144
|
| A paused run, or a run a restart **Stopped** | Continues it once nothing on the project still needs fixing. |
|
|
145
145
|
| Topics a schedule holds for approval | Approves the ones that fit and turns down the rest. |
|
|
146
146
|
| A video marked **Late · upload now** | Reports it. It cannot upload. |
|
|
147
|
-
| A finishing touch the Studio extension failed (captions, end screen, pinned comment) | Reports it. Fixing it is the extension's or yours. |
|
|
147
|
+
| A finishing touch the Studio extension failed (captions, end screen, pinned comment), or a post it couldn't fill on another site | Reports it. Fixing it is the extension's or yours. |
|
|
148
|
+
| A post filled in another site's form, waiting for your **Post** (`post-due`) | Reports it. Posting is yours. |
|
|
148
149
|
|
|
149
150
|
Projects that are **Kept as is** are not listed. Each entry says whether Slopify is already retrying or redoing it by itself (the agent leaves those alone and looks again later) and whether the agent can act on it at all.
|
|
150
151
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Assistant
|
|
2
|
+
|
|
3
|
+
The assistant is a chat inside Slopify. Ask it how something works, or ask it to do something on the screen you have open: "Which templates do I have?", "Why did this run stop?", "Remake the thumbnail of this project". It runs on your own **Claude Code** or **Codex** sign-in, so it uses your plan's allowance and is not added to a project's run cost.
|
|
4
|
+
|
|
5
|
+
**Where to find it:** **Assistant** in the left rail, under the search field. On a phone it is the sparkle button in the top bar. It opens beside the page without moving it.
|
|
6
|
+
|
|
7
|
+
## What it can and can't do
|
|
8
|
+
|
|
9
|
+
- It uses only Slopify's own tools, the same ones an [agent](Agents-MCP) uses, with the same checks and refusals as the buttons in the page. It reads this guide to answer questions about Slopify.
|
|
10
|
+
- It has no web search, can't run commands and can't open files on your computer. None of your Claude Code or Codex settings, hooks, plugins, skills, memory or other MCP servers are loaded for it. If the CLI ever offers it a tool from outside Slopify, Slopify stops the answer at once and says so.
|
|
11
|
+
- It never answers an external review, exports or imports the Library, uploads a file from disk, empties the Trash or deletes old versions. Uploading to YouTube, backups, provider keys and updates stay yours.
|
|
12
|
+
- What it changes shows as **by assistant** in a project's history, in notifications and in **Settings → Connections → Agents (MCP)**.
|
|
13
|
+
|
|
14
|
+
## It asks first
|
|
15
|
+
|
|
16
|
+
Anything that costs money (starting a run or a remake, retrying a step, generating topics, a cast picture or a voice sample), anything a button would confirm, anything that removes something, and anything that sends to or changes an address outside Slopify (where notifications go, a test notification) waits for you. The chat shows a card with what it wants to do, for which project, and what it costs: the estimate the remake preview or Play's review showed, or "Slopify can't price it here" when there is none.
|
|
17
|
+
|
|
18
|
+
| Press | What happens |
|
|
19
|
+
| --- | --- |
|
|
20
|
+
| **Allow** | The assistant does that one thing, with exactly what it asked for, and tells you what happened. An Allow is used once and only in that chat. |
|
|
21
|
+
| **Don't** | Nothing runs. The assistant sees your answer the next time you write. |
|
|
22
|
+
|
|
23
|
+
An ask you don't answer within 15 minutes expires: ask again, so it can check the cost again. The assistant can't press Allow for you; only a press in the page counts.
|
|
24
|
+
|
|
25
|
+
## Using the chat
|
|
26
|
+
|
|
27
|
+
- **Send** (or Enter) sends your message; Shift+Enter starts a new line. The answer streams in as it is written, with a line for each tool it used.
|
|
28
|
+
- **Stop** ends the answer at once. Whatever it already did stays done. An answer that takes longer than 10 minutes is stopped for you.
|
|
29
|
+
- **New chat** starts over. Each chat keeps its last 200 messages; the assistant remembers the last 20 of them (up to about 24,000 characters) when it answers.
|
|
30
|
+
- **Runs on** picks the CLI: **Auto** takes Claude Code when it is installed and signed in, otherwise Codex.
|
|
31
|
+
- It knows the page you have open: the project, schedule, channel or template on it, the section, and the rows you selected in a list.
|
|
32
|
+
|
|
33
|
+
## When it can't start
|
|
34
|
+
|
|
35
|
+
The panel says why and links to the fix:
|
|
36
|
+
|
|
37
|
+
| Message | Fix |
|
|
38
|
+
| --- | --- |
|
|
39
|
+
| Neither Claude Code nor Codex is ready | Install one and sign in, then check it in **Settings → Providers** ([AI CLIs](AI-CLIs)). |
|
|
40
|
+
| Claude Code (or Codex) is signed out | Run `claude auth login` (or `codex login`) in a terminal, then send your message again. |
|
|
41
|
+
| Codex is too old | Update Codex CLI to the version the message names. |
|
|
42
|
+
| The Docker version | The assistant isn't available in Docker: there the CLIs run on your computer through the host helper, which can't hand them Slopify's tools safely. Install Slopify with npm ([Install](Install)) to use it. |
|
|
@@ -47,6 +47,8 @@ A channel page has six tabs: **Brand**, **Cast**, **Templates**, **Schedules**,
|
|
|
47
47
|
|
|
48
48
|
Mark voices in **Settings → Voices** and Image prompts in **Library → Prompts**. See [Publishing to YouTube](Publishing-to-YouTube).
|
|
49
49
|
|
|
50
|
+
**Posting.** The channel's accounts on other sites, used only for [posting](Posting-to-Social-Sites). **X subscription** (Free, Premium or Premium+) only changes which warnings a post gets: Free warns about text over 280 characters and videos over 2 min 20 s. **Account names** (such as `@yourname` on X, `in/your-name` on LinkedIn, `yourname.bsky.social` on Bluesky, `u/yourname` on Reddit) let the extension find a published post's link on your profile. They are never a sign-in. Saved with **Save channel**.
|
|
51
|
+
|
|
50
52
|
**Series brief.** What the channel covers, its style and what makes a topic worth a video. Topic generation reads it for every schedule of this channel that has no brief of its own. It doesn't change how videos are written. Up to 10,000 characters. See [Schedules](Schedules#topics-that-find-themselves).
|
|
51
53
|
|
|
52
54
|
**Language of new projects.** The language this channel's new projects are written and narrated in when Play or the template picks none. It applies even with the brand kit off, since it is not styling. Pick voices that speak it. Not set means English. Projects already made keep their language. See [Other languages](Other-Languages).
|