@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,212 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { zValidator } from "@hono/zod-validator";
|
|
3
|
+
import { Hono } from "hono";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { defaultSpeechVoice, detectSpeechCached, primaryLanguage, } from "../../kernel/ports/system-speech.js";
|
|
6
|
+
import { loudnessOfDefault } from "../../slices/loudness/model.js";
|
|
7
|
+
import { firstRunView } from "../../slices/onboarding/first-run.js";
|
|
8
|
+
import { fullVideoDraft } from "../../slices/onboarding/full-video.js";
|
|
9
|
+
import { installPack } from "../../slices/onboarding/install.js";
|
|
10
|
+
import { fullVideoInputSchema, quickShortInputSchema, sampleCopyInputSchema, } from "../../slices/onboarding/model.js";
|
|
11
|
+
import { packById, starterSet } from "../../slices/onboarding/packs.js";
|
|
12
|
+
import { planShortProviders, shortDraft, } from "../../slices/onboarding/quick-short.js";
|
|
13
|
+
import { copySample, restoreSamples, sampleProjectIds, } from "../../slices/onboarding/sample.js";
|
|
14
|
+
import { dismissFirstRun, recordShortRequest, shortRequestProject, } from "../../slices/onboarding/state.js";
|
|
15
|
+
import { systemVoiceProvider } from "../../slices/settings/model.js";
|
|
16
|
+
import { readSettings } from "../../slices/settings/playback.js";
|
|
17
|
+
import { providerStatuses } from "../../slices/settings/readiness.js";
|
|
18
|
+
import { listVoices } from "../../slices/settings/repo.js";
|
|
19
|
+
import { addVoice } from "../../slices/settings/voices.js";
|
|
20
|
+
import { projectTitle } from "../../slices/storage/repo.js";
|
|
21
|
+
import { onInvalid, problem, titleOf } from "./problem.js";
|
|
22
|
+
import { createProject } from "./project-create.js";
|
|
23
|
+
const packParam = z.object({ id: z.string().min(1).max(40) });
|
|
24
|
+
// The first five minutes: the first-run screen's state, starter packs, "Make a 60-second
|
|
25
|
+
// short" and the bundled samples' Restore and Make my own copy.
|
|
26
|
+
export function onboardingRoutes(deps) {
|
|
27
|
+
const statuses = () => providerStatuses({
|
|
28
|
+
db: deps.db,
|
|
29
|
+
probe: deps.probe,
|
|
30
|
+
hostCliStatus: deps.hostCliStatus,
|
|
31
|
+
host: deps.speechHost,
|
|
32
|
+
});
|
|
33
|
+
const speech = () => detectSpeechCached(deps.probe, deps.speechHost ?? process);
|
|
34
|
+
// The best speech program found and its English voice, for a short made without a voice key.
|
|
35
|
+
const systemVoicePick = async () => {
|
|
36
|
+
const engine = (await speech()).engines[0];
|
|
37
|
+
const voice = engine === undefined ? undefined : defaultSpeechVoice(engine);
|
|
38
|
+
return engine === undefined || voice === undefined
|
|
39
|
+
? undefined
|
|
40
|
+
: { model: engine.id, voiceId: voice.id, name: voice.name };
|
|
41
|
+
};
|
|
42
|
+
const templates = { ...deps, uuid: randomUUID };
|
|
43
|
+
const sample = {
|
|
44
|
+
db: deps.db,
|
|
45
|
+
paths: deps.paths,
|
|
46
|
+
clock: deps.clock,
|
|
47
|
+
ids: deps.ids,
|
|
48
|
+
log: deps.log,
|
|
49
|
+
appVersion: deps.version,
|
|
50
|
+
hasInflight: deps.runner.hasInflight,
|
|
51
|
+
...(deps.sampleArchives === undefined ? {} : { archives: deps.sampleArchives }),
|
|
52
|
+
};
|
|
53
|
+
return new Hono()
|
|
54
|
+
.get("/", async (c) => {
|
|
55
|
+
c.header("Cache-Control", "no-store");
|
|
56
|
+
return c.json(firstRunView(deps.db, await statuses()));
|
|
57
|
+
})
|
|
58
|
+
.post("/dismiss", (c) => {
|
|
59
|
+
dismissFirstRun(deps.db, deps.clock.now().toISOString());
|
|
60
|
+
return c.json({ dismissed: true });
|
|
61
|
+
})
|
|
62
|
+
.post("/packs/:id", zValidator("param", packParam, onInvalid), (c) => {
|
|
63
|
+
const installed = installPack(templates, c.req.valid("param").id);
|
|
64
|
+
if (!installed.ok)
|
|
65
|
+
return problem(c, {
|
|
66
|
+
status: 404,
|
|
67
|
+
title: titleOf(404),
|
|
68
|
+
detail: "Slopify doesn't have that starter pack. Reload the page and pick one of the packs listed.",
|
|
69
|
+
});
|
|
70
|
+
return c.json(installed.value);
|
|
71
|
+
})
|
|
72
|
+
.post("/short", zValidator("json", quickShortInputSchema, onInvalid), async (c) => {
|
|
73
|
+
const input = c.req.valid("json");
|
|
74
|
+
const earlier = shortRequestProject(deps.db, input.requestId);
|
|
75
|
+
if (earlier !== undefined && projectTitle(deps.db, earlier) !== undefined)
|
|
76
|
+
return c.json({ projectId: earlier, replayed: true });
|
|
77
|
+
const pack = input.packId === undefined ? starterSet : packById(input.packId);
|
|
78
|
+
if (pack === undefined)
|
|
79
|
+
return problem(c, {
|
|
80
|
+
status: 404,
|
|
81
|
+
title: titleOf(404),
|
|
82
|
+
detail: "Slopify doesn't have that starter pack. Reload the page and pick one of the packs listed.",
|
|
83
|
+
});
|
|
84
|
+
const modelsFor = deps.modelsFor;
|
|
85
|
+
if (modelsFor === undefined)
|
|
86
|
+
return problem(c, {
|
|
87
|
+
status: 503,
|
|
88
|
+
title: titleOf(503),
|
|
89
|
+
detail: "Slopify can't list the models on this machine right now. Restart Slopify and try again.",
|
|
90
|
+
});
|
|
91
|
+
const plan = await planShortProviders({
|
|
92
|
+
statuses: await statuses(),
|
|
93
|
+
voices: listVoices(deps.db),
|
|
94
|
+
modelsFor,
|
|
95
|
+
suggested: pack.voice,
|
|
96
|
+
systemVoice: await systemVoicePick(),
|
|
97
|
+
});
|
|
98
|
+
if (!plan.ok)
|
|
99
|
+
return problem(c, {
|
|
100
|
+
status: 409,
|
|
101
|
+
title: titleOf(409),
|
|
102
|
+
detail: plan.gaps.map((gap) => gap.message).join(" "),
|
|
103
|
+
extensions: { gaps: plan.gaps },
|
|
104
|
+
});
|
|
105
|
+
const installed = installPack(templates, pack.id, { template: false });
|
|
106
|
+
if (!installed.ok)
|
|
107
|
+
return problem(c, {
|
|
108
|
+
status: 404,
|
|
109
|
+
title: titleOf(404),
|
|
110
|
+
detail: "Slopify doesn't have that starter pack. Reload the page and pick one of the packs listed.",
|
|
111
|
+
});
|
|
112
|
+
// The computer's voice is saved like any other, so Play and a later rebuild offer it.
|
|
113
|
+
const audio = plan.providers.audio;
|
|
114
|
+
if (plan.systemVoice &&
|
|
115
|
+
!listVoices(deps.db).some((voice) => voice.provider === audio.provider && voice.voiceId === audio.voice)) {
|
|
116
|
+
const found = await speech();
|
|
117
|
+
const spoken = found.engines
|
|
118
|
+
.flatMap((engine) => engine.voices)
|
|
119
|
+
.find((voice) => voice.id === audio.voice);
|
|
120
|
+
const language = spoken === undefined ? undefined : primaryLanguage(spoken);
|
|
121
|
+
addVoice({ db: deps.db, ids: deps.ids }, {
|
|
122
|
+
provider: systemVoiceProvider,
|
|
123
|
+
name: `${spoken?.name ?? audio.voice} (computer voice)`,
|
|
124
|
+
voiceId: audio.voice,
|
|
125
|
+
...(language === undefined ? {} : { languages: [language] }),
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
const created = await createProject(deps, shortDraft({
|
|
129
|
+
topic: input.topic,
|
|
130
|
+
pack,
|
|
131
|
+
names: installed.value.prompts,
|
|
132
|
+
providers: plan.providers,
|
|
133
|
+
loudness: loudnessOfDefault(readSettings({ db: deps.db, log: deps.log }).loudness),
|
|
134
|
+
}));
|
|
135
|
+
if (!created.ok)
|
|
136
|
+
return problem(c, {
|
|
137
|
+
status: created.status,
|
|
138
|
+
title: titleOf(created.status),
|
|
139
|
+
detail: `${created.detail} ${created.fields.map((field) => field.message).join(" ")}`.trim(),
|
|
140
|
+
...(created.fields.length === 0 ? {} : { extensions: { fields: created.fields } }),
|
|
141
|
+
});
|
|
142
|
+
recordShortRequest(deps.db, input.requestId, created.project.id);
|
|
143
|
+
// A real project now exists, so the first-run screen is done with.
|
|
144
|
+
dismissFirstRun(deps.db, deps.clock.now().toISOString());
|
|
145
|
+
return c.json({ projectId: created.project.id, replayed: false }, 201);
|
|
146
|
+
})
|
|
147
|
+
.post("/full-video", zValidator("json", fullVideoInputSchema, onInvalid), (c) => {
|
|
148
|
+
const made = fullVideoDraft(templates, c.req.valid("json"));
|
|
149
|
+
if (made.ok)
|
|
150
|
+
return c.json(made.value, 201);
|
|
151
|
+
if (made.reason === "not-found")
|
|
152
|
+
return problem(c, {
|
|
153
|
+
status: 404,
|
|
154
|
+
title: titleOf(404),
|
|
155
|
+
detail: "This short or its starter pack is no longer here, so Play couldn't be set up from it. Open Play and pick a template yourself.",
|
|
156
|
+
});
|
|
157
|
+
if (made.reason === "not-short")
|
|
158
|
+
return problem(c, {
|
|
159
|
+
status: 409,
|
|
160
|
+
title: titleOf(409),
|
|
161
|
+
detail: "This project is already a long video. Open Play to start another one on a new topic.",
|
|
162
|
+
});
|
|
163
|
+
return problem(c, {
|
|
164
|
+
status: 409,
|
|
165
|
+
title: titleOf(409),
|
|
166
|
+
detail: "Slopify couldn't open a Play draft for the full video. Press Make the full video on this topic again; if it keeps failing, open Play and pick the pack's template in Library → Templates.",
|
|
167
|
+
extensions: { reason: made.reason },
|
|
168
|
+
});
|
|
169
|
+
})
|
|
170
|
+
.get("/sample", (c) => {
|
|
171
|
+
const samples = sampleProjectIds(deps.db);
|
|
172
|
+
return c.json({ projectId: samples.library, samples });
|
|
173
|
+
})
|
|
174
|
+
.post("/sample/restore", async (c) => {
|
|
175
|
+
const restored = await restoreSamples(sample);
|
|
176
|
+
if (!restored.ok)
|
|
177
|
+
return problem(c, {
|
|
178
|
+
status: restored.status,
|
|
179
|
+
title: titleOf(restored.status),
|
|
180
|
+
detail: restored.detail,
|
|
181
|
+
});
|
|
182
|
+
return c.json({ projectId: restored.samples.library, samples: restored.samples });
|
|
183
|
+
})
|
|
184
|
+
.post("/sample/copy", async (c) => {
|
|
185
|
+
// Which sample to copy is in the body; without one, the Library of Alexandria.
|
|
186
|
+
const body = await c.req.json().catch(() => ({}));
|
|
187
|
+
const input = sampleCopyInputSchema.safeParse(body ?? {});
|
|
188
|
+
if (!input.success)
|
|
189
|
+
return problem(c, {
|
|
190
|
+
status: 400,
|
|
191
|
+
title: titleOf(400),
|
|
192
|
+
detail: "Slopify couldn't tell which sample to copy. Reload the page and try again.",
|
|
193
|
+
});
|
|
194
|
+
const catalogue = deps.catalogue;
|
|
195
|
+
const id = input.data.projectId ?? sampleProjectIds(deps.db).library ?? undefined;
|
|
196
|
+
if (catalogue === undefined || id === undefined)
|
|
197
|
+
return problem(c, {
|
|
198
|
+
status: 404,
|
|
199
|
+
title: titleOf(404),
|
|
200
|
+
detail: "The sample project isn't here. Bring it back with Settings → Backup & storage → Restore samples, then press Make my own copy.",
|
|
201
|
+
});
|
|
202
|
+
const copied = copySample({ ...sample, catalogue: catalogue.read() }, id);
|
|
203
|
+
if (!copied.ok)
|
|
204
|
+
return problem(c, {
|
|
205
|
+
status: copied.status,
|
|
206
|
+
title: titleOf(copied.status),
|
|
207
|
+
detail: copied.detail,
|
|
208
|
+
});
|
|
209
|
+
deps.hub.emitGlobal({ type: "project.updated", projectId: copied.projectId });
|
|
210
|
+
return c.json({ projectId: copied.projectId }, 201);
|
|
211
|
+
});
|
|
212
|
+
}
|
|
@@ -41,7 +41,7 @@ export function openFolderRoutes(deps) {
|
|
|
41
41
|
return problem(c, {
|
|
42
42
|
status: 404,
|
|
43
43
|
title: titleOf(404),
|
|
44
|
-
detail: "This output's file was deleted or moved. Use
|
|
44
|
+
detail: "This output's file was deleted or moved. Use More → make it again in its section on the project page to make it again.",
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
47
|
return replyForFolder(c, deps, id, download.download.path);
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import { bundledPatchNotesDir, PatchNotesMissing, readPatchNote, } from "../../slices/patch-notes/library.js";
|
|
3
|
+
import { markPatchNotesSeen, readPatchNotes } from "../../slices/patch-notes/seen.js";
|
|
4
|
+
import { problem, titleOf } from "./problem.js";
|
|
5
|
+
// A build without its notes says so in plain words; anything else is the app's error handler's.
|
|
6
|
+
function missing(c, error) {
|
|
7
|
+
if (!(error instanceof PatchNotesMissing))
|
|
8
|
+
throw error;
|
|
9
|
+
return problem(c, { status: 500, title: titleOf(500), detail: error.message });
|
|
10
|
+
}
|
|
11
|
+
// Settings → Patch notes: the list (with which note is due to open by itself), one note's
|
|
12
|
+
// Markdown, and recording that the note that opened by itself was closed.
|
|
13
|
+
// Preserve Hono's inferred route types for the web client.
|
|
14
|
+
export function patchNotesRoutes(deps) {
|
|
15
|
+
const dir = deps.patchNotesDir ?? bundledPatchNotesDir();
|
|
16
|
+
return new Hono()
|
|
17
|
+
.get("/", async (c) => {
|
|
18
|
+
try {
|
|
19
|
+
return c.json(await readPatchNotes(deps.db, dir, deps.version));
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
return missing(c, error);
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
.post("/seen", (c) => {
|
|
26
|
+
markPatchNotesSeen(deps.db, deps.version);
|
|
27
|
+
return c.json({ seen: true });
|
|
28
|
+
})
|
|
29
|
+
.get("/:id", async (c) => {
|
|
30
|
+
let markdown;
|
|
31
|
+
try {
|
|
32
|
+
markdown = await readPatchNote(dir, c.req.param("id"));
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
return missing(c, error);
|
|
36
|
+
}
|
|
37
|
+
if (markdown === undefined)
|
|
38
|
+
return problem(c, {
|
|
39
|
+
status: 404,
|
|
40
|
+
title: titleOf(404),
|
|
41
|
+
detail: "These patch notes are not in this version of Slopify. Open Settings → Patch notes and pick one from the list.",
|
|
42
|
+
});
|
|
43
|
+
return c.body(markdown, 200, { "content-type": "text/markdown; charset=utf-8" });
|
|
44
|
+
});
|
|
45
|
+
}
|
|
@@ -5,6 +5,7 @@ import { modelFields } from "../../catalog/validate.js";
|
|
|
5
5
|
import { runDraftSchema } from "../../slices/admission/repo.js";
|
|
6
6
|
import { admit } from "../../slices/admission/rules.js";
|
|
7
7
|
import { batchExists, enqueueBatch, pumpQueue, queueEntries } from "../../slices/batch/index.js";
|
|
8
|
+
import { castVoicedRun } from "../../slices/channels/runs.js";
|
|
8
9
|
import { estimateRun } from "../../slices/estimate/index.js";
|
|
9
10
|
import { resolveFont } from "../../slices/fonts/index.js";
|
|
10
11
|
import { pickTemplates, renderPicked } from "../../slices/library/slots.js";
|
|
@@ -32,8 +33,9 @@ export function planningRoutes(deps) {
|
|
|
32
33
|
title: item.title,
|
|
33
34
|
values: { ...input.draft.values, ...item.values },
|
|
34
35
|
})) ?? [input.draft];
|
|
35
|
-
return drafts.map((
|
|
36
|
-
|
|
36
|
+
return drafts.map((sent, index) => {
|
|
37
|
+
// Cast speakers take the cast's voices as they are now, as a run started from Play does.
|
|
38
|
+
const picked = pickTemplates(deps.db, castVoicedRun(deps.db, sent));
|
|
37
39
|
const admitted = admit({
|
|
38
40
|
draft: picked.draft,
|
|
39
41
|
staged: stagedFiles(deps.db),
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { modelFields } from "../../catalog/validate.js";
|
|
2
|
+
import { stagesOf } from "../../slices/admission/repo.js";
|
|
3
|
+
import { admit } from "../../slices/admission/rules.js";
|
|
4
|
+
import { startRun } from "../../slices/admission/start.js";
|
|
5
|
+
import { castVoicedRun } from "../../slices/channels/runs.js";
|
|
6
|
+
import { resolveFont } from "../../slices/fonts/index.js";
|
|
7
|
+
import { pickTemplates, renderPicked } from "../../slices/library/slots.js";
|
|
8
|
+
import { stagedFiles } from "../../slices/storage/repo.js";
|
|
9
|
+
import { record } from "../../slices/telemetry/record.js";
|
|
10
|
+
// Starts a project from a whole draft, the way POST /api/projects always has: the prompt
|
|
11
|
+
// bodies are read now, the draft is admitted, the project and its first work are committed in
|
|
12
|
+
// one transaction, and only then is the runner woken. "Make a 60-second short" starts its
|
|
13
|
+
// project through the same door.
|
|
14
|
+
export async function createProject(deps, sent) {
|
|
15
|
+
// Cast speakers take the cast's voices as they are now, as a run started from Play does.
|
|
16
|
+
const draft = castVoicedRun(deps.db, sent);
|
|
17
|
+
if (draft.checkpoints?.length)
|
|
18
|
+
return {
|
|
19
|
+
ok: false,
|
|
20
|
+
status: 409,
|
|
21
|
+
detail: "This project has checkpoints turned on, so it must be started from Play with Review and start.",
|
|
22
|
+
fields: [],
|
|
23
|
+
};
|
|
24
|
+
const subtitles = draft.subtitles;
|
|
25
|
+
if (subtitles !== undefined && subtitles.mode !== "off") {
|
|
26
|
+
try {
|
|
27
|
+
await resolveFont(deps.paths, subtitles.fontId);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return {
|
|
31
|
+
ok: false,
|
|
32
|
+
status: 400,
|
|
33
|
+
detail: "The subtitle font you picked is no longer available. Choose another font before starting.",
|
|
34
|
+
fields: [
|
|
35
|
+
{
|
|
36
|
+
field: "subtitles.fontId",
|
|
37
|
+
message: "This font is no longer available. Choose another or upload it again in Settings.",
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// The bodies are read here, at the click, so an edit made since the prompt was selected is
|
|
44
|
+
// the one that runs.
|
|
45
|
+
const picked = pickTemplates(deps.db, draft);
|
|
46
|
+
const admitted = admit({
|
|
47
|
+
draft: picked.draft,
|
|
48
|
+
staged: stagedFiles(deps.db),
|
|
49
|
+
requiredSlots: picked.requiredSlots,
|
|
50
|
+
});
|
|
51
|
+
const modelErrors = modelFields(picked.draft, deps.catalogue);
|
|
52
|
+
if (!admitted.ok || picked.missing.length > 0 || modelErrors.length > 0)
|
|
53
|
+
return {
|
|
54
|
+
ok: false,
|
|
55
|
+
status: 400,
|
|
56
|
+
detail: "This video cannot start yet. Fix the highlighted fields, then try again.",
|
|
57
|
+
fields: [...picked.missing, ...modelErrors, ...(admitted.ok ? [] : admitted.fields)],
|
|
58
|
+
};
|
|
59
|
+
const storage = {
|
|
60
|
+
catalogue: deps.catalogue,
|
|
61
|
+
db: deps.db,
|
|
62
|
+
paths: deps.paths,
|
|
63
|
+
ids: deps.ids,
|
|
64
|
+
clock: deps.clock,
|
|
65
|
+
log: deps.log,
|
|
66
|
+
emit: (event) => {
|
|
67
|
+
deps.hub.emitGlobal(event);
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
// Rendered from the values admit() has trimmed, so the stored text carries no padding the
|
|
71
|
+
// user did not intend.
|
|
72
|
+
const { project } = startRun(storage, admitted.draft, renderPicked(picked, admitted.draft.values), false, Object.fromEntries(picked.bodies.map(({ key, body }) => [key, body])));
|
|
73
|
+
const telemetry = {
|
|
74
|
+
db: deps.db,
|
|
75
|
+
ids: deps.ids,
|
|
76
|
+
clock: deps.clock,
|
|
77
|
+
log: deps.log,
|
|
78
|
+
appVersion: deps.version,
|
|
79
|
+
};
|
|
80
|
+
// One event per project created. record() swallows its own failures, so a broken telemetry
|
|
81
|
+
// write cannot cost the user the run.
|
|
82
|
+
record(telemetry, "project.created", {});
|
|
83
|
+
deps.flushSoon();
|
|
84
|
+
// The run starts only once the project is committed.
|
|
85
|
+
deps.runner.tick(project.id);
|
|
86
|
+
return { ok: true, project, stages: stagesOf(deps.db, project.id) };
|
|
87
|
+
}
|
|
@@ -3,6 +3,7 @@ import { Hono } from "hono";
|
|
|
3
3
|
import { HTTPException } from "hono/http-exception";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { createTemplateFromProject, templateFromProjectSchema, } from "../../slices/project-templates/from-project.js";
|
|
6
|
+
import { makeNextChapter, nextChapterSchema } from "../../slices/project-templates/next-chapter.js";
|
|
6
7
|
import { templateCreateSchema, templateDeleteSchema, templateInstantiateSchema, templateUpdateSchema, } from "../../slices/project-templates/schema.js";
|
|
7
8
|
import { createTemplate, deleteTemplate, instantiateTemplate, listTemplates, readTemplate, updateTemplate, } from "../../slices/project-templates/service.js";
|
|
8
9
|
import { onInvalid, problem, titleOf } from "./problem.js";
|
|
@@ -37,7 +38,7 @@ function routes(deps) {
|
|
|
37
38
|
throw new HTTPException(404);
|
|
38
39
|
return deps;
|
|
39
40
|
};
|
|
40
|
-
return new Hono()
|
|
41
|
+
return (new Hono()
|
|
41
42
|
.get("/", (c) => c.json({ templates: listTemplates(service()) }))
|
|
42
43
|
.post("/from-project/:projectId", zValidator("param", templateFromProjectSchema.unwrap().pick({ projectId: true }), onInvalid), zValidator("json", templateFromProjectSchema.unwrap().omit({ projectId: true }), onInvalid), (c) => {
|
|
43
44
|
const result = createTemplateFromProject(service(), {
|
|
@@ -45,6 +46,22 @@ function routes(deps) {
|
|
|
45
46
|
projectId: c.req.valid("param").projectId,
|
|
46
47
|
});
|
|
47
48
|
return result.ok ? c.json(result.value, 201) : refused(c, result);
|
|
49
|
+
})
|
|
50
|
+
// A finished audiobook chapter's "Make the next chapter": a new Play draft for the book.
|
|
51
|
+
.post("/next-chapter/:projectId", zValidator("param", nextChapterSchema.unwrap().pick({ projectId: true }), onInvalid), zValidator("json", nextChapterSchema.unwrap().omit({ projectId: true }), onInvalid), (c) => {
|
|
52
|
+
const result = makeNextChapter(service(), {
|
|
53
|
+
...c.req.valid("json"),
|
|
54
|
+
projectId: c.req.valid("param").projectId,
|
|
55
|
+
});
|
|
56
|
+
if (result.ok)
|
|
57
|
+
return c.json(result.value, 201);
|
|
58
|
+
const status = result.reason === "not-found" ? 404 : result.reason === "conflict" ? 409 : 400;
|
|
59
|
+
return problem(c, {
|
|
60
|
+
status,
|
|
61
|
+
title: titleOf(status),
|
|
62
|
+
detail: result.message,
|
|
63
|
+
extensions: { reason: result.reason },
|
|
64
|
+
});
|
|
48
65
|
})
|
|
49
66
|
.post("/", zValidator("json", templateCreateSchema, onInvalid), (c) => {
|
|
50
67
|
const result = createTemplate(service(), c.req.valid("json"));
|
|
@@ -74,5 +91,5 @@ function routes(deps) {
|
|
|
74
91
|
templateId: c.req.valid("param").id,
|
|
75
92
|
});
|
|
76
93
|
return result.ok ? c.json(result.value, 201) : refused(c, result);
|
|
77
|
-
});
|
|
94
|
+
}));
|
|
78
95
|
}
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import { zValidator } from "@hono/zod-validator";
|
|
2
2
|
import { Hono } from "hono";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
-
import { modelFields } from "../../catalog/validate.js";
|
|
5
4
|
import { derive, progressOf } from "../../kernel/runner/graph.js";
|
|
6
|
-
import { listProjects, projectById, runDraftSchema, stageStandingsByProject, stagesOf, } from "../../slices/admission/repo.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
5
|
+
import { listProjects, projectById, projectTrashed, runDraftSchema, stageStandingsByProject, stagesOf, } from "../../slices/admission/repo.js";
|
|
6
|
+
import { defaultChannelId } from "../../slices/channels/model.js";
|
|
7
|
+
import { projectChannels } from "../../slices/channels/repo.js";
|
|
9
8
|
import { withProjectControl } from "../../slices/control/lock.js";
|
|
10
|
-
import {
|
|
11
|
-
import { pickTemplates, renderPicked } from "../../slices/library/slots.js";
|
|
9
|
+
import { stagesWithEta } from "../../slices/eta/view.js";
|
|
12
10
|
import { resumable } from "../../slices/rebuild/recovery-repo.js";
|
|
13
11
|
import { adoptBaseline } from "../../slices/revisions/adopt.js";
|
|
14
12
|
import { currentRevisionId } from "../../slices/revisions/repo.js";
|
|
15
|
-
import {
|
|
16
|
-
import { outputsOf
|
|
17
|
-
import {
|
|
13
|
+
import { limitWaitsByProject, listingWait } from "../../slices/run-cost/panel.js";
|
|
14
|
+
import { outputsOf } from "../../slices/storage/repo.js";
|
|
15
|
+
import { trashProject } from "../../slices/trash/service.js";
|
|
16
|
+
import { uploadedProjects } from "../../slices/uploads/repo.js";
|
|
18
17
|
import { onInvalid, problem, titleOf } from "./problem.js";
|
|
18
|
+
import { createProject } from "./project-create.js";
|
|
19
19
|
const idParam = z.object({
|
|
20
20
|
id: z
|
|
21
21
|
.string()
|
|
@@ -26,28 +26,11 @@ const idParam = z.object({
|
|
|
26
26
|
// The return type is inferred so Hono keeps the route types the SPA's client is
|
|
27
27
|
// generated from; see stagingRoutes.
|
|
28
28
|
export function projectRoutes(deps) {
|
|
29
|
-
const
|
|
30
|
-
catalogue: deps.catalogue,
|
|
29
|
+
const storageForTrash = {
|
|
31
30
|
db: deps.db,
|
|
32
31
|
paths: deps.paths,
|
|
33
|
-
ids: deps.ids,
|
|
34
32
|
clock: deps.clock,
|
|
35
33
|
log: deps.log,
|
|
36
|
-
emit: (event) => {
|
|
37
|
-
deps.hub.emitGlobal(event);
|
|
38
|
-
},
|
|
39
|
-
};
|
|
40
|
-
const telemetry = {
|
|
41
|
-
db: deps.db,
|
|
42
|
-
ids: deps.ids,
|
|
43
|
-
clock: deps.clock,
|
|
44
|
-
log: deps.log,
|
|
45
|
-
appVersion: deps.version,
|
|
46
|
-
};
|
|
47
|
-
const storageForDelete = {
|
|
48
|
-
db: deps.db,
|
|
49
|
-
paths: deps.paths,
|
|
50
|
-
log: deps.log,
|
|
51
34
|
hasInflight: deps.runner.hasInflight,
|
|
52
35
|
};
|
|
53
36
|
const summarise = (project) => ({
|
|
@@ -56,76 +39,36 @@ export function projectRoutes(deps) {
|
|
|
56
39
|
});
|
|
57
40
|
return (new Hono()
|
|
58
41
|
.post("/", zValidator("json", runDraftSchema, onInvalid), async (c) => {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
status: 409,
|
|
62
|
-
title: titleOf(409),
|
|
63
|
-
detail: "This project has checkpoints turned on, so it must be started from Play with Review and start.",
|
|
64
|
-
});
|
|
65
|
-
const requestedSubtitles = c.req.valid("json").subtitles;
|
|
66
|
-
if (requestedSubtitles !== undefined && requestedSubtitles.mode !== "off") {
|
|
67
|
-
try {
|
|
68
|
-
await resolveFont(deps.paths, requestedSubtitles.fontId);
|
|
69
|
-
}
|
|
70
|
-
catch {
|
|
71
|
-
return problem(c, {
|
|
72
|
-
status: 400,
|
|
73
|
-
title: titleOf(400),
|
|
74
|
-
detail: "The subtitle font you picked is no longer available. Choose another font before starting.",
|
|
75
|
-
extensions: {
|
|
76
|
-
fields: [
|
|
77
|
-
{
|
|
78
|
-
field: "subtitles.fontId",
|
|
79
|
-
message: "This font is no longer available. Choose another or upload it again in Settings.",
|
|
80
|
-
},
|
|
81
|
-
],
|
|
82
|
-
},
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
// The bodies are read here, at the click, so an edit made since the
|
|
87
|
-
// prompt was selected is the one that runs.
|
|
88
|
-
const picked = pickTemplates(deps.db, c.req.valid("json"));
|
|
89
|
-
const admitted = admit({
|
|
90
|
-
draft: picked.draft,
|
|
91
|
-
staged: stagedFiles(deps.db),
|
|
92
|
-
requiredSlots: picked.requiredSlots,
|
|
93
|
-
});
|
|
94
|
-
const modelErrors = modelFields(picked.draft, deps.catalogue);
|
|
95
|
-
if (!admitted.ok || picked.missing.length > 0 || modelErrors.length > 0) {
|
|
42
|
+
const created = await createProject(deps, c.req.valid("json"));
|
|
43
|
+
if (!created.ok)
|
|
96
44
|
return problem(c, {
|
|
97
|
-
status:
|
|
98
|
-
title: titleOf(
|
|
99
|
-
detail:
|
|
100
|
-
extensions: {
|
|
101
|
-
fields: [...picked.missing, ...modelErrors, ...(admitted.ok ? [] : admitted.fields)],
|
|
102
|
-
},
|
|
45
|
+
status: created.status,
|
|
46
|
+
title: titleOf(created.status),
|
|
47
|
+
detail: created.detail,
|
|
48
|
+
...(created.fields.length === 0 ? {} : { extensions: { fields: created.fields } }),
|
|
103
49
|
});
|
|
104
|
-
}
|
|
105
|
-
// Rendered from the values admit() has trimmed, so the stored text carries no
|
|
106
|
-
// padding the user did not intend.
|
|
107
|
-
const { project } = startRun(storage, admitted.draft, renderPicked(picked, admitted.draft.values), false, Object.fromEntries(picked.bodies.map(({ key, body }) => [key, body])));
|
|
108
|
-
// One event per project created. record() swallows its own
|
|
109
|
-
// failures, so a broken telemetry write cannot cost the user the run.
|
|
110
|
-
record(telemetry, "project.created", {});
|
|
111
|
-
deps.flushSoon();
|
|
112
|
-
// The run starts only once the project is committed.
|
|
113
|
-
deps.runner.tick(project.id);
|
|
114
50
|
// Read back after the tick, not from the rows startRun built: the runner has
|
|
115
51
|
// already claimed every eligible stage, and a body that paired status "running"
|
|
116
52
|
// with "video: pending" would contradict itself.
|
|
117
|
-
return c.json({ project: summarise(project), stages: stagesOf(deps.db, project.id) }, 201);
|
|
53
|
+
return c.json({ project: summarise(created.project), stages: stagesOf(deps.db, created.project.id) }, 201);
|
|
118
54
|
})
|
|
119
55
|
// One statement for every project's stage standings, not one per row: the list needs
|
|
120
56
|
// a status word and a meter, and both come out of the same five columns.
|
|
121
57
|
.get("/", (c) => {
|
|
122
58
|
const standings = stageStandingsByProject(deps.db);
|
|
59
|
+
const channels = projectChannels(deps.db);
|
|
60
|
+
const uploads = uploadedProjects(deps.db);
|
|
61
|
+
const waits = limitWaitsByProject(deps.db);
|
|
123
62
|
const projects = listProjects(deps.db).map((project) => {
|
|
124
63
|
const stages = standings.get(project.id) ?? [];
|
|
64
|
+
const waiting = waits.get(project.id);
|
|
125
65
|
return {
|
|
126
66
|
...project,
|
|
127
67
|
status: derive(stages, project.paused),
|
|
128
68
|
progress: progressOf(stages),
|
|
69
|
+
channelId: channels.get(project.id) ?? defaultChannelId,
|
|
70
|
+
uploadedAt: uploads.get(project.id) ?? null,
|
|
71
|
+
...(waiting === undefined ? {} : { limitWaits: waiting.map(listingWait) }),
|
|
129
72
|
};
|
|
130
73
|
});
|
|
131
74
|
return c.json({ projects });
|
|
@@ -136,7 +79,9 @@ export function projectRoutes(deps) {
|
|
|
136
79
|
return problem(c, {
|
|
137
80
|
status: 404,
|
|
138
81
|
title: titleOf(404),
|
|
139
|
-
detail:
|
|
82
|
+
detail: projectTrashed(deps.db, c.req.valid("param").id)
|
|
83
|
+
? "This project is in the trash. Restore it in Settings → Trash to open it again."
|
|
84
|
+
: "This project no longer exists. Go back to Projects to pick another.",
|
|
140
85
|
});
|
|
141
86
|
}
|
|
142
87
|
if (deps.catalogue !== undefined)
|
|
@@ -145,34 +90,26 @@ export function projectRoutes(deps) {
|
|
|
145
90
|
revisionId: currentRevisionId(deps.db, project.id) ?? null,
|
|
146
91
|
resumable: resumable(deps.db, project.id),
|
|
147
92
|
project: summarise(project),
|
|
148
|
-
stages: stagesOf(deps.db, project.id),
|
|
93
|
+
stages: stagesWithEta(deps.db, stagesOf(deps.db, project.id), project.config, deps.clock.now()),
|
|
149
94
|
outputs: outputsOf(deps.db, project.id),
|
|
150
95
|
});
|
|
151
96
|
})
|
|
152
|
-
//
|
|
153
|
-
//
|
|
97
|
+
// Moves the project to the trash (Settings → Trash) for 30 days; removing it for good is
|
|
98
|
+
// Delete now there, or the daily purge (`slices/trash`).
|
|
154
99
|
.delete("/:id", zValidator("param", idParam, onInvalid), (c) => {
|
|
155
100
|
const id = c.req.valid("param").id;
|
|
156
101
|
return withProjectControl(deps.db, id, () => {
|
|
157
|
-
const result =
|
|
102
|
+
const result = trashProject(storageForTrash, id);
|
|
158
103
|
if (result.ok)
|
|
159
104
|
return c.body(null, 204);
|
|
105
|
+
const status = result.reason === "running" ? 409 : 404;
|
|
160
106
|
return problem(c, {
|
|
161
|
-
status
|
|
162
|
-
title: titleOf(
|
|
163
|
-
detail: result.
|
|
107
|
+
status,
|
|
108
|
+
title: titleOf(status),
|
|
109
|
+
detail: result.reason === "running"
|
|
110
|
+
? "This project is still running. Use Cancel run on the project page first, then delete it."
|
|
111
|
+
: "This project no longer exists. Go back to Projects to pick another.",
|
|
164
112
|
});
|
|
165
113
|
});
|
|
166
114
|
}));
|
|
167
115
|
}
|
|
168
|
-
const deleteStatus = {
|
|
169
|
-
"no-project": 404,
|
|
170
|
-
running: 409,
|
|
171
|
-
files: 500,
|
|
172
|
-
};
|
|
173
|
-
const deleteDetails = {
|
|
174
|
-
"no-project": "This project no longer exists. Go back to Projects to pick another.",
|
|
175
|
-
// The run has to be stopped before its files can go.
|
|
176
|
-
running: "This project is still running. Use Cancel run on the project page first, then delete it.",
|
|
177
|
-
files: "Some of this project's files could not be removed. Close any program using files in the project folder, then try deleting again.",
|
|
178
|
-
};
|