@gentbajko/slopify 2.5.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -21
- package/NOTICE +12 -0
- package/README.md +24 -41
- package/SUBTITLES.md +1 -1
- package/dist/adapter-registry.js +31 -0
- package/dist/adapters/alignment/cache.js +4 -1
- package/dist/adapters/alignment/ctc.js +25 -23
- package/dist/adapters/alignment/index.js +37 -6
- package/dist/adapters/alignment/multilingual.js +267 -0
- package/dist/adapters/alignment/protocol.js +11 -0
- package/dist/adapters/alignment/quality.js +4 -3
- package/dist/adapters/alignment/sentences.js +95 -0
- package/dist/adapters/alignment/spec.js +24 -0
- package/dist/adapters/alignment/spell.js +769 -0
- package/dist/adapters/alignment/text.js +56 -9
- package/dist/adapters/alignment/window.js +34 -30
- package/dist/adapters/alignment/worker.js +23 -21
- package/dist/adapters/explain.js +11 -11
- package/dist/adapters/host-cli/index.js +44 -5
- package/dist/adapters/host-cli/transport.js +7 -5
- package/dist/adapters/image/bytes.js +2 -2
- package/dist/adapters/image/codex-output.js +1 -1
- package/dist/adapters/image/codex.js +87 -22
- package/dist/adapters/image/fal.js +13 -5
- package/dist/adapters/image/google.js +11 -8
- package/dist/adapters/image/openai.js +14 -7
- package/dist/adapters/image/reference.js +34 -0
- package/dist/adapters/image/replicate.js +7 -2
- package/dist/adapters/image/video.js +2 -2
- package/dist/adapters/key-probes.js +85 -0
- package/dist/adapters/llm/claude-code.js +168 -32
- package/dist/adapters/llm/cli-login-error.js +1 -1
- package/dist/adapters/llm/codex-limits.js +157 -0
- package/dist/adapters/llm/codex.js +99 -12
- package/dist/adapters/llm/document-workspace.js +2 -2
- package/dist/adapters/llm/gemini-workspace.js +1 -1
- package/dist/adapters/llm/gemini.js +16 -3
- package/dist/adapters/llm/image-workspace.js +58 -0
- package/dist/adapters/llm/openrouter.js +11 -3
- package/dist/adapters/llm/run-cli.js +12 -7
- package/dist/adapters/tts/cartesia.js +10 -0
- package/dist/adapters/tts/elevenlabs.js +31 -9
- package/dist/adapters/tts/gemini.js +206 -0
- package/dist/adapters/tts/inworld-async.js +9 -5
- package/dist/adapters/tts/inworld.js +17 -3
- package/dist/adapters/tts/openai.js +3 -0
- package/dist/adapters/tts/pcm-mp3.js +62 -0
- package/dist/adapters/tts/system.js +147 -0
- package/dist/adapters/tts/voice-languages.js +80 -0
- package/dist/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/NotoSansArabic-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/NotoSansDevanagari-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/NotoSansHebrew-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/NotoSansThai-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/OFL.txt +93 -0
- package/dist/assets/fonts/noto/SOURCE.txt +18 -0
- package/dist/assets/models.yaml +38 -0
- package/dist/assets/sample/sample-audiobook.tar +0 -0
- package/dist/assets/sample/sample-podcast.tar +0 -0
- package/dist/assets/sample/sample-project.tar +0 -0
- package/dist/assets/style-preview/narration.mp3 +0 -0
- package/dist/assets/style-preview/tall-1.jpg +0 -0
- package/dist/assets/style-preview/tall-2.jpg +0 -0
- package/dist/assets/style-preview/tall-3.jpg +0 -0
- package/dist/assets/style-preview/wide-1.jpg +0 -0
- package/dist/assets/style-preview/wide-2.jpg +0 -0
- package/dist/assets/style-preview/wide-3.jpg +0 -0
- package/dist/catalog/merge.js +132 -0
- package/dist/catalog/registry.js +17 -14
- package/dist/catalog/schema.js +2 -0
- package/dist/catalog/store.js +147 -8
- package/dist/catalog/validate.js +2 -2
- package/dist/compose.yaml +71 -0
- package/dist/edge/autostart/docker-record.js +32 -0
- package/dist/edge/autostart/docker.js +118 -0
- package/dist/edge/autostart/index.js +78 -0
- package/dist/edge/autostart/launcher.js +176 -0
- package/dist/edge/autostart/model.js +3 -0
- package/dist/edge/autostart/native.js +210 -0
- package/dist/edge/autostart/prompt.js +72 -0
- package/dist/edge/autostart/service.js +90 -0
- package/dist/edge/cli-args.js +71 -0
- package/dist/edge/cli.js +64 -63
- package/dist/edge/docker-install/activation.js +56 -0
- package/dist/edge/docker-install/apply.js +458 -0
- package/dist/edge/docker-install/engine.js +362 -0
- package/dist/edge/docker-install/run.js +160 -0
- package/dist/edge/docker-install/state.js +146 -0
- package/dist/edge/{docker-projects → docker-install}/volume.js +1 -5
- package/dist/edge/docker.js +14 -22
- package/dist/edge/events/hub.js +3 -1
- package/dist/edge/host-cli.js +1 -1
- package/dist/edge/http/actions.js +38 -11
- package/dist/edge/http/app.js +60 -2
- package/dist/edge/http/auditions.js +70 -0
- package/dist/edge/http/autostart.js +37 -0
- package/dist/edge/http/byte-range.js +42 -0
- package/dist/edge/http/channel-memory.js +92 -0
- package/dist/edge/http/channels.js +128 -0
- package/dist/edge/http/diagnostics.js +1 -0
- package/dist/edge/http/draft-files.js +2 -4
- package/dist/edge/http/entries.js +22 -3
- package/dist/edge/http/files.js +11 -7
- package/dist/edge/http/folder-location.js +1 -1
- package/dist/edge/http/home.js +39 -0
- package/dist/edge/http/host-cli.js +29 -5
- package/dist/edge/http/narration-peaks.js +76 -0
- package/dist/edge/http/onboarding.js +212 -0
- package/dist/edge/http/open-folder.js +1 -1
- package/dist/edge/http/patch-notes.js +45 -0
- package/dist/edge/http/planning.js +4 -2
- package/dist/edge/http/project-create.js +87 -0
- package/dist/edge/http/project-templates.js +19 -2
- package/dist/edge/http/projects.js +37 -100
- package/dist/edge/http/prompts.js +40 -3
- package/dist/edge/http/pronunciations.js +23 -4
- package/dist/edge/http/providers.js +119 -3
- package/dist/edge/http/reviews.js +131 -0
- package/dist/edge/http/revision-files.js +3 -5
- package/dist/edge/http/revisions.js +1 -1
- package/dist/edge/http/run-cost.js +25 -0
- package/dist/edge/http/schedules.js +115 -6
- package/dist/edge/http/settings.js +84 -6
- package/dist/edge/http/storage-files.js +50 -0
- package/dist/edge/http/storage.js +43 -2
- package/dist/edge/http/studio.js +287 -0
- package/dist/edge/http/style-preview.js +61 -0
- package/dist/edge/http/subtitles.js +1 -1
- package/dist/edge/http/trash.js +69 -0
- package/dist/edge/http/tutorials.js +59 -0
- package/dist/edge/http/update.js +24 -3
- package/dist/edge/http/whats-new.js +15 -0
- package/dist/edge/http/youtube-edits.js +92 -0
- package/dist/edge/native-update.js +62 -0
- package/dist/extension/slopify-studio-chrome.zip +0 -0
- package/dist/extension/slopify-studio-firefox.zip +0 -0
- package/dist/host-cli/gemini-login.js +83 -0
- package/dist/host-cli/open-folder.js +10 -10
- package/dist/host-cli/runtime.js +9 -1
- package/dist/host-cli/status.js +4 -2
- package/dist/kernel/db/migrations/0025-automatic-reviews.sql +40 -0
- package/dist/kernel/db/migrations/0026-schedule-topic-generation.sql +23 -0
- package/dist/kernel/db/migrations/0027-run-cost.sql +58 -0
- package/dist/kernel/db/migrations/0030-automatic-retries.sql +18 -0
- package/dist/kernel/db/migrations/0031-prompt-history-and-description-edits.sql +33 -0
- package/dist/kernel/db/migrations/0032-channels.sql +65 -0
- package/dist/kernel/db/migrations/0034-script-prompts.sql +23 -0
- package/dist/kernel/db/migrations/0035-narration-aliases.sql +13 -0
- package/dist/kernel/db/migrations/0037-project-uploads.sql +8 -0
- package/dist/kernel/db/migrations/0038-voice-languages.sql +7 -0
- package/dist/kernel/db/migrations/0039-channel-essentials.sql +64 -0
- package/dist/kernel/db/migrations/0040-standalone-usage.sql +35 -0
- package/dist/kernel/db/migrations/0041-cast-hosts.sql +3 -0
- package/dist/kernel/paths.js +27 -6
- package/dist/kernel/pipeline.js +4 -1
- package/dist/kernel/ports/host-cli.js +122 -14
- package/dist/kernel/ports/key-probe.js +1 -0
- package/dist/kernel/ports/languages.js +157 -0
- package/dist/kernel/ports/model.js +10 -5
- package/dist/kernel/ports/narration-aliases.js +60 -0
- package/dist/kernel/ports/plan-limits.js +36 -0
- package/dist/kernel/ports/system-speech.js +203 -0
- package/dist/kernel/runner/attempt.js +17 -8
- package/dist/kernel/runner/audition.js +36 -0
- package/dist/kernel/runner/graph.js +40 -5
- package/dist/kernel/runner/index.js +39 -9
- package/dist/kernel/runner/meter.js +1 -0
- package/dist/kernel/runner/providers.js +142 -7
- package/dist/kernel/runner/retry-policy.js +43 -0
- package/dist/kernel/runner/standalone.js +134 -0
- package/dist/kernel/runner/work-authority.js +28 -2
- package/dist/main.js +277 -16
- package/dist/model-catalog.js +2 -0
- package/dist/patch-notes/0.4.2-to-1.0.md +93 -0
- package/dist/patch-notes/1.0-to-2.0.md +74 -0
- package/dist/patch-notes/2.0-to-3.0.md +257 -0
- package/dist/patch-notes/3.0.0.md +70 -0
- package/dist/patch-notes/3.0.1.md +105 -0
- package/dist/patch-notes/index.json +32 -0
- package/dist/slices/admission/model.js +27 -0
- package/dist/slices/admission/repo.js +24 -4
- package/dist/slices/admission/rules.js +80 -2
- package/dist/slices/admission/schema.js +58 -1
- package/dist/slices/admission/short-mode.js +47 -0
- package/dist/slices/admission/start.js +42 -6
- package/dist/slices/article/continuation.js +2 -2
- package/dist/slices/article/provided-entries.js +2 -2
- package/dist/slices/article/run.js +6 -2
- package/dist/slices/article/segments.js +3 -3
- package/dist/slices/backups/folder.js +14 -6
- package/dist/slices/batch/index.js +7 -2
- package/dist/slices/cancel/index.js +1 -1
- package/dist/slices/channels/cast-images.js +110 -0
- package/dist/slices/channels/cast-match.js +32 -0
- package/dist/slices/channels/images.js +19 -0
- package/dist/slices/channels/model.js +5 -0
- package/dist/slices/channels/rebrand.js +100 -0
- package/dist/slices/channels/repo.js +221 -0
- package/dist/slices/channels/runs.js +129 -0
- package/dist/slices/channels/schema.js +152 -0
- package/dist/slices/channels/service.js +180 -0
- package/dist/slices/channels/studio-csv.js +110 -0
- package/dist/slices/channels/videos.js +120 -0
- package/dist/slices/checkpoints/recovery.js +10 -0
- package/dist/slices/control/index.js +5 -2
- package/dist/slices/control/providers.js +4 -1
- package/dist/slices/document/fonts.js +1 -1
- package/dist/slices/document/render.js +1 -1
- package/dist/slices/episodes/related.js +90 -0
- package/dist/slices/episodes/repo.js +60 -0
- package/dist/slices/episodes/service.js +58 -0
- package/dist/slices/episodes/summarize.js +170 -0
- package/dist/slices/estimate/index.js +109 -28
- package/dist/slices/estimate/requests.js +68 -2
- package/dist/slices/eta/history.js +56 -0
- package/dist/slices/eta/model.js +63 -0
- package/dist/slices/eta/view.js +25 -0
- package/dist/slices/fixes/rules.js +73 -0
- package/dist/slices/fonts/coverage.js +82 -0
- package/dist/slices/fonts/sfnt.js +74 -0
- package/dist/slices/images/run.js +8 -5
- package/dist/slices/images/scale.js +84 -0
- package/dist/slices/library/diff.js +122 -0
- package/dist/slices/library/history.js +100 -0
- package/dist/slices/library/model.js +3 -0
- package/dist/slices/library/photorealistic.js +49 -0
- package/dist/slices/library/repo.js +19 -14
- package/dist/slices/library/save.js +62 -12
- package/dist/slices/library/slots.js +7 -1
- package/dist/slices/library/used-by.js +153 -0
- package/dist/slices/loudness/level-pieces.js +41 -0
- package/dist/slices/loudness/loudnorm.js +164 -0
- package/dist/slices/loudness/model.js +153 -0
- package/dist/slices/model-upkeep/model.js +10 -0
- package/dist/slices/model-upkeep/switch.js +217 -0
- package/dist/slices/model-upkeep/usage.js +185 -0
- package/dist/slices/narration/aliases-library.js +31 -0
- package/dist/slices/narration/aliases-schema.js +53 -0
- package/dist/slices/narration/aliases.js +49 -0
- package/dist/slices/narration/blocks.js +352 -0
- package/dist/slices/narration/chunk.js +19 -7
- package/dist/slices/narration/concat.js +2 -2
- package/dist/slices/narration/describe.js +45 -0
- package/dist/slices/narration/pauses-model.js +63 -0
- package/dist/slices/narration/pauses.js +269 -0
- package/dist/slices/narration/peaks-model.js +1 -0
- package/dist/slices/narration/peaks.js +94 -0
- package/dist/slices/narration/preparation.js +21 -3
- package/dist/slices/narration/pronunciation-chunks.js +3 -3
- package/dist/slices/narration/pronunciation.js +72 -25
- package/dist/slices/narration/run.js +6 -6
- package/dist/slices/narration/shared-glossary.js +1 -1
- package/dist/slices/narration/steering.js +15 -4
- package/dist/slices/notifications/notifier.js +47 -3
- package/dist/slices/notifications/rules.js +48 -3
- package/dist/slices/onboarding/copy.js +143 -0
- package/dist/slices/onboarding/first-run.js +72 -0
- package/dist/slices/onboarding/full-video.js +81 -0
- package/dist/slices/onboarding/install.js +119 -0
- package/dist/slices/onboarding/model.js +85 -0
- package/dist/slices/onboarding/packs.js +491 -0
- package/dist/slices/onboarding/quick-short.js +144 -0
- package/dist/slices/onboarding/sample.js +132 -0
- package/dist/slices/onboarding/state.js +68 -0
- package/dist/slices/onboarding/template.js +73 -0
- package/dist/slices/patch-notes/library.js +73 -0
- package/dist/slices/patch-notes/seen.js +77 -0
- package/dist/slices/play-drafts/convert.js +127 -8
- package/dist/slices/play-drafts/readiness.js +4 -1
- package/dist/slices/play-drafts/repo.js +2 -0
- package/dist/slices/play-drafts/review-inputs.js +20 -3
- package/dist/slices/play-drafts/schema.js +89 -0
- package/dist/slices/play-drafts/service.js +4 -1
- package/dist/slices/play-drafts/start.js +43 -18
- package/dist/slices/project-templates/from-project.js +59 -6
- package/dist/slices/project-templates/next-chapter.js +94 -0
- package/dist/slices/project-templates/one-off.js +26 -0
- package/dist/slices/project-templates/repo.js +2 -1
- package/dist/slices/project-templates/schema.js +7 -1
- package/dist/slices/project-templates/service.js +15 -4
- package/dist/slices/project-templates/setup.js +18 -2
- package/dist/slices/rebuild/admission-repo.js +1 -1
- package/dist/slices/rebuild/model.js +6 -0
- package/dist/slices/rebuild/preview-details.js +46 -3
- package/dist/slices/rebuild/preview-plan.js +13 -6
- package/dist/slices/rebuild/recipe-audio-parts.js +40 -7
- package/dist/slices/rebuild/recipe-audio.js +66 -12
- package/dist/slices/rebuild/recipe-build.js +8 -4
- package/dist/slices/rebuild/recipe-cast.js +18 -0
- package/dist/slices/rebuild/recipe-checkpoints.js +11 -9
- package/dist/slices/rebuild/recipe-describe.js +93 -0
- package/dist/slices/rebuild/recipe-edit.js +13 -1
- package/dist/slices/rebuild/recipe-exports.js +67 -8
- package/dist/slices/rebuild/recipe-input-schema.js +35 -0
- package/dist/slices/rebuild/recipe-legacy.js +9 -0
- package/dist/slices/rebuild/recipe-loudness.js +45 -0
- package/dist/slices/rebuild/recipe-model.js +25 -6
- package/dist/slices/rebuild/recipe-pauses.js +7 -0
- package/dist/slices/rebuild/recipe-preparation.js +3 -3
- package/dist/slices/rebuild/recipe-provider-choice.js +11 -1
- package/dist/slices/rebuild/recipe-reference.js +15 -1
- package/dist/slices/rebuild/recipe-reviews.js +117 -0
- package/dist/slices/rebuild/recipe-save.js +4 -1
- package/dist/slices/rebuild/recipe-shorts.js +23 -3
- package/dist/slices/rebuild/recipe-text.js +114 -19
- package/dist/slices/rebuild/recipe-validation.js +14 -2
- package/dist/slices/rebuild/recipe-visual.js +96 -33
- package/dist/slices/rebuild/recipe-voices.js +286 -0
- package/dist/slices/rebuild/recipe-work.js +77 -6
- package/dist/slices/rebuild/recipe-youtube.js +2 -0
- package/dist/slices/rebuild/recovery-conflict.js +7 -3
- package/dist/slices/rebuild/recovery-model.js +3 -0
- package/dist/slices/rebuild/recovery-selection.js +28 -2
- package/dist/slices/rebuild/recovery.js +35 -17
- package/dist/slices/rebuild/repo.js +3 -3
- package/dist/slices/rebuild/retry.js +18 -0
- package/dist/slices/rebuild/review-redo.js +125 -0
- package/dist/slices/rebuild/runtime-actions.js +5 -0
- package/dist/slices/rebuild/runtime-admission.js +4 -1
- package/dist/slices/rebuild/runtime-animate.js +4 -4
- package/dist/slices/rebuild/runtime-article.js +5 -5
- package/dist/slices/rebuild/runtime-document.js +4 -4
- package/dist/slices/rebuild/runtime-export-bed.js +43 -0
- package/dist/slices/rebuild/runtime-export-edit.js +70 -11
- package/dist/slices/rebuild/runtime-export-inputs.js +52 -10
- package/dist/slices/rebuild/runtime-export-short.js +85 -0
- package/dist/slices/rebuild/runtime-export.js +84 -19
- package/dist/slices/rebuild/runtime-figure-card.js +87 -0
- package/dist/slices/rebuild/runtime-image.js +21 -2
- package/dist/slices/rebuild/runtime-local.js +148 -24
- package/dist/slices/rebuild/runtime-materialize.js +8 -0
- package/dist/slices/rebuild/runtime-narration-reuse.js +2 -1
- package/dist/slices/rebuild/runtime-narration-text.js +7 -7
- package/dist/slices/rebuild/runtime-pictures.js +28 -0
- package/dist/slices/rebuild/runtime-piece-label.js +27 -0
- package/dist/slices/rebuild/runtime-provider.js +126 -18
- package/dist/slices/rebuild/runtime-publication.js +21 -5
- package/dist/slices/rebuild/runtime-review.js +267 -0
- package/dist/slices/rebuild/runtime-run.js +17 -11
- package/dist/slices/rebuild/runtime-shorts.js +44 -17
- package/dist/slices/rebuild/runtime-store.js +62 -11
- package/dist/slices/rebuild/runtime-subtitles.js +72 -19
- package/dist/slices/rebuild/runtime-voices.js +103 -0
- package/dist/slices/rebuild/runtime-youtube.js +7 -6
- package/dist/slices/rebuild/service-readiness.js +3 -3
- package/dist/slices/rebuild/soften.js +49 -0
- package/dist/slices/rebuild/transition-repo.js +9 -2
- package/dist/slices/research/run.js +4 -4
- package/dist/slices/research/synthesis.js +2 -2
- package/dist/slices/reviews/model.js +105 -0
- package/dist/slices/reviews/outcome.js +22 -0
- package/dist/slices/reviews/repo.js +118 -0
- package/dist/slices/reviews/rules.js +63 -0
- package/dist/slices/reviews/verdict.js +71 -0
- package/dist/slices/reviews/view.js +30 -0
- package/dist/slices/revisions/adopt-content.js +5 -2
- package/dist/slices/revisions/adopt.js +3 -1
- package/dist/slices/revisions/downloads.js +7 -1
- package/dist/slices/revisions/image-plan.js +183 -0
- package/dist/slices/revisions/mutation-assets.js +10 -0
- package/dist/slices/revisions/mutation-cues.js +71 -0
- package/dist/slices/revisions/mutation-request.js +2 -1
- package/dist/slices/revisions/mutations.js +97 -3
- package/dist/slices/revisions/schema.js +11 -1
- package/dist/slices/run-cost/limits.js +128 -0
- package/dist/slices/run-cost/meter.js +47 -0
- package/dist/slices/run-cost/panel.js +256 -0
- package/dist/slices/run-cost/pricing.js +95 -0
- package/dist/slices/run-cost/week.js +108 -0
- package/dist/slices/schedules/agenda.js +222 -0
- package/dist/slices/schedules/repo.js +58 -18
- package/dist/slices/schedules/scheduler.js +68 -17
- package/dist/slices/schedules/schema.js +179 -1
- package/dist/slices/schedules/service.js +43 -4
- package/dist/slices/schedules/similar.js +128 -0
- package/dist/slices/schedules/topic-list.js +163 -0
- package/dist/slices/schedules/topics.js +523 -0
- package/dist/slices/settings/first-run.js +106 -0
- package/dist/slices/settings/health.js +247 -0
- package/dist/slices/settings/key-guides.js +138 -0
- package/dist/slices/settings/key-test.js +93 -0
- package/dist/slices/settings/keys.js +10 -5
- package/dist/slices/settings/model-reach.js +52 -0
- package/dist/slices/settings/model.js +55 -1
- package/dist/slices/settings/models.js +5 -1
- package/dist/slices/settings/playback.js +15 -0
- package/dist/slices/settings/readiness.js +14 -2
- package/dist/slices/settings/repo.js +17 -3
- package/dist/slices/settings/tutorial-schema.js +5 -0
- package/dist/slices/settings/voices.js +64 -4
- package/dist/slices/settings/whats-new.js +64 -0
- package/dist/slices/shorts/captions.js +9 -0
- package/dist/slices/shorts/pick.js +3 -3
- package/dist/slices/shorts/prompts.js +1 -1
- package/dist/slices/shorts/render.js +63 -13
- package/dist/slices/storage/asset-name.js +3 -0
- package/dist/slices/storage/backup-export.js +48 -9
- package/dist/slices/storage/backup-format.js +38 -1
- package/dist/slices/storage/backup-import.js +191 -19
- package/dist/slices/storage/delete-project.js +7 -4
- package/dist/slices/storage/documents.js +87 -0
- package/dist/slices/storage/downloads.js +33 -1
- package/dist/slices/storage/files-location.js +531 -0
- package/dist/slices/storage/layout.js +14 -4
- package/dist/slices/storage/model.js +11 -0
- package/dist/slices/storage/portable.js +94 -12
- package/dist/slices/storage/reconcile.js +14 -1
- package/dist/slices/storage/repo.js +4 -1
- package/dist/slices/storage/schema.js +11 -0
- package/dist/slices/storage/trim.js +146 -0
- package/dist/slices/studio/disclosure.js +74 -0
- package/dist/slices/studio/model.js +30 -0
- package/dist/slices/studio/pack.js +211 -0
- package/dist/slices/studio/queue.js +69 -0
- package/dist/slices/studio/settings.js +170 -0
- package/dist/slices/style-preview/images.js +61 -0
- package/dist/slices/style-preview/narration.js +33 -0
- package/dist/slices/style-preview/render.js +148 -0
- package/dist/slices/style-preview/schema.js +59 -0
- package/dist/slices/style-preview/service.js +92 -0
- package/dist/slices/style-preview/settings.js +139 -0
- package/dist/slices/subtitles/captions.js +42 -9
- package/dist/slices/subtitles/model.js +22 -0
- package/dist/slices/subtitles/prepare.js +27 -16
- package/dist/slices/subtitles/transcript.js +1 -1
- package/dist/slices/thumbnail/run.js +7 -7
- package/dist/slices/trash/model.js +25 -0
- package/dist/slices/trash/service.js +273 -0
- package/dist/slices/tutorials/anchors.js +57 -0
- package/dist/slices/tutorials/library.js +185 -0
- package/dist/slices/uploads/repo.js +20 -0
- package/dist/slices/video/ambient-bed-schema.js +15 -0
- package/dist/slices/video/ambient-bed.js +95 -0
- package/dist/slices/video/ambient-mix.js +72 -0
- package/dist/slices/video/audio-inputs.js +2 -2
- package/dist/slices/video/cards.js +5 -2
- package/dist/slices/video/edit-list.js +23 -2
- package/dist/slices/video/edit-settings.js +13 -3
- package/dist/slices/video/ffmpeg.js +63 -9
- package/dist/slices/video/figure-card.js +492 -0
- package/dist/slices/video/figure-spans.js +51 -0
- package/dist/slices/video/plan.js +125 -13
- package/dist/slices/video/run.js +14 -12
- package/dist/slices/video/slideshow.js +39 -4
- package/dist/slices/voices/attribution.js +69 -0
- package/dist/slices/voices/audio-files.js +87 -0
- package/dist/slices/voices/audition.js +25 -0
- package/dist/slices/voices/cast.js +74 -0
- package/dist/slices/voices/grouping.js +59 -0
- package/dist/slices/voices/join.js +35 -0
- package/dist/slices/voices/languages.js +25 -0
- package/dist/slices/voices/model.js +218 -0
- package/dist/slices/voices/palette.js +26 -0
- package/dist/slices/voices/panel.js +91 -0
- package/dist/slices/voices/portraits.js +55 -0
- package/dist/slices/voices/script.js +136 -0
- package/dist/slices/voices/timing.js +58 -0
- package/dist/slices/youtube/answer.js +1 -1
- package/dist/slices/youtube/chapters.js +126 -0
- package/dist/slices/youtube/edits-repo.js +84 -0
- package/dist/slices/youtube/edits.js +89 -0
- package/dist/slices/youtube/placeholders.js +98 -0
- package/dist/slices/youtube/transcript.js +11 -4
- package/dist/tutorials/AI-CLIs.md +146 -0
- package/dist/tutorials/Backups.md +121 -0
- package/dist/tutorials/Calendar.md +103 -0
- package/dist/tutorials/Cast-Library.md +112 -0
- package/dist/tutorials/Channels.md +148 -0
- package/dist/tutorials/Costs-and-Run-Cost.md +136 -0
- package/dist/tutorials/Docker.md +171 -0
- package/dist/tutorials/Document-Themes.md +207 -0
- package/dist/tutorials/Editing-a-Project.md +204 -0
- package/dist/tutorials/FAQ.md +134 -0
- package/dist/tutorials/First-Launch-and-Welcome.md +153 -0
- package/dist/tutorials/Home-and-Projects.md +174 -0
- package/dist/tutorials/Home.md +119 -0
- package/dist/tutorials/Install.md +154 -0
- package/dist/tutorials/Intros-and-Outros.md +86 -0
- package/dist/tutorials/Keyboard-Shortcuts-and-Command-Palette.md +215 -0
- package/dist/tutorials/Library-Overview.md +75 -0
- package/dist/tutorials/Models.md +133 -0
- package/dist/tutorials/Multiple-Voices.md +161 -0
- package/dist/tutorials/Narration-Aliases-and-Glossary.md +129 -0
- package/dist/tutorials/Notifications.md +86 -0
- package/dist/tutorials/Other-Languages.md +110 -0
- package/dist/tutorials/PDF-Documents.md +100 -0
- package/dist/tutorials/Play-Images.md +135 -0
- package/dist/tutorials/Play-Narration.md +167 -0
- package/dist/tutorials/Play-Outputs.md +109 -0
- package/dist/tutorials/Play-Overview.md +216 -0
- package/dist/tutorials/Play-Title-and-Article.md +135 -0
- package/dist/tutorials/Play-Video-and-Style.md +143 -0
- package/dist/tutorials/Project-Page.md +228 -0
- package/dist/tutorials/Prompts.md +136 -0
- package/dist/tutorials/Providers-and-Keys.md +290 -0
- package/dist/tutorials/Publishing-to-YouTube.md +135 -0
- package/dist/tutorials/Recovery-and-Retries.md +141 -0
- package/dist/tutorials/Reviews-and-Checkpoints.md +143 -0
- package/dist/tutorials/Schedules.md +154 -0
- package/dist/tutorials/Settings-Reference.md +258 -0
- package/dist/tutorials/Shorts.md +116 -0
- package/dist/tutorials/Start-at-Login.md +126 -0
- package/dist/tutorials/Studio-Extension.md +146 -0
- package/dist/tutorials/Templates.md +146 -0
- package/dist/tutorials/Trash.md +87 -0
- package/dist/tutorials/Troubleshooting.md +149 -0
- package/dist/tutorials/Uninstalling-and-Moving.md +113 -0
- package/dist/tutorials/Updating-and-Patch-Notes.md +120 -0
- package/dist/tutorials/Video-Editing.md +135 -0
- package/dist/tutorials/Where-Your-Files-Live.md +149 -0
- package/dist/tutorials/YouTube-Description.md +132 -0
- package/dist/tutorials/Your-First-Short.md +125 -0
- package/dist/tutorials/_Footer.md +1 -0
- package/dist/tutorials/_Sidebar.md +69 -0
- package/dist/updater/service.js +66 -21
- package/dist/web/assets/index-DBLSfqA-.css +1 -0
- package/dist/web/assets/index-DFdIhmm0.js +307 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
- package/dist/web/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
- package/dist/web/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
- package/dist/web/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
- package/dist/web/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
- package/dist/web/assets/{pdf-BLF2i8c2.js → pdf-B9OY1ioX.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +5 -5
- package/dist/edge/docker-launch.js +0 -81
- package/dist/edge/docker-projects/activation.js +0 -35
- package/dist/edge/docker-projects/claims.js +0 -85
- package/dist/edge/docker-projects/committed.js +0 -40
- package/dist/edge/docker-projects/engine.js +0 -461
- package/dist/edge/docker-projects/install.js +0 -254
- package/dist/edge/docker-projects/prune.js +0 -66
- package/dist/edge/docker-projects/recover.js +0 -100
- package/dist/edge/docker-projects/state.js +0 -225
- package/dist/web/assets/index-CktpxlJd.js +0 -171
- package/dist/web/assets/index-DB-vOnGU.css +0 -1
- package/scripts/docker-run.sh +0 -3
- /package/dist/edge/{docker-projects → docker-install}/tree.js +0 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { derive } from "../../kernel/runner/graph.js";
|
|
3
|
+
import { liveProject, projectPaused, stageStandingsByProject } from "../admission/repo.js";
|
|
4
|
+
import { queueEntries } from "../batch/index.js";
|
|
5
|
+
import { sampleIds } from "../onboarding/model.js";
|
|
6
|
+
import { readSampleRecord } from "../onboarding/state.js";
|
|
7
|
+
import { limitWaitsByProject, listingWait } from "../run-cost/panel.js";
|
|
8
|
+
import { uploadedProjects } from "../uploads/repo.js";
|
|
9
|
+
import { nextOccurrence } from "./calendar.js";
|
|
10
|
+
import { scheduleRows } from "./repo.js";
|
|
11
|
+
import { calendarMaxDays, } from "./schema.js";
|
|
12
|
+
import { renderedTitle } from "./topic-list.js";
|
|
13
|
+
// Enough for a run every day of the longest range, with room for a queue.
|
|
14
|
+
const occurrencesMax = 1000;
|
|
15
|
+
const dayMs = 24 * 60 * 60_000;
|
|
16
|
+
// Everything the coming weeks hold between `from` and `to`: every run a live schedule will
|
|
17
|
+
// make, with the topic it will use; every project running in the range or finished in it; and
|
|
18
|
+
// every batch item still waiting its turn.
|
|
19
|
+
export function calendarRange(deps, from, to) {
|
|
20
|
+
if (Number.isNaN(from.valueOf()) ||
|
|
21
|
+
Number.isNaN(to.valueOf()) ||
|
|
22
|
+
to <= from ||
|
|
23
|
+
to.valueOf() - from.valueOf() > calendarMaxDays * dayMs)
|
|
24
|
+
return { ok: false, reason: "invalid-input" };
|
|
25
|
+
const now = deps.clock.now();
|
|
26
|
+
const runs = scheduleRows(deps.db)
|
|
27
|
+
.flatMap((schedule) => upcomingRuns(deps, schedule, now, from, to))
|
|
28
|
+
.sort((a, b) => a.at.localeCompare(b.at) || a.scheduleName.localeCompare(b.scheduleName));
|
|
29
|
+
return {
|
|
30
|
+
ok: true,
|
|
31
|
+
value: {
|
|
32
|
+
from: from.toISOString(),
|
|
33
|
+
to: to.toISOString(),
|
|
34
|
+
runs,
|
|
35
|
+
projects: projectsIn(deps, from, to),
|
|
36
|
+
queued: queued(deps),
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
// Run k from now uses the k-th queued topic. A schedule that does not find its own topics
|
|
41
|
+
// completes when its queue empties, so its runs stop there; one with no queue at all runs the
|
|
42
|
+
// template as saved every time.
|
|
43
|
+
function upcomingRuns(deps, schedule, now, from, to) {
|
|
44
|
+
if (schedule.deletedAt !== null)
|
|
45
|
+
return [];
|
|
46
|
+
if (schedule.status !== "active" && schedule.status !== "paused")
|
|
47
|
+
return [];
|
|
48
|
+
const paused = schedule.status === "paused";
|
|
49
|
+
// A paused schedule resumes from the next occurrence after the moment Resume is pressed.
|
|
50
|
+
let at = schedule.nextRunAt === null
|
|
51
|
+
? null
|
|
52
|
+
: paused
|
|
53
|
+
? nextOccurrence(schedule.cadence, schedule.timezone, now)
|
|
54
|
+
: new Date(schedule.nextRunAt);
|
|
55
|
+
const template = deps.template(schedule.templateId, schedule.templateVersion);
|
|
56
|
+
const templateName = template?.name ?? null;
|
|
57
|
+
const form = template?.document.form;
|
|
58
|
+
const { items } = schedule;
|
|
59
|
+
const generation = schedule.topicGeneration.mode;
|
|
60
|
+
const held = generation === "hold" ? schedule.topics.held : 0;
|
|
61
|
+
const runs = [];
|
|
62
|
+
for (let k = 0; at !== null && at < to && k < occurrencesMax; k++) {
|
|
63
|
+
if (generation === "off" && items.length > 0 && k >= items.length)
|
|
64
|
+
break;
|
|
65
|
+
const queuedTopic = items[k];
|
|
66
|
+
// Known for a queued topic, and for a run of the template as saved; a held or generated
|
|
67
|
+
// topic has no text yet.
|
|
68
|
+
const known = queuedTopic !== undefined || generation === "off";
|
|
69
|
+
if (at >= from)
|
|
70
|
+
runs.push({
|
|
71
|
+
at: at.toISOString(),
|
|
72
|
+
scheduleId: schedule.id,
|
|
73
|
+
scheduleName: schedule.name,
|
|
74
|
+
scheduleVersion: schedule.version,
|
|
75
|
+
paused,
|
|
76
|
+
templateId: schedule.templateId,
|
|
77
|
+
templateVersion: schedule.templateVersion,
|
|
78
|
+
templateName,
|
|
79
|
+
index: queuedTopic === undefined ? null : k,
|
|
80
|
+
topic: queuedTopic?.title ?? null,
|
|
81
|
+
topicSource: queuedTopic !== undefined
|
|
82
|
+
? "queued"
|
|
83
|
+
: generation === "off"
|
|
84
|
+
? "template"
|
|
85
|
+
: k < items.length + held
|
|
86
|
+
? "held"
|
|
87
|
+
: "generated",
|
|
88
|
+
renderedTitle: form !== undefined && known ? renderedTitle(form, schedule, queuedTopic) : null,
|
|
89
|
+
});
|
|
90
|
+
if (schedule.cadence.kind === "once")
|
|
91
|
+
break;
|
|
92
|
+
at = nextOccurrence(schedule.cadence, schedule.timezone, new Date(at.valueOf() + 1000));
|
|
93
|
+
}
|
|
94
|
+
return runs;
|
|
95
|
+
}
|
|
96
|
+
const projectRow = z.object({
|
|
97
|
+
id: z.string(),
|
|
98
|
+
title: z.string(),
|
|
99
|
+
config: z.string(),
|
|
100
|
+
created_at: z.string(),
|
|
101
|
+
finished_at: z.string().nullable(),
|
|
102
|
+
});
|
|
103
|
+
// Projects the person has something to decide on in the current revision: a review
|
|
104
|
+
// checkpoint holding the run, or an automatic review that failed an item and waits for
|
|
105
|
+
// Overrule or Redo (its latest verdict, with no redo under way).
|
|
106
|
+
function awaitingReview(db) {
|
|
107
|
+
const ids = db
|
|
108
|
+
.prepare(`SELECT c.project_id AS id FROM review_checkpoints c
|
|
109
|
+
JOIN project_heads h ON h.project_id=c.project_id AND h.revision_id=c.revision_id
|
|
110
|
+
WHERE c.state IN ('held','pending-review') AND c.approved_at IS NULL
|
|
111
|
+
UNION
|
|
112
|
+
SELECT v.project_id AS id FROM review_verdicts v
|
|
113
|
+
JOIN project_heads h ON h.project_id=v.project_id AND h.revision_id=v.revision_id
|
|
114
|
+
WHERE v.passed=0 AND v.action IS NULL AND (v.redo_state IS NULL OR v.redo_state='failed')
|
|
115
|
+
AND NOT EXISTS (SELECT 1 FROM review_verdicts n WHERE n.project_id=v.project_id
|
|
116
|
+
AND n.item_key=v.item_key AND (n.created_at>v.created_at OR (n.created_at=v.created_at AND n.rowid>v.rowid)))`)
|
|
117
|
+
.all();
|
|
118
|
+
return new Set(ids.map((row) => z.string().parse(row.id)));
|
|
119
|
+
}
|
|
120
|
+
// Whether a finished project's settings make a video: projects stored before sources were
|
|
121
|
+
// recorded made one.
|
|
122
|
+
const videoSource = z.object({ sources: z.object({ video: z.string() }).partial() }).partial();
|
|
123
|
+
function makesVideo(config) {
|
|
124
|
+
try {
|
|
125
|
+
const parsed = videoSource.safeParse(JSON.parse(config));
|
|
126
|
+
return !parsed.success || parsed.data.sources?.video !== "off";
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function sampleProjectIds(db) {
|
|
133
|
+
return new Set(sampleIds.flatMap((id) => {
|
|
134
|
+
const record = readSampleRecord(db, id);
|
|
135
|
+
return record?.projectId === undefined ? [] : [record.projectId];
|
|
136
|
+
}));
|
|
137
|
+
}
|
|
138
|
+
// What a project on the calendar asks of the person, if anything. The same readings Home's
|
|
139
|
+
// Needs you uses: a failed or paused run, or one waiting for a review.
|
|
140
|
+
function needsOf(state, reviewing) {
|
|
141
|
+
if (state === "failed")
|
|
142
|
+
return "failed";
|
|
143
|
+
if (state === "paused")
|
|
144
|
+
return "paused";
|
|
145
|
+
if (reviewing && state !== "canceled")
|
|
146
|
+
return "review";
|
|
147
|
+
return undefined;
|
|
148
|
+
}
|
|
149
|
+
// Running (or waiting) projects made before `to`, and finished ones whose last stage ended in
|
|
150
|
+
// the range, with what each needs from the person and whether its video is ready to upload.
|
|
151
|
+
function projectsIn(deps, from, to) {
|
|
152
|
+
const standings = stageStandingsByProject(deps.db);
|
|
153
|
+
const reviewing = awaitingReview(deps.db);
|
|
154
|
+
const uploads = uploadedProjects(deps.db);
|
|
155
|
+
const samples = sampleProjectIds(deps.db);
|
|
156
|
+
const waits = limitWaitsByProject(deps.db);
|
|
157
|
+
const fromSchedule = new Map();
|
|
158
|
+
for (const row of deps.db
|
|
159
|
+
.prepare(`SELECT DISTINCT admitted.value AS project_id, schedule_runs.schedule_id
|
|
160
|
+
FROM schedule_runs, json_each(schedule_runs.project_ids_json) AS admitted`)
|
|
161
|
+
.all()) {
|
|
162
|
+
const parsed = z.object({ project_id: z.string(), schedule_id: z.string() }).parse(row);
|
|
163
|
+
fromSchedule.set(parsed.project_id, parsed.schedule_id);
|
|
164
|
+
}
|
|
165
|
+
return deps.db
|
|
166
|
+
.prepare(`SELECT projects.id,projects.title,projects.config,projects.created_at,max(stages.finished_at) AS finished_at
|
|
167
|
+
FROM projects LEFT JOIN stages ON stages.project_id=projects.id
|
|
168
|
+
WHERE projects.created_at<? AND ${liveProject()} GROUP BY projects.id ORDER BY projects.created_at,projects.id`)
|
|
169
|
+
.all(to.toISOString())
|
|
170
|
+
.flatMap((row) => {
|
|
171
|
+
const project = projectRow.parse(row);
|
|
172
|
+
const state = derive(standings.get(project.id) ?? [], projectPaused(deps.db, project.id));
|
|
173
|
+
const terminal = state === "done" || state === "partial" || state === "failed" || state === "canceled";
|
|
174
|
+
if (terminal) {
|
|
175
|
+
const ended = project.finished_at ?? project.created_at;
|
|
176
|
+
if (ended < from.toISOString() || ended >= to.toISOString())
|
|
177
|
+
return [];
|
|
178
|
+
}
|
|
179
|
+
const needs = needsOf(state, reviewing.has(project.id));
|
|
180
|
+
const ready = (state === "done" || state === "partial") &&
|
|
181
|
+
makesVideo(project.config) &&
|
|
182
|
+
!uploads.has(project.id) &&
|
|
183
|
+
!samples.has(project.id);
|
|
184
|
+
const waiting = waits.get(project.id);
|
|
185
|
+
return [
|
|
186
|
+
{
|
|
187
|
+
id: project.id,
|
|
188
|
+
title: project.title,
|
|
189
|
+
state,
|
|
190
|
+
createdAt: project.created_at,
|
|
191
|
+
finishedAt: terminal ? (project.finished_at ?? project.created_at) : null,
|
|
192
|
+
scheduleId: fromSchedule.get(project.id) ?? null,
|
|
193
|
+
...(needs === undefined ? {} : { needs }),
|
|
194
|
+
...(ready ? { readyToUpload: true } : {}),
|
|
195
|
+
...(waiting === undefined ? {} : { limitWaits: waiting.map(listingWait) }),
|
|
196
|
+
},
|
|
197
|
+
];
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
function queued(deps) {
|
|
201
|
+
return queueEntries(deps.db).flatMap((entry) => {
|
|
202
|
+
if (entry.state === "finished")
|
|
203
|
+
return [];
|
|
204
|
+
const row = deps.db
|
|
205
|
+
.prepare(`SELECT projects.title, batches.created_at FROM projects, batches
|
|
206
|
+
WHERE projects.id=? AND batches.id=? AND ${liveProject()}`)
|
|
207
|
+
.get(entry.projectId, entry.batchId);
|
|
208
|
+
if (row === undefined)
|
|
209
|
+
return [];
|
|
210
|
+
const parsed = z.object({ title: z.string(), created_at: z.string() }).parse(row);
|
|
211
|
+
return [
|
|
212
|
+
{
|
|
213
|
+
projectId: entry.projectId,
|
|
214
|
+
title: parsed.title,
|
|
215
|
+
batchId: entry.batchId,
|
|
216
|
+
position: entry.position,
|
|
217
|
+
state: entry.state,
|
|
218
|
+
queuedAt: parsed.created_at,
|
|
219
|
+
},
|
|
220
|
+
];
|
|
221
|
+
});
|
|
222
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
1
2
|
import { z } from "zod";
|
|
2
3
|
import { transact } from "../../kernel/db/tx.js";
|
|
3
4
|
import { derive } from "../../kernel/runner/graph.js";
|
|
4
|
-
import { projectPaused, stagesOf } from "../admission/repo.js";
|
|
5
|
+
import { projectExists, projectPaused, stagesOf } from "../admission/repo.js";
|
|
5
6
|
import { readStartReceipt } from "../play-drafts/start-repo.js";
|
|
6
7
|
import { scheduleRunSchema, scheduleSummarySchema, } from "./schema.js";
|
|
7
8
|
const rowSchema = z.object({
|
|
@@ -23,30 +24,39 @@ const rowSchema = z.object({
|
|
|
23
24
|
created_at: z.string(),
|
|
24
25
|
updated_at: z.string(),
|
|
25
26
|
deleted_at: z.string().nullable(),
|
|
27
|
+
brief: z.string().nullable(),
|
|
28
|
+
topic_mode: z.string(),
|
|
29
|
+
topic_min: z.number(),
|
|
30
|
+
topic_llm_json: z.string().nullable(),
|
|
31
|
+
topics_generating_at: z.string().nullable(),
|
|
32
|
+
topics_generated_at: z.string().nullable(),
|
|
33
|
+
topics_failed_at: z.string().nullable(),
|
|
34
|
+
topics_error: z.string().nullable(),
|
|
35
|
+
held_topics: z.number(),
|
|
26
36
|
});
|
|
37
|
+
const scheduleColumns = `id,name,template_id,template_version,cadence_json,timezone,missed_policy,
|
|
38
|
+
overlap_policy,spend_limit_cents,items_json,topic_keyword,values_json,status,version,next_run_at,
|
|
39
|
+
created_at,updated_at,deleted_at,brief,topic_mode,topic_min,topic_llm_json,topics_generating_at,
|
|
40
|
+
topics_generated_at,topics_failed_at,topics_error,
|
|
41
|
+
(SELECT count(*) FROM schedule_topics AS held
|
|
42
|
+
WHERE held.schedule_id=schedules.id AND held.state='held') AS held_topics`;
|
|
27
43
|
export function scheduleById(db, id) {
|
|
28
44
|
const row = db
|
|
29
|
-
.prepare(`SELECT
|
|
30
|
-
overlap_policy,spend_limit_cents,items_json,topic_keyword,values_json,status,version,next_run_at,created_at,updated_at,
|
|
31
|
-
deleted_at
|
|
45
|
+
.prepare(`SELECT ${scheduleColumns}
|
|
32
46
|
FROM schedules WHERE id=?`)
|
|
33
47
|
.get(id);
|
|
34
48
|
return row === undefined ? undefined : parseSchedule(row);
|
|
35
49
|
}
|
|
36
50
|
export function scheduleRows(db) {
|
|
37
51
|
return db
|
|
38
|
-
.prepare(`SELECT
|
|
39
|
-
overlap_policy,spend_limit_cents,items_json,topic_keyword,values_json,status,version,next_run_at,created_at,updated_at,
|
|
40
|
-
deleted_at
|
|
52
|
+
.prepare(`SELECT ${scheduleColumns}
|
|
41
53
|
FROM schedules ORDER BY created_at DESC,id`)
|
|
42
54
|
.all()
|
|
43
55
|
.map(parseSchedule);
|
|
44
56
|
}
|
|
45
57
|
export function dueSchedules(db, now) {
|
|
46
58
|
return db
|
|
47
|
-
.prepare(`SELECT
|
|
48
|
-
overlap_policy,spend_limit_cents,items_json,topic_keyword,values_json,status,version,next_run_at,created_at,updated_at,
|
|
49
|
-
deleted_at
|
|
59
|
+
.prepare(`SELECT ${scheduleColumns}
|
|
50
60
|
FROM schedules WHERE deleted_at IS NULL AND status='active' AND next_run_at IS NOT NULL
|
|
51
61
|
AND next_run_at <= ?
|
|
52
62
|
ORDER BY next_run_at,id`)
|
|
@@ -57,17 +67,27 @@ export function insertSchedule(db, schedule, creationHash) {
|
|
|
57
67
|
db.prepare(`INSERT INTO schedules
|
|
58
68
|
(id,name,template_id,template_version,cadence_json,timezone,missed_policy,overlap_policy,
|
|
59
69
|
spend_limit_cents,items_json,topic_keyword,values_json,status,version,creation_hash,
|
|
60
|
-
next_run_at,created_at,updated_at,deleted_at)
|
|
61
|
-
VALUES (
|
|
70
|
+
next_run_at,created_at,updated_at,deleted_at,brief,topic_mode,topic_min,topic_llm_json)
|
|
71
|
+
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`).run(schedule.id, schedule.name, schedule.templateId, schedule.templateVersion, JSON.stringify(schedule.cadence), schedule.timezone, schedule.missedPolicy, schedule.overlapPolicy, schedule.spendLimitCents, JSON.stringify(schedule.items), schedule.topicKeyword, JSON.stringify(schedule.values), schedule.status, schedule.version, creationHash, schedule.nextRunAt, schedule.createdAt, schedule.updatedAt, schedule.deletedAt, ...generationColumns(schedule));
|
|
72
|
+
}
|
|
73
|
+
function generationColumns(schedule) {
|
|
74
|
+
const generation = schedule.topicGeneration;
|
|
75
|
+
return [
|
|
76
|
+
schedule.brief,
|
|
77
|
+
generation.mode,
|
|
78
|
+
generation.keepAtLeast,
|
|
79
|
+
generation.llm === null ? null : JSON.stringify(generation.llm),
|
|
80
|
+
];
|
|
62
81
|
}
|
|
63
82
|
export function updateScheduleRow(db, schedule, mutationId, mutationHash, baseVersion) {
|
|
64
83
|
const changed = db
|
|
65
84
|
.prepare(`UPDATE schedules SET name=?,template_id=?,template_version=?,cadence_json=?,timezone=?,
|
|
66
85
|
missed_policy=?,overlap_policy=?,spend_limit_cents=?,items_json=?,topic_keyword=?,
|
|
67
86
|
values_json=?,status=?,version=?,
|
|
68
|
-
next_run_at=?,updated_at=?,mutation_id=?,mutation_hash
|
|
87
|
+
next_run_at=?,updated_at=?,mutation_id=?,mutation_hash=?,
|
|
88
|
+
brief=?,topic_mode=?,topic_min=?,topic_llm_json=?,topics_failed_at=NULL,topics_error=NULL
|
|
69
89
|
WHERE id=? AND version=? AND deleted_at IS NULL`)
|
|
70
|
-
.run(schedule.name, schedule.templateId, schedule.templateVersion, JSON.stringify(schedule.cadence), schedule.timezone, schedule.missedPolicy, schedule.overlapPolicy, schedule.spendLimitCents, JSON.stringify(schedule.items), schedule.topicKeyword, JSON.stringify(schedule.values), schedule.status, schedule.version, schedule.nextRunAt, schedule.updatedAt, mutationId, mutationHash, schedule.id, baseVersion);
|
|
90
|
+
.run(schedule.name, schedule.templateId, schedule.templateVersion, JSON.stringify(schedule.cadence), schedule.timezone, schedule.missedPolicy, schedule.overlapPolicy, schedule.spendLimitCents, JSON.stringify(schedule.items), schedule.topicKeyword, JSON.stringify(schedule.values), schedule.status, schedule.version, schedule.nextRunAt, schedule.updatedAt, mutationId, mutationHash, ...generationColumns(schedule), schedule.id, baseVersion);
|
|
71
91
|
return Number(changed.changes) === 1;
|
|
72
92
|
}
|
|
73
93
|
export function tombstoneScheduleRow(db, id, version, deletedAt) {
|
|
@@ -138,7 +158,13 @@ function consumeTopic(db, scheduleId, used, updatedAt) {
|
|
|
138
158
|
if (at === -1)
|
|
139
159
|
return;
|
|
140
160
|
const items = schedule.items.filter((_item, index) => index !== at);
|
|
141
|
-
|
|
161
|
+
// A schedule that finds its own topics keeps going when its queue empties.
|
|
162
|
+
const done = items.length === 0 &&
|
|
163
|
+
schedule.topicGeneration.mode === "off" &&
|
|
164
|
+
(schedule.status === "active" || schedule.status === "paused");
|
|
165
|
+
// Remembered so topic generation never suggests it again.
|
|
166
|
+
db.prepare(`INSERT INTO schedule_topics (id,schedule_id,title,state,rank,created_at,decided_at)
|
|
167
|
+
VALUES (?,?,?,'used',0,?,?)`).run(randomUUID(), scheduleId, used.title, updatedAt, updatedAt);
|
|
142
168
|
db.prepare(`UPDATE schedules SET items_json=?,status=?,next_run_at=?,version=version+1,updated_at=?
|
|
143
169
|
WHERE id=? AND deleted_at IS NULL`).run(JSON.stringify(items), done ? "completed" : schedule.status, done ? null : schedule.nextRunAt, updatedAt, scheduleId);
|
|
144
170
|
}
|
|
@@ -217,10 +243,11 @@ export function settleTerminalScheduleRuns(db, settledAt) {
|
|
|
217
243
|
});
|
|
218
244
|
}
|
|
219
245
|
function projectIsActive(db, projectId) {
|
|
220
|
-
|
|
246
|
+
// A project in the trash takes no part in a schedule's overlap: it cannot run there.
|
|
247
|
+
if (!projectExists(db, projectId))
|
|
221
248
|
return false;
|
|
222
249
|
const state = derive(stagesOf(db, projectId), projectPaused(db, projectId));
|
|
223
|
-
return state !== "done" && state !== "failed" && state !== "canceled";
|
|
250
|
+
return state !== "done" && state !== "partial" && state !== "failed" && state !== "canceled";
|
|
224
251
|
}
|
|
225
252
|
export function recoverRunningRuns(db, endedAt, error = "Slopify was closed while this run was in progress. Check its video in Projects; the next run will go ahead as planned.") {
|
|
226
253
|
return transact(db, () => {
|
|
@@ -267,6 +294,19 @@ function parseSchedule(row) {
|
|
|
267
294
|
items: JSON.parse(value.items_json),
|
|
268
295
|
topicKeyword: value.topic_keyword,
|
|
269
296
|
values: JSON.parse(value.values_json),
|
|
297
|
+
brief: value.brief,
|
|
298
|
+
topicGeneration: {
|
|
299
|
+
mode: value.topic_mode,
|
|
300
|
+
keepAtLeast: value.topic_min,
|
|
301
|
+
llm: value.topic_llm_json === null ? null : JSON.parse(value.topic_llm_json),
|
|
302
|
+
},
|
|
303
|
+
topics: {
|
|
304
|
+
held: value.held_topics,
|
|
305
|
+
generatingSince: value.topics_generating_at,
|
|
306
|
+
generatedAt: value.topics_generated_at,
|
|
307
|
+
failedAt: value.topics_failed_at,
|
|
308
|
+
error: value.topics_error,
|
|
309
|
+
},
|
|
270
310
|
status: value.status,
|
|
271
311
|
version: value.version,
|
|
272
312
|
nextRunAt: value.next_run_at,
|
|
@@ -308,7 +348,7 @@ function parseRun(row) {
|
|
|
308
348
|
const internalRunError = "Slopify hit an internal error while starting this run. The next run will try again; if it keeps happening, use Download diagnostics in Settings and report it.";
|
|
309
349
|
const runErrors = {
|
|
310
350
|
"missing-template": "The template this schedule uses was deleted or changed. Edit the schedule and choose a template again.",
|
|
311
|
-
"unsupported-media": "This template uses audio, images, a thumbnail
|
|
351
|
+
"unsupported-media": "This template uses audio, images, a thumbnail, shorts background music or an ambient sound file you supplied, which scheduled runs cannot use. Pick a template that generates these instead, or on Play remove the music under Outputs → More shorts options (or pick Rain, Fireplace or Wind under Video and style → Ambient sound) and save the template again.",
|
|
312
352
|
"spend-limit": "Not started: the estimated cost was above this schedule's spend limit, or some prices were unknown. Raise the spend limit or choose models with known prices.",
|
|
313
353
|
readiness: "Not started: a provider was not ready, for example a missing API key, model or voice. Check Settings → Providers; the next run will try again.",
|
|
314
354
|
"not-found": "The template's settings are no longer valid, for example a prompt or voice it uses was deleted. Fix the template in Library > Templates and save it.",
|
|
@@ -1,21 +1,62 @@
|
|
|
1
1
|
import { transact } from "../../kernel/db/tx.js";
|
|
2
|
-
import { render } from "../admission/substitute.js";
|
|
3
2
|
import { reviewDraft } from "../play-drafts/review.js";
|
|
4
3
|
import { createDraft } from "../play-drafts/service.js";
|
|
5
4
|
import { startPlayDraft } from "../play-drafts/start.js";
|
|
6
5
|
import { freshTemplateDraft } from "../project-templates/setup.js";
|
|
7
6
|
import { nextOccurrence } from "./calendar.js";
|
|
8
|
-
import { activeRun, dueSchedules, insertRun, recordRunDispatch, recordRunStartIdentity, recoverRunningRuns, scheduleById, updateRun, } from "./repo.js";
|
|
7
|
+
import { activeRun, dueSchedules, insertRun, recordRunDispatch, recordRunStartIdentity, recoverRunningRuns, scheduleById, scheduleRows, updateRun, } from "./repo.js";
|
|
8
|
+
import { renderedTitle, scheduledValues } from "./topic-list.js";
|
|
9
|
+
import { generateTopics, generationDue, releaseTopicLeases } from "./topics.js";
|
|
9
10
|
const missedGraceMs = 60_000;
|
|
10
11
|
export function createScheduleRunner(deps) {
|
|
11
12
|
let ticking = false;
|
|
13
|
+
// ceiling: one entry per schedule generating now.
|
|
14
|
+
const generations = new Set();
|
|
15
|
+
const stopping = new AbortController();
|
|
16
|
+
const generated = async () => {
|
|
17
|
+
while (generations.size > 0)
|
|
18
|
+
await Promise.allSettled([...generations]);
|
|
19
|
+
};
|
|
20
|
+
// Topic generation can take minutes, so a tick starts it and moves on: another schedule's
|
|
21
|
+
// run is never late because of it. The lease inside `generateTopics` is what keeps it to
|
|
22
|
+
// one at a time per schedule.
|
|
23
|
+
const start = (scheduleId, force) => {
|
|
24
|
+
if (stopping.signal.aborted)
|
|
25
|
+
return;
|
|
26
|
+
const running = generateTopics(deps, scheduleId, { signal: stopping.signal, force })
|
|
27
|
+
.then((result) => {
|
|
28
|
+
if (!result.ok && result.reason === "failed")
|
|
29
|
+
deps.log.write("warn", "schedule.topics", { detail: result.message });
|
|
30
|
+
})
|
|
31
|
+
.catch(() => {
|
|
32
|
+
deps.log.write("error", "schedule.topics", {
|
|
33
|
+
detail: "Topic generation could not record its result.",
|
|
34
|
+
});
|
|
35
|
+
})
|
|
36
|
+
.finally(() => {
|
|
37
|
+
generations.delete(running);
|
|
38
|
+
});
|
|
39
|
+
generations.add(running);
|
|
40
|
+
};
|
|
41
|
+
const startGenerations = (now) => {
|
|
42
|
+
for (const schedule of scheduleRows(deps.db))
|
|
43
|
+
if (generationDue(schedule, now))
|
|
44
|
+
start(schedule.id, false);
|
|
45
|
+
};
|
|
12
46
|
return {
|
|
47
|
+
generated,
|
|
48
|
+
requestTopics: (scheduleId) => start(scheduleId, true),
|
|
49
|
+
stop: async () => {
|
|
50
|
+
stopping.abort();
|
|
51
|
+
await generated();
|
|
52
|
+
},
|
|
13
53
|
tick: async (now = deps.clock.now()) => {
|
|
14
54
|
if (ticking)
|
|
15
55
|
return;
|
|
16
56
|
ticking = true;
|
|
17
57
|
try {
|
|
18
58
|
recoverRunningRuns(deps.db, now.toISOString(), "The previous schedule tick could not record this run's result.");
|
|
59
|
+
startGenerations(now);
|
|
19
60
|
const due = dueSchedules(deps.db, now.toISOString());
|
|
20
61
|
const claimed = due.flatMap((schedule) => claim(deps, schedule, now));
|
|
21
62
|
const settled = await Promise.allSettled(claimed.map((entry) => execute(deps, entry)));
|
|
@@ -27,7 +68,10 @@ export function createScheduleRunner(deps) {
|
|
|
27
68
|
ticking = false;
|
|
28
69
|
}
|
|
29
70
|
},
|
|
30
|
-
recover: (now = deps.clock.now()) =>
|
|
71
|
+
recover: (now = deps.clock.now()) => {
|
|
72
|
+
releaseTopicLeases(deps);
|
|
73
|
+
return recoverRunningRuns(deps.db, now.toISOString());
|
|
74
|
+
},
|
|
31
75
|
};
|
|
32
76
|
}
|
|
33
77
|
function claim(deps, schedule, now) {
|
|
@@ -47,7 +91,11 @@ function claim(deps, schedule, now) {
|
|
|
47
91
|
current.status !== "active")
|
|
48
92
|
return [];
|
|
49
93
|
const overlapping = activeRun(deps.db, schedule.id, now.toISOString());
|
|
50
|
-
|
|
94
|
+
// A schedule that finds its own topics never runs the template without one.
|
|
95
|
+
const noTopic = current.topicGeneration.mode !== "off" && current.items.length === 0
|
|
96
|
+
? noTopicReason(current)
|
|
97
|
+
: null;
|
|
98
|
+
const skip = occurrence || overlapping || noTopic !== null;
|
|
51
99
|
const run = {
|
|
52
100
|
id: deps.uuid(),
|
|
53
101
|
scheduleId: schedule.id,
|
|
@@ -61,7 +109,9 @@ function claim(deps, schedule, now) {
|
|
|
61
109
|
error: skip
|
|
62
110
|
? overlapping
|
|
63
111
|
? "Skipped because the previous run of this schedule was still going."
|
|
64
|
-
:
|
|
112
|
+
: occurrence
|
|
113
|
+
? "Skipped because Slopify was not running at the scheduled time."
|
|
114
|
+
: noTopic
|
|
65
115
|
: null,
|
|
66
116
|
};
|
|
67
117
|
if (!insertRun(deps.db, run))
|
|
@@ -72,6 +122,14 @@ function claim(deps, schedule, now) {
|
|
|
72
122
|
return skip ? [] : [{ run, schedule }];
|
|
73
123
|
});
|
|
74
124
|
}
|
|
125
|
+
function noTopicReason(schedule) {
|
|
126
|
+
const { held, error } = schedule.topics;
|
|
127
|
+
if (held > 0)
|
|
128
|
+
return `Skipped because no topic was approved: ${String(held)} ${held === 1 ? "topic is" : "topics are"} waiting for you. Approve them under Schedules → ${schedule.name} → Topics waiting.`;
|
|
129
|
+
if (error !== null)
|
|
130
|
+
return `Skipped because the topic queue was empty. ${error}`;
|
|
131
|
+
return "Skipped because the topic queue was empty and new topics were still being generated. The next run uses the first one.";
|
|
132
|
+
}
|
|
75
133
|
async function execute(deps, claimed) {
|
|
76
134
|
let run = claimed.run;
|
|
77
135
|
try {
|
|
@@ -136,21 +194,14 @@ async function execute(deps, claimed) {
|
|
|
136
194
|
}
|
|
137
195
|
// One project per run. The first queued topic fills the chosen keyword, the schedule's fixed
|
|
138
196
|
// values fill the rest over the template's own, and the template's title is filled from the
|
|
139
|
-
// same keywords, so "
|
|
197
|
+
// same keywords, so "History: {{Topic}}" names the project after the topic. A topic saved
|
|
140
198
|
// before topics had a keyword brings its own title and values instead.
|
|
141
199
|
function runForm(form, schedule, topic) {
|
|
142
|
-
|
|
143
|
-
...form
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
...(topic !== undefined && schedule.topicKeyword !== null
|
|
147
|
-
? { [schedule.topicKeyword]: topic.title }
|
|
148
|
-
: {}),
|
|
200
|
+
return {
|
|
201
|
+
...form,
|
|
202
|
+
title: renderedTitle(form, schedule, topic),
|
|
203
|
+
values: scheduledValues(form, schedule, topic),
|
|
149
204
|
};
|
|
150
|
-
const title = topic !== undefined && schedule.topicKeyword === null
|
|
151
|
-
? topic.title
|
|
152
|
-
: render(form.title, values);
|
|
153
|
-
return { ...form, title, values };
|
|
154
205
|
}
|
|
155
206
|
class ScheduleDispatchError extends Error {
|
|
156
207
|
reason;
|