@gentbajko/slopify 3.25.9 → 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/4.0.0.md +51 -0
- package/dist/patch-notes/index.json +6 -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 +25 -45
- 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,176 @@
|
|
|
1
|
+
import { errorText } from "../../kernel/errors.js";
|
|
2
|
+
import { StylePreviewError } from "../style-preview/render.js";
|
|
3
|
+
import { normalizeStylePreview, stylePreviewHash } from "../style-preview/settings.js";
|
|
4
|
+
import { documentHash, documentThemeOfTemplate } from "./document-sample.js";
|
|
5
|
+
import { videoSampleRequest } from "./free-video.js";
|
|
6
|
+
import { loudnessGoalOf, loudnessHash } from "./loudness.js";
|
|
7
|
+
// The samples the studio makes on this computer, free: the 30-second video, the loudness before
|
|
8
|
+
// and after, and the PDF. Each is kept by the hash of what it is made from, so a setting changed
|
|
9
|
+
// back, or the same setup in another template, shows at once.
|
|
10
|
+
export const freeKinds = ["video", "loudness", "document"];
|
|
11
|
+
const press = "press Make samples";
|
|
12
|
+
export function createFreeSamples(deps) {
|
|
13
|
+
const failures = new Map();
|
|
14
|
+
const making = new Set();
|
|
15
|
+
const keyOf = (kind, hash) => `${kind}:${hash}`;
|
|
16
|
+
const videoHash = (document) => stylePreviewHash(normalizeStylePreview(videoSampleRequest(document)));
|
|
17
|
+
const loudnessHashOf = (document) => {
|
|
18
|
+
const goal = loudnessGoalOf(document, deps.loudnessDefault());
|
|
19
|
+
return goal === undefined ? undefined : loudnessHash(goal);
|
|
20
|
+
};
|
|
21
|
+
const documentHashOf = (document) => documentHash(documentThemeOfTemplate(document));
|
|
22
|
+
const attempt = async (kind, hash, work, failure) => {
|
|
23
|
+
const key = keyOf(kind, hash);
|
|
24
|
+
making.add(key);
|
|
25
|
+
failures.delete(key);
|
|
26
|
+
try {
|
|
27
|
+
await work();
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
failures.set(key, failure(error));
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
making.delete(key);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
const pending = (job, kind, hash, what) => {
|
|
37
|
+
const key = keyOf(kind, hash);
|
|
38
|
+
const failed = failures.get(key);
|
|
39
|
+
if (failed !== undefined)
|
|
40
|
+
return { state: "needs-fix", sentence: failed, url: null };
|
|
41
|
+
if (making.has(key) || job.running)
|
|
42
|
+
return { state: "making", sentence: `Making the ${what} on this computer.`, url: null };
|
|
43
|
+
if (job.queued)
|
|
44
|
+
return {
|
|
45
|
+
state: "waiting",
|
|
46
|
+
sentence: `The ${what} waits for another template's samples to finish.`,
|
|
47
|
+
url: null,
|
|
48
|
+
};
|
|
49
|
+
return { state: "stale", sentence: `No ${what} for this version yet: ${press}.`, url: null };
|
|
50
|
+
};
|
|
51
|
+
return {
|
|
52
|
+
loudnessHashOf,
|
|
53
|
+
documentHashOf,
|
|
54
|
+
make: async (document, signal) => {
|
|
55
|
+
const { form } = document;
|
|
56
|
+
if (form.sources.video !== "off" && deps.stylePreviews !== undefined) {
|
|
57
|
+
const previews = deps.stylePreviews;
|
|
58
|
+
await attempt("video", videoHash(document), async () => {
|
|
59
|
+
await previews.render(videoSampleRequest(document));
|
|
60
|
+
}, videoFailure);
|
|
61
|
+
}
|
|
62
|
+
if (signal.aborted)
|
|
63
|
+
return;
|
|
64
|
+
const goal = loudnessGoalOf(document, deps.loudnessDefault());
|
|
65
|
+
if (goal !== undefined && deps.loudness !== undefined) {
|
|
66
|
+
const samples = deps.loudness;
|
|
67
|
+
await attempt("loudness", loudnessHash(goal), () => samples.make(goal, signal), (error) => ffmpegFailure("loudness before and after", error));
|
|
68
|
+
}
|
|
69
|
+
if (signal.aborted)
|
|
70
|
+
return;
|
|
71
|
+
if (form.sources.document === "generate" && deps.documents !== undefined) {
|
|
72
|
+
const documents = deps.documents;
|
|
73
|
+
const theme = documentThemeOfTemplate(document);
|
|
74
|
+
await attempt("document", documentHash(theme), () => documents.make(theme), (error) => `The PDF sample couldn't be made: ${errorText(error)} Change the theme in Document (PDF) → Fine-tune, then ${press}.`);
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
view: (templateId, document, job) => {
|
|
78
|
+
const { form } = document;
|
|
79
|
+
const base = `/api/template-studio/${encodeURIComponent(templateId)}`;
|
|
80
|
+
const unavailable = (what) => ({
|
|
81
|
+
state: "needs-fix",
|
|
82
|
+
sentence: `The ${what} can't be made because this Slopify started without ffmpeg. Install ffmpeg, or use the Docker image, then restart Slopify and ${press}.`,
|
|
83
|
+
url: null,
|
|
84
|
+
});
|
|
85
|
+
let video;
|
|
86
|
+
if (form.sources.video === "off")
|
|
87
|
+
video = { state: "off", sentence: "The video is off.", url: null };
|
|
88
|
+
else if (deps.stylePreviews === undefined)
|
|
89
|
+
video = unavailable("video sample");
|
|
90
|
+
else {
|
|
91
|
+
const hash = videoHash(document);
|
|
92
|
+
const saved = deps.stylePreviews.file(hash);
|
|
93
|
+
video =
|
|
94
|
+
saved === undefined
|
|
95
|
+
? pending(job, "video", hash, "30-second video")
|
|
96
|
+
: {
|
|
97
|
+
state: "current",
|
|
98
|
+
sentence: "30 seconds in your frame, captions, Look and picture timing, on Slopify's sample pictures and narration.",
|
|
99
|
+
url: `/api/style-preview/${hash}.mp4?v=${String(saved.version)}`,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
const lHash = loudnessHashOf(document);
|
|
103
|
+
let loudness;
|
|
104
|
+
if (lHash === undefined)
|
|
105
|
+
loudness = {
|
|
106
|
+
state: "off",
|
|
107
|
+
sentence: "Level the volume is off: the narration plays as the voice gives it.",
|
|
108
|
+
url: null,
|
|
109
|
+
beforeUrl: null,
|
|
110
|
+
beforeLufs: null,
|
|
111
|
+
afterLufs: null,
|
|
112
|
+
targetLufs: null,
|
|
113
|
+
ceiling: null,
|
|
114
|
+
};
|
|
115
|
+
else if (deps.loudness === undefined)
|
|
116
|
+
loudness = {
|
|
117
|
+
...unavailable("loudness before and after"),
|
|
118
|
+
beforeUrl: null,
|
|
119
|
+
beforeLufs: null,
|
|
120
|
+
afterLufs: null,
|
|
121
|
+
targetLufs: null,
|
|
122
|
+
ceiling: null,
|
|
123
|
+
};
|
|
124
|
+
else {
|
|
125
|
+
const reading = deps.loudness.reading(lHash);
|
|
126
|
+
const shown = reading === undefined
|
|
127
|
+
? pending(job, "loudness", lHash, "loudness before and after")
|
|
128
|
+
: undefined;
|
|
129
|
+
loudness = {
|
|
130
|
+
...(shown ?? {
|
|
131
|
+
state: "current",
|
|
132
|
+
sentence: "Slopify's sample narration as it came, and levelled the way your videos will be. Your own voice's sample replaces it once paid samples are made.",
|
|
133
|
+
url: `${base}/loudness/after.mp3?v=${lHash.slice(0, 12)}`,
|
|
134
|
+
}),
|
|
135
|
+
beforeUrl: `${base}/loudness/before.mp3`,
|
|
136
|
+
beforeLufs: reading?.before ?? null,
|
|
137
|
+
afterLufs: reading?.after ?? null,
|
|
138
|
+
targetLufs: reading?.target ?? null,
|
|
139
|
+
ceiling: reading?.ceiling ?? null,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
let pdf;
|
|
143
|
+
if (form.sources.document !== "generate")
|
|
144
|
+
pdf = { state: "off", sentence: "The PDF document is off.", url: null };
|
|
145
|
+
else if (deps.documents === undefined)
|
|
146
|
+
pdf = unavailable("PDF sample");
|
|
147
|
+
else {
|
|
148
|
+
const hash = documentHashOf(document);
|
|
149
|
+
pdf =
|
|
150
|
+
deps.documents.file(hash) === undefined
|
|
151
|
+
? pending(job, "document", hash, "PDF sample")
|
|
152
|
+
: {
|
|
153
|
+
state: "current",
|
|
154
|
+
sentence: "Slopify's sample article laid out in your theme.",
|
|
155
|
+
url: `${base}/document.pdf?v=${hash.slice(0, 12)}`,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
return { video, loudness, document: pdf };
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
function videoFailure(error) {
|
|
163
|
+
if (error instanceof StylePreviewError) {
|
|
164
|
+
if (error.reason === "font-missing")
|
|
165
|
+
return `The video sample couldn't be made because the caption font is no longer on this computer. Choose another font in Video & captions → Fine-tune, then ${press}.`;
|
|
166
|
+
if (error.reason === "ffmpeg-missing")
|
|
167
|
+
return `The video sample couldn't be made because ffmpeg wasn't found. Install ffmpeg, or use the Docker image, then ${press}.`;
|
|
168
|
+
}
|
|
169
|
+
return ffmpegFailure("video sample", error);
|
|
170
|
+
}
|
|
171
|
+
function ffmpegFailure(what, error) {
|
|
172
|
+
const message = errorText(error);
|
|
173
|
+
if (/\bENOENT\b/.test(message) || message.startsWith("Slopify could not start ffmpeg"))
|
|
174
|
+
return `The ${what} couldn't be made because ffmpeg wasn't found. Install ffmpeg, or use the Docker image, then ${press}.`;
|
|
175
|
+
return `The ${what} couldn't be made because ffmpeg stopped with an error. ${press[0]?.toUpperCase()}${press.slice(1)} again; if it fails again, use Download diagnostics in Settings and report it.`;
|
|
176
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
function runnable(byVersion, version, seen = new Set()) {
|
|
2
|
+
const gate = byVersion.get(version);
|
|
3
|
+
if (gate === undefined || seen.has(version))
|
|
4
|
+
return false;
|
|
5
|
+
if (gate.state !== "carried")
|
|
6
|
+
return true;
|
|
7
|
+
const from = gate.carriedFrom;
|
|
8
|
+
return from !== null && from < version && runnable(byVersion, from, new Set([...seen, version]));
|
|
9
|
+
}
|
|
10
|
+
// The newest version at or below the head that was legacy, marked ready, or carried from a
|
|
11
|
+
// runnable one. Undefined when none was ever checked. What a pack carries.
|
|
12
|
+
export function checkedVersion(gates, head) {
|
|
13
|
+
const byVersion = new Map(gates.map((gate) => [gate.version, gate]));
|
|
14
|
+
return [...byVersion.keys()]
|
|
15
|
+
.filter((version) => version <= head && runnable(byVersion, version))
|
|
16
|
+
.sort((a, b) => b - a)[0];
|
|
17
|
+
}
|
|
18
|
+
// A template is gated from the first time any of its versions is marked ready in the studio.
|
|
19
|
+
// Before that it follows its newest version as 3.x did, so an edit applies to the next run.
|
|
20
|
+
export function isGated(gates) {
|
|
21
|
+
return gates.some((gate) => gate.state === "ready");
|
|
22
|
+
}
|
|
23
|
+
// The version a schedule runs: the newest one while the template isn't gated, else the newest
|
|
24
|
+
// checked one (a newer unchecked version waits for Mark ready).
|
|
25
|
+
export function runnableVersion(gates, head) {
|
|
26
|
+
return isGated(gates) ? checkedVersion(gates, head) : head;
|
|
27
|
+
}
|
|
28
|
+
export function gateOf(gates, version) {
|
|
29
|
+
return gates.find((gate) => gate.version === version)?.state ?? "unchecked";
|
|
30
|
+
}
|
|
31
|
+
// A version Slopify itself wrote (a model switch, a restore under a new name) keeps the gate of
|
|
32
|
+
// the version it came from, so a schedule is never stopped by a change its user did not make.
|
|
33
|
+
export function carryGate(gates, parent, next) {
|
|
34
|
+
if (next <= parent)
|
|
35
|
+
return undefined;
|
|
36
|
+
const byVersion = new Map(gates.map((gate) => [gate.version, gate]));
|
|
37
|
+
return runnable(byVersion, parent)
|
|
38
|
+
? { version: next, state: "carried", carriedFrom: parent }
|
|
39
|
+
: undefined;
|
|
40
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { transact } from "../../kernel/db/tx.js";
|
|
2
|
+
import { deleteProject } from "../storage/delete-project.js";
|
|
3
|
+
// Removes one of the studio's hidden projects (a sample made again on its own project, a
|
|
4
|
+
// bake-off copy) for good. What it was charged today stays counted against the sample budget:
|
|
5
|
+
// its priced calls are kept in `template_studio_spend` first, so removing a project never
|
|
6
|
+
// frees budget it already spent.
|
|
7
|
+
export function removeHiddenProject(deps, projectId) {
|
|
8
|
+
return transact(deps.db, () => {
|
|
9
|
+
deps.db
|
|
10
|
+
.prepare(`INSERT INTO template_studio_spend (project_id,cost,created_at)
|
|
11
|
+
SELECT project_id,cost,created_at FROM provider_usage
|
|
12
|
+
WHERE project_id=? AND on_plan=0 AND cost > 0`)
|
|
13
|
+
.run(projectId);
|
|
14
|
+
const removed = deleteProject(deps, projectId);
|
|
15
|
+
// A project that stays (running, a file the OS keeps) keeps its own rows: not counted twice.
|
|
16
|
+
if (!removed.ok)
|
|
17
|
+
deps.db.prepare("DELETE FROM template_studio_spend WHERE project_id=?").run(projectId);
|
|
18
|
+
return removed;
|
|
19
|
+
});
|
|
20
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { transact } from "../../kernel/db/tx.js";
|
|
3
|
+
import { supersedeQueuedBakeoff } from "./bakeoff-repo.js";
|
|
4
|
+
import { sampleJobKinds } from "./model.js";
|
|
5
|
+
const jobRow = z.object({
|
|
6
|
+
templateId: z.string(),
|
|
7
|
+
templateVersion: z.number().int().positive(),
|
|
8
|
+
ackHigh: z.number(),
|
|
9
|
+
kind: z.enum(sampleJobKinds),
|
|
10
|
+
requestedAt: z.string(),
|
|
11
|
+
});
|
|
12
|
+
const columns = "template_id AS templateId,template_version AS templateVersion,ack_high AS ackHigh,kind,requested_at AS requestedAt";
|
|
13
|
+
// Queues a template's sample job. A newer request takes the place of the one still queued; the
|
|
14
|
+
// running one is left to finish and this one waits behind it. A free-only request never takes
|
|
15
|
+
// the place of a queued paid one for the same version, which already makes the free samples,
|
|
16
|
+
// nor of a queued bake-off.
|
|
17
|
+
export function enqueueJob(db, job) {
|
|
18
|
+
transact(db, () => {
|
|
19
|
+
const queued = queuedJob(db, job.templateId);
|
|
20
|
+
if (queued !== undefined &&
|
|
21
|
+
job.kind === "free" &&
|
|
22
|
+
queued.kind !== "free" &&
|
|
23
|
+
(queued.templateVersion === job.templateVersion || queued.kind === "bakeoff"))
|
|
24
|
+
return;
|
|
25
|
+
// A bake-off still waiting was priced on the version a paid change replaces: it is
|
|
26
|
+
// dropped, saying so, and nothing was spent on it.
|
|
27
|
+
if (queued?.kind === "bakeoff" && job.kind !== "bakeoff")
|
|
28
|
+
supersedeQueuedBakeoff(db, job.templateId, job.requestedAt);
|
|
29
|
+
db.prepare(`INSERT INTO template_sample_jobs (template_id,template_version,ack_high,kind,requested_at,state)
|
|
30
|
+
VALUES (?,?,?,?,?,'queued')
|
|
31
|
+
ON CONFLICT(template_id,state) DO UPDATE SET template_version=excluded.template_version,
|
|
32
|
+
ack_high=excluded.ack_high,kind=excluded.kind,requested_at=excluded.requested_at`).run(job.templateId, job.templateVersion, job.ackHigh, job.kind, job.requestedAt);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
export function queuedJob(db, templateId) {
|
|
36
|
+
const row = db
|
|
37
|
+
.prepare(`SELECT ${columns} FROM template_sample_jobs WHERE template_id=? AND state='queued'`)
|
|
38
|
+
.get(templateId);
|
|
39
|
+
return row === undefined ? undefined : jobRow.parse(row);
|
|
40
|
+
}
|
|
41
|
+
export function runningJob(db) {
|
|
42
|
+
const row = db
|
|
43
|
+
.prepare(`SELECT ${columns} FROM template_sample_jobs WHERE state='running' LIMIT 1`)
|
|
44
|
+
.get();
|
|
45
|
+
return row === undefined ? undefined : jobRow.parse(row);
|
|
46
|
+
}
|
|
47
|
+
// The next job to make: the oldest queued one, only while no other is running anywhere, so one
|
|
48
|
+
// sample is made at a time across every template.
|
|
49
|
+
export function claimNextJob(db) {
|
|
50
|
+
return transact(db, () => {
|
|
51
|
+
if (runningJob(db) !== undefined)
|
|
52
|
+
return undefined;
|
|
53
|
+
const row = db
|
|
54
|
+
.prepare(`SELECT ${columns} FROM template_sample_jobs WHERE state='queued' ORDER BY requested_at,template_id LIMIT 1`)
|
|
55
|
+
.get();
|
|
56
|
+
if (row === undefined)
|
|
57
|
+
return undefined;
|
|
58
|
+
const job = jobRow.parse(row);
|
|
59
|
+
db.prepare("UPDATE template_sample_jobs SET state='running' WHERE template_id=? AND state='queued'").run(job.templateId);
|
|
60
|
+
return job;
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
// The running job of a template now continues the project its paid work started on: queued
|
|
64
|
+
// again by a restart, it is a Save of the same version, never a new topic's fresh project.
|
|
65
|
+
export function continueJobAsSave(db, templateId) {
|
|
66
|
+
db.prepare("UPDATE template_sample_jobs SET kind='save' WHERE template_id=? AND state='running' AND kind IN ('topic','full-script')").run(templateId);
|
|
67
|
+
}
|
|
68
|
+
export function finishJob(db, templateId) {
|
|
69
|
+
db.prepare("DELETE FROM template_sample_jobs WHERE template_id=? AND state='running'").run(templateId);
|
|
70
|
+
}
|
|
71
|
+
// At start: a job that was running when Slopify stopped is queued again, unless a newer one
|
|
72
|
+
// already waits for its template, which then takes its place.
|
|
73
|
+
export function recoverJobs(db) {
|
|
74
|
+
transact(db, () => {
|
|
75
|
+
db.prepare(`DELETE FROM template_sample_jobs WHERE state='running' AND template_id IN
|
|
76
|
+
(SELECT template_id FROM template_sample_jobs WHERE state='queued')`).run();
|
|
77
|
+
db.prepare("UPDATE template_sample_jobs SET state='queued' WHERE state='running'").run();
|
|
78
|
+
});
|
|
79
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { levelFile } from "../loudness/loudnorm.js";
|
|
6
|
+
import { encodeHeadroom, videoTruePeak, } from "../loudness/model.js";
|
|
7
|
+
import { runFfmpeg } from "../video/ffmpeg.js";
|
|
8
|
+
// Voice's loudness before and after: Slopify's sample narration as it came, and the same
|
|
9
|
+
// levelled to the template's video target, as the run's master would. Phase 3's own voice sample
|
|
10
|
+
// takes the bundled narration's place.
|
|
11
|
+
// The target a template's video is levelled to, or undefined while Level the volume is off.
|
|
12
|
+
export function loudnessGoalOf(document, fallback) {
|
|
13
|
+
const chosen = document.form.loudness;
|
|
14
|
+
if (!(chosen?.enabled ?? fallback.enabled))
|
|
15
|
+
return undefined;
|
|
16
|
+
return {
|
|
17
|
+
lufs: chosen?.videoLufs ?? fallback.videoLufs,
|
|
18
|
+
truePeak: videoTruePeak - encodeHeadroom,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export function loudnessHash(goal) {
|
|
22
|
+
return createHash("sha256")
|
|
23
|
+
.update(JSON.stringify({ version: 2, lufs: goal.lufs, truePeak: goal.truePeak }))
|
|
24
|
+
.digest("hex");
|
|
25
|
+
}
|
|
26
|
+
const readingSchema = z.object({
|
|
27
|
+
before: z.number(),
|
|
28
|
+
after: z.number(),
|
|
29
|
+
target: z.number(),
|
|
30
|
+
ceiling: z.number(),
|
|
31
|
+
});
|
|
32
|
+
const hashPattern = /^[a-f0-9]{64}$/;
|
|
33
|
+
export function createLoudnessSamples(deps) {
|
|
34
|
+
const pathOf = (hash, extension) => join(deps.dir, `${hash}.${extension}`);
|
|
35
|
+
const file = (hash) => {
|
|
36
|
+
if (!hashPattern.test(hash))
|
|
37
|
+
return undefined;
|
|
38
|
+
try {
|
|
39
|
+
const stats = statSync(pathOf(hash, "mp3"));
|
|
40
|
+
return stats.isFile() && stats.size > 0
|
|
41
|
+
? { path: pathOf(hash, "mp3"), bytes: stats.size }
|
|
42
|
+
: undefined;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
return {
|
|
49
|
+
source: deps.source,
|
|
50
|
+
file,
|
|
51
|
+
reading: (hash) => {
|
|
52
|
+
if (file(hash) === undefined)
|
|
53
|
+
return undefined;
|
|
54
|
+
try {
|
|
55
|
+
return readingSchema.parse(JSON.parse(readFileSync(pathOf(hash, "json"), "utf8")));
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
make: async (goal, signal) => {
|
|
62
|
+
const hash = loudnessHash(goal);
|
|
63
|
+
if (file(hash) !== undefined)
|
|
64
|
+
return;
|
|
65
|
+
mkdirSync(deps.dir, { recursive: true, mode: 0o700 });
|
|
66
|
+
const work = join(deps.dir, `${hash}.${randomUUID()}`);
|
|
67
|
+
const priority = deps.priority?.();
|
|
68
|
+
const run = { bin: deps.ffmpeg, log: deps.log, signal, priority };
|
|
69
|
+
try {
|
|
70
|
+
const levelled = await levelFile(run, deps.source, `${work}.wav`, goal, {
|
|
71
|
+
sampleRate: 48_000,
|
|
72
|
+
channels: 1,
|
|
73
|
+
});
|
|
74
|
+
await runFfmpeg({
|
|
75
|
+
bin: deps.ffmpeg,
|
|
76
|
+
args: [
|
|
77
|
+
"-hide_banner",
|
|
78
|
+
"-nostdin",
|
|
79
|
+
"-loglevel",
|
|
80
|
+
"error",
|
|
81
|
+
"-y",
|
|
82
|
+
"-i",
|
|
83
|
+
`${work}.wav`,
|
|
84
|
+
"-c:a",
|
|
85
|
+
"libmp3lame",
|
|
86
|
+
"-b:a",
|
|
87
|
+
"128k",
|
|
88
|
+
"-f",
|
|
89
|
+
"mp3",
|
|
90
|
+
`${work}.mp3`,
|
|
91
|
+
],
|
|
92
|
+
signal,
|
|
93
|
+
log: deps.log,
|
|
94
|
+
priority,
|
|
95
|
+
onProgress: () => undefined,
|
|
96
|
+
});
|
|
97
|
+
writeFileSync(pathOf(hash, "json"), JSON.stringify({
|
|
98
|
+
before: Math.round(levelled.before.integrated * 10) / 10,
|
|
99
|
+
after: Math.round(levelled.after.integrated * 10) / 10,
|
|
100
|
+
target: goal.lufs,
|
|
101
|
+
ceiling: goal.truePeak,
|
|
102
|
+
}), { mode: 0o600 });
|
|
103
|
+
renameSync(`${work}.mp3`, pathOf(hash, "mp3"));
|
|
104
|
+
}
|
|
105
|
+
finally {
|
|
106
|
+
rmSync(`${work}.wav`, { force: true });
|
|
107
|
+
rmSync(`${work}.mp3`, { force: true });
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { templateById } from "../project-templates/repo.js";
|
|
2
|
+
import { finishJob } from "./jobs.js";
|
|
3
|
+
import { priceScheduledRun } from "./schedule-price.js";
|
|
4
|
+
import { priceKey, templateSchedules } from "./service.js";
|
|
5
|
+
// What one sample job makes: the free samples of its version, its paid sample unless it asked
|
|
6
|
+
// for the free ones only, then the price of each schedule's run, the studio told after each so its page fills in as they land. A template deleted since
|
|
7
|
+
// the job was queued makes nothing.
|
|
8
|
+
export function sampleMaker(deps) {
|
|
9
|
+
return async (job, signal) => {
|
|
10
|
+
const template = templateById(deps.db, job.templateId, job.templateVersion);
|
|
11
|
+
if (template === undefined)
|
|
12
|
+
return;
|
|
13
|
+
// A bake-off makes only its copies: the template's own samples are current already.
|
|
14
|
+
if (job.kind === "bakeoff") {
|
|
15
|
+
await deps.bakeoff?.(job, signal);
|
|
16
|
+
deps.changed(template.id);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
await deps.free.make(template.document, signal);
|
|
20
|
+
deps.changed(template.id);
|
|
21
|
+
if (job.kind !== "free" && deps.paid !== undefined && !signal.aborted) {
|
|
22
|
+
await deps.paid(job, signal);
|
|
23
|
+
// Made: a quit while the schedules are priced below never queues it again.
|
|
24
|
+
finishJob(deps.db, job.templateId);
|
|
25
|
+
deps.changed(template.id);
|
|
26
|
+
}
|
|
27
|
+
const head = templateById(deps.db, job.templateId);
|
|
28
|
+
if (head === undefined)
|
|
29
|
+
return;
|
|
30
|
+
for (const schedule of templateSchedules(deps.db, head.id)) {
|
|
31
|
+
if (signal.aborted)
|
|
32
|
+
return;
|
|
33
|
+
const key = priceKey(schedule, head);
|
|
34
|
+
if (deps.prices.get(key) !== undefined)
|
|
35
|
+
continue;
|
|
36
|
+
deps.prices.set(key, await priceScheduledRun(deps.review, head, schedule));
|
|
37
|
+
deps.changed(head.id);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// Template studio (4.0): a template is set up once, section by section, with live samples on a
|
|
2
|
+
// hidden project, then marked ready for its schedules. Not to be confused with `slices/studio`,
|
|
3
|
+
// which is the YouTube Studio pack and its extension.
|
|
4
|
+
// In pipeline order: the studio's rail shows them so.
|
|
5
|
+
export const templateSections = [
|
|
6
|
+
"subject",
|
|
7
|
+
"script",
|
|
8
|
+
"voice",
|
|
9
|
+
"pictures",
|
|
10
|
+
"video",
|
|
11
|
+
"thumbnail",
|
|
12
|
+
"shorts",
|
|
13
|
+
"document",
|
|
14
|
+
"reviews",
|
|
15
|
+
"upload",
|
|
16
|
+
"schedule",
|
|
17
|
+
];
|
|
18
|
+
export const sectionLabel = {
|
|
19
|
+
subject: "Subject & research",
|
|
20
|
+
script: "Script",
|
|
21
|
+
voice: "Voice",
|
|
22
|
+
pictures: "Pictures",
|
|
23
|
+
video: "Video & captions",
|
|
24
|
+
thumbnail: "Thumbnail & title",
|
|
25
|
+
shorts: "Shorts",
|
|
26
|
+
document: "Document (PDF)",
|
|
27
|
+
reviews: "Reviews & checkpoints",
|
|
28
|
+
upload: "Upload pack",
|
|
29
|
+
schedule: "Schedule",
|
|
30
|
+
};
|
|
31
|
+
// What a section's sample is: made for the current version, being made, queued behind another,
|
|
32
|
+
// failed with a fix, made for an older version, switched off, or provided by the user at run time.
|
|
33
|
+
export const sectionStates = [
|
|
34
|
+
"current",
|
|
35
|
+
"making",
|
|
36
|
+
"waiting",
|
|
37
|
+
"needs-fix",
|
|
38
|
+
"stale",
|
|
39
|
+
"off",
|
|
40
|
+
"provided",
|
|
41
|
+
];
|
|
42
|
+
// A version's gate. A version without one was never checked in the studio.
|
|
43
|
+
export const gateStates = ["legacy", "ready", "carried"];
|
|
44
|
+
export const sampleVersionStates = [
|
|
45
|
+
"queued",
|
|
46
|
+
"making",
|
|
47
|
+
"done",
|
|
48
|
+
"failed",
|
|
49
|
+
"skipped",
|
|
50
|
+
"held",
|
|
51
|
+
];
|
|
52
|
+
export const sampleJobKinds = ["save", "topic", "full-script", "bakeoff", "free"];
|
|
53
|
+
// The topic every template's sample is made about until the user picks another.
|
|
54
|
+
export const fallbackSampleTopic = "The history of the lighthouse";
|