@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
|
@@ -2,7 +2,10 @@ import { isLocalCliProvider } from "./model.js";
|
|
|
2
2
|
// These marketplaces host models with different input schemas. Discovery alone cannot
|
|
3
3
|
// make an arbitrary endpoint compatible with our image request.
|
|
4
4
|
export function allowsCustomModel(provider) {
|
|
5
|
-
return provider !== "fal" &&
|
|
5
|
+
return (provider !== "fal" &&
|
|
6
|
+
provider !== "replicate" &&
|
|
7
|
+
provider !== "codex-image" &&
|
|
8
|
+
provider !== "system-voice");
|
|
6
9
|
}
|
|
7
10
|
const ttlMs = 5 * 60_000;
|
|
8
11
|
const retryMs = 30_000;
|
|
@@ -86,6 +89,7 @@ function catalogNotice(provider) {
|
|
|
86
89
|
gemini: "Models come from your installed Gemini CLI, not an account access check. Missing a new model? Update Gemini CLI, rerun the Docker launcher if applicable, then refresh this list.",
|
|
87
90
|
"claude-code": "Models come from Claude Code. Missing a new model? Update Claude Code, rerun the Docker launcher if applicable, then refresh this list. You can also enter an exact model ID.",
|
|
88
91
|
inworld: "These are Inworld’s documented TTS models. You can also enter a compatible model ID.",
|
|
92
|
+
"system-voice": "These are the speech programs found on this computer, best first. Install another (for example espeak-ng) and refresh to see it.",
|
|
89
93
|
cartesia: "Cartesia has no model-list API. These are bundled compatible choices; you can enter a newer model ID.",
|
|
90
94
|
fal: "These models have input formats supported by Slopify's fal adapter.",
|
|
91
95
|
replicate: "These models have input formats supported by Slopify's Replicate adapter.",
|
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { silenceGapSecondsMax } from "../admission/rules.js";
|
|
3
|
+
import { loudnessFields } from "../loudness/model.js";
|
|
3
4
|
import { appearances, defaultSettings } from "./model.js";
|
|
4
5
|
import { readSetting, writeSetting } from "./repo.js";
|
|
5
6
|
// The `settings` table is key/value, so each setting is its own row and a value the app
|
|
6
7
|
// does not understand costs only that one setting.
|
|
7
8
|
const silenceGapKey = "silenceGapSeconds";
|
|
8
9
|
const appearanceKey = "appearance";
|
|
10
|
+
const loudnessKey = "loudness";
|
|
9
11
|
const silenceGapValue = z.number().int().min(0).max(silenceGapSecondsMax);
|
|
10
12
|
const appearanceValue = z.enum(appearances);
|
|
13
|
+
const loudnessValue = z.object({
|
|
14
|
+
enabled: z.boolean(),
|
|
15
|
+
videoLufs: z.number(),
|
|
16
|
+
audioFilesLufs: z.number(),
|
|
17
|
+
});
|
|
11
18
|
export function readSettings(deps) {
|
|
12
19
|
return {
|
|
13
20
|
silenceGapSeconds: parsed(deps, silenceGapKey, silenceGapValue, defaultSettings.silenceGapSeconds),
|
|
14
21
|
appearance: parsed(deps, appearanceKey, appearanceValue, defaultSettings.appearance),
|
|
22
|
+
loudness: parsed(deps, loudnessKey, loudnessValue, defaultSettings.loudness),
|
|
15
23
|
};
|
|
16
24
|
}
|
|
17
25
|
export function saveSettings(deps, settings) {
|
|
@@ -25,11 +33,18 @@ export function saveSettings(deps, settings) {
|
|
|
25
33
|
if (!appearanceValue.safeParse(settings.appearance).success) {
|
|
26
34
|
fields.push({ field: "appearance", message: "Choose System, Light or Dark." });
|
|
27
35
|
}
|
|
36
|
+
if (!loudnessValue.safeParse(settings.loudness).success) {
|
|
37
|
+
fields.push({ field: "loudness", message: "Choose whether new runs level the volume." });
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
fields.push(...loudnessFields(settings.loudness));
|
|
41
|
+
}
|
|
28
42
|
if (fields.length > 0) {
|
|
29
43
|
return { ok: false, fields };
|
|
30
44
|
}
|
|
31
45
|
writeSetting(deps.db, silenceGapKey, JSON.stringify(settings.silenceGapSeconds));
|
|
32
46
|
writeSetting(deps.db, appearanceKey, JSON.stringify(settings.appearance));
|
|
47
|
+
writeSetting(deps.db, loudnessKey, JSON.stringify(settings.loudness));
|
|
33
48
|
return { ok: true, settings };
|
|
34
49
|
}
|
|
35
50
|
// A stored value is JSON, and a hand-edited database is the only way it can be anything
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { detectSpeechCached } from "../../kernel/ports/system-speech.js";
|
|
1
2
|
import { cliPathStatus } from "./cli-paths.js";
|
|
2
3
|
import { cliProbeTimeoutMs, readinessFromProbe } from "./cli-status.js";
|
|
3
|
-
import { isLocalCliProvider, providers } from "./model.js";
|
|
4
|
+
import { isLocalCliProvider, providers, sharedKeyOf } from "./model.js";
|
|
4
5
|
import { keyedProviders } from "./repo.js";
|
|
5
6
|
// Every supported provider is listed, keyed or not, found or not, so Play can grey one out with
|
|
6
7
|
// a reason instead of hiding it. A keyed provider is ready when a key is stored; a CLI provider
|
|
@@ -10,6 +11,16 @@ export async function providerStatuses(deps) {
|
|
|
10
11
|
const probes = new Map();
|
|
11
12
|
return await Promise.all(providers.map(async (provider) => {
|
|
12
13
|
const base = { id: provider.id, family: provider.family, displayName: provider.displayName };
|
|
14
|
+
if (provider.auth === "local") {
|
|
15
|
+
const found = await detectSpeechCached(deps.probe, deps.host ?? process);
|
|
16
|
+
const engine = found.engines[0];
|
|
17
|
+
return {
|
|
18
|
+
...base,
|
|
19
|
+
readiness: engine === undefined
|
|
20
|
+
? { kind: "local", available: false, ...(found.issue ? { issue: found.issue } : {}) }
|
|
21
|
+
: { kind: "local", available: true, engine: engine.name },
|
|
22
|
+
};
|
|
23
|
+
}
|
|
13
24
|
if (provider.auth !== "cli")
|
|
14
25
|
return { ...base, readiness: keyedReadiness(keyed, provider.id) };
|
|
15
26
|
if (deps.hostCliStatus && isLocalCliProvider(provider.id)) {
|
|
@@ -41,5 +52,6 @@ export async function providerStatuses(deps) {
|
|
|
41
52
|
}));
|
|
42
53
|
}
|
|
43
54
|
function keyedReadiness(keyed, id) {
|
|
44
|
-
|
|
55
|
+
const shared = sharedKeyOf[id];
|
|
56
|
+
return { kind: "keyed", hasKey: keyed.has(id) || (shared !== undefined && keyed.has(shared)) };
|
|
45
57
|
}
|
|
@@ -10,7 +10,9 @@ const voiceRow = z.object({
|
|
|
10
10
|
provider: z.enum(providerIds),
|
|
11
11
|
name: z.string(),
|
|
12
12
|
voice_id: z.string(),
|
|
13
|
+
languages_json: z.string().nullable(),
|
|
13
14
|
});
|
|
15
|
+
const languagesJson = z.array(z.string());
|
|
14
16
|
const settingRow = z.object({ value: z.string() });
|
|
15
17
|
export function upsertKey(db, provider, key, updatedAt) {
|
|
16
18
|
db.prepare("INSERT INTO provider_keys (provider,key,updated_at,credential_generation) VALUES (?,?,?,?)" +
|
|
@@ -37,11 +39,16 @@ export function hasKey(db, provider) {
|
|
|
37
39
|
return db.prepare("SELECT 1 FROM provider_keys WHERE provider = ?").get(provider) !== undefined;
|
|
38
40
|
}
|
|
39
41
|
export function insertVoice(db, voice) {
|
|
40
|
-
db.prepare("INSERT INTO voices (id, provider, name, voice_id) VALUES (?, ?, ?, ?)").run(voice.id, voice.provider, voice.name, voice.voiceId);
|
|
42
|
+
db.prepare("INSERT INTO voices (id, provider, name, voice_id, languages_json) VALUES (?, ?, ?, ?, ?)").run(voice.id, voice.provider, voice.name, voice.voiceId, voice.languages === undefined ? null : JSON.stringify(voice.languages));
|
|
43
|
+
}
|
|
44
|
+
export function updateVoiceLanguages(db, id, languages) {
|
|
45
|
+
return (Number(db
|
|
46
|
+
.prepare("UPDATE voices SET languages_json = ? WHERE id = ?")
|
|
47
|
+
.run(languages === undefined ? null : JSON.stringify(languages), id).changes) > 0);
|
|
41
48
|
}
|
|
42
49
|
export function listVoices(db) {
|
|
43
50
|
return db
|
|
44
|
-
.prepare("SELECT id, provider, name, voice_id FROM voices ORDER BY provider, name, voice_id")
|
|
51
|
+
.prepare("SELECT id, provider, name, voice_id, languages_json FROM voices ORDER BY provider, name, voice_id")
|
|
45
52
|
.all()
|
|
46
53
|
.map((row) => toVoice(voiceRow.parse(row)));
|
|
47
54
|
}
|
|
@@ -57,5 +64,12 @@ export function writeSetting(db, key, value) {
|
|
|
57
64
|
" ON CONFLICT(key) DO UPDATE SET value = excluded.value").run(key, value);
|
|
58
65
|
}
|
|
59
66
|
function toVoice(row) {
|
|
60
|
-
|
|
67
|
+
const languages = row.languages_json === null ? undefined : languagesJson.parse(JSON.parse(row.languages_json));
|
|
68
|
+
return {
|
|
69
|
+
id: row.id,
|
|
70
|
+
provider: row.provider,
|
|
71
|
+
name: row.name,
|
|
72
|
+
voiceId: row.voice_id,
|
|
73
|
+
...(languages === undefined ? {} : { languages }),
|
|
74
|
+
};
|
|
61
75
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isUniqueConstraint } from "../../kernel/db/index.js";
|
|
2
2
|
import { providerById } from "./model.js";
|
|
3
|
-
import { deleteVoice, insertVoice, listVoices } from "./repo.js";
|
|
3
|
+
import { deleteVoice, insertVoice, listVoices, readSetting, updateVoiceLanguages, writeSetting, } from "./repo.js";
|
|
4
4
|
export const voiceNameMax = 200;
|
|
5
5
|
export const voiceIdMax = 200;
|
|
6
6
|
// A non-empty name and a non-empty voice ID, the ID unique within its provider, names free to
|
|
@@ -24,7 +24,16 @@ export function addVoice(deps, draft) {
|
|
|
24
24
|
if (voiceId.length > voiceIdMax) {
|
|
25
25
|
return { ok: false, reason: "voice-id-too-long" };
|
|
26
26
|
}
|
|
27
|
-
const
|
|
27
|
+
const languages = voiceLanguagesOf(draft.languages);
|
|
28
|
+
if (languages === null)
|
|
29
|
+
return { ok: false, reason: "unknown-language" };
|
|
30
|
+
const voice = {
|
|
31
|
+
id: deps.ids.next(),
|
|
32
|
+
provider: draft.provider,
|
|
33
|
+
name,
|
|
34
|
+
voiceId,
|
|
35
|
+
...(languages === undefined ? {} : { languages }),
|
|
36
|
+
};
|
|
28
37
|
try {
|
|
29
38
|
insertVoice(deps.db, voice);
|
|
30
39
|
}
|
|
@@ -39,9 +48,60 @@ export function addVoice(deps, draft) {
|
|
|
39
48
|
}
|
|
40
49
|
return { ok: true, voice };
|
|
41
50
|
}
|
|
51
|
+
// Lower-case primary codes, each once; an empty list is unknown. Null when one is not a
|
|
52
|
+
// language code at all.
|
|
53
|
+
export function voiceLanguagesOf(raw) {
|
|
54
|
+
if (raw === undefined)
|
|
55
|
+
return undefined;
|
|
56
|
+
const codes = raw.map((one) => one.trim().toLowerCase()).filter((one) => one !== "");
|
|
57
|
+
if (codes.some((one) => !/^[a-z]{2,3}$/.test(one)))
|
|
58
|
+
return null;
|
|
59
|
+
return codes.length === 0 ? undefined : [...new Set(codes)];
|
|
60
|
+
}
|
|
61
|
+
export function setVoiceLanguages(deps, id, raw) {
|
|
62
|
+
const languages = voiceLanguagesOf(raw);
|
|
63
|
+
if (languages === null)
|
|
64
|
+
return { ok: false, reason: "unknown-language" };
|
|
65
|
+
return updateVoiceLanguages(deps.db, id, languages)
|
|
66
|
+
? { ok: true }
|
|
67
|
+
: { ok: false, reason: "missing" };
|
|
68
|
+
}
|
|
42
69
|
export function removeVoice(deps, id) {
|
|
43
|
-
|
|
70
|
+
if (!deleteVoice(deps.db, id))
|
|
71
|
+
return { ok: false };
|
|
72
|
+
writeRealPersonVoices(deps.db, [...realPersonVoiceIds(deps.db)].filter((one) => one !== id));
|
|
73
|
+
return { ok: true };
|
|
44
74
|
}
|
|
45
75
|
export function voices(deps) {
|
|
46
|
-
|
|
76
|
+
const flagged = realPersonVoiceIds(deps.db);
|
|
77
|
+
return listVoices(deps.db).map((voice) => flagged.has(voice.id) ? { ...voice, imitatesRealPerson: true } : voice);
|
|
78
|
+
}
|
|
79
|
+
// Which saved voices imitate a real person: one row of the key/value `settings` table holding
|
|
80
|
+
// their ids, so the flag needs no column of its own and travels with a backup.
|
|
81
|
+
export const realPersonVoicesKey = "voices.realPerson";
|
|
82
|
+
export function realPersonVoiceIds(db) {
|
|
83
|
+
const stored = readSetting(db, realPersonVoicesKey);
|
|
84
|
+
if (stored === undefined)
|
|
85
|
+
return new Set();
|
|
86
|
+
try {
|
|
87
|
+
const value = JSON.parse(stored);
|
|
88
|
+
return new Set(Array.isArray(value) ? value.filter((one) => typeof one === "string") : []);
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return new Set();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function writeRealPersonVoices(db, ids) {
|
|
95
|
+
if (ids.length === 0)
|
|
96
|
+
db.prepare("DELETE FROM settings WHERE key = ?").run(realPersonVoicesKey);
|
|
97
|
+
else
|
|
98
|
+
writeSetting(db, realPersonVoicesKey, JSON.stringify([...new Set(ids)].sort()));
|
|
99
|
+
}
|
|
100
|
+
// Settings → Voices' "Imitates a real person" tick.
|
|
101
|
+
export function setVoiceImitatesRealPerson(deps, id, on) {
|
|
102
|
+
if (!listVoices(deps.db).some((voice) => voice.id === id))
|
|
103
|
+
return { ok: false, reason: "missing" };
|
|
104
|
+
const others = [...realPersonVoiceIds(deps.db)].filter((one) => one !== id);
|
|
105
|
+
writeRealPersonVoices(deps.db, on ? [...others, id] : others);
|
|
106
|
+
return { ok: true };
|
|
47
107
|
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { transact } from "../../kernel/db/tx.js";
|
|
2
|
+
import { machineOf } from "../telemetry/repo.js";
|
|
3
|
+
import { readSetting, writeSetting } from "./repo.js";
|
|
4
|
+
// "What's new" is a short tour shown once per major version, on the first launch after an
|
|
5
|
+
// update. The newest major this install has been shown (or has no need to be shown) lives in
|
|
6
|
+
// one settings key, so it is once per install rather than once per browser.
|
|
7
|
+
export const whatsNewSeenKey = "whats-new.seen-major";
|
|
8
|
+
export function majorOf(version) {
|
|
9
|
+
const match = /^(\d+)\.\d+\.\d+/.exec(version);
|
|
10
|
+
return match === null ? undefined : Number(match[1]);
|
|
11
|
+
}
|
|
12
|
+
// The newest major already shown; undefined when none was recorded, null when the stored
|
|
13
|
+
// value is damaged.
|
|
14
|
+
function seenMajor(db) {
|
|
15
|
+
const raw = readSetting(db, whatsNewSeenKey);
|
|
16
|
+
if (raw === undefined)
|
|
17
|
+
return undefined;
|
|
18
|
+
let value;
|
|
19
|
+
try {
|
|
20
|
+
value = JSON.parse(raw);
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
if (!(error instanceof SyntaxError))
|
|
24
|
+
throw error;
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : null;
|
|
28
|
+
}
|
|
29
|
+
// Telling an update from a fresh install: the machine row is written once, when the
|
|
30
|
+
// first-run notice is dismissed, and keeps the version that was running then. A fresh 3.x
|
|
31
|
+
// install either has no machine yet (the notice is still up) or one stamped 3.x, so it is not
|
|
32
|
+
// shown a tour of what changed since 2.x. An install that has been running since 2.x has a
|
|
33
|
+
// machine stamped 2.x. Once the tour is shown and closed, the key above takes over, so a
|
|
34
|
+
// later 4.0 compares against 3 rather than against the machine's first version.
|
|
35
|
+
export function readWhatsNew(db, version) {
|
|
36
|
+
const major = majorOf(version);
|
|
37
|
+
if (major === undefined)
|
|
38
|
+
return { show: false, major: null };
|
|
39
|
+
const seen = seenMajor(db);
|
|
40
|
+
// A damaged value is not a reason to show the tour on every launch.
|
|
41
|
+
if (seen === null)
|
|
42
|
+
return { show: false, major };
|
|
43
|
+
if (seen !== undefined)
|
|
44
|
+
return { show: seen < major, major };
|
|
45
|
+
const machine = machineOf(db);
|
|
46
|
+
if (machine === undefined)
|
|
47
|
+
return { show: false, major };
|
|
48
|
+
const first = majorOf(machine.appVersion);
|
|
49
|
+
return { show: first !== undefined && first < major, major };
|
|
50
|
+
}
|
|
51
|
+
// Closing the tour, finishing it or skipping it all record the running major. Idempotent, and
|
|
52
|
+
// never lowers the value, so running an older version for a while and updating again does not
|
|
53
|
+
// bring back a tour already closed.
|
|
54
|
+
export function dismissWhatsNew(db, version) {
|
|
55
|
+
const major = majorOf(version);
|
|
56
|
+
if (major === undefined)
|
|
57
|
+
return { show: false, major: null };
|
|
58
|
+
transact(db, () => {
|
|
59
|
+
const seen = seenMajor(db);
|
|
60
|
+
if (seen === undefined || seen === null || seen < major)
|
|
61
|
+
writeSetting(db, whatsNewSeenKey, JSON.stringify(major));
|
|
62
|
+
});
|
|
63
|
+
return { show: false, major };
|
|
64
|
+
}
|
|
@@ -173,6 +173,15 @@ export function shortCaptionsAss(words, style) {
|
|
|
173
173
|
continue;
|
|
174
174
|
const shown = next === undefined ? last.end + holdSeconds : Math.min(last.end + holdSeconds, next.start);
|
|
175
175
|
const lines = lineBreaks(group.map((word) => word.text));
|
|
176
|
+
if (style.wordByWord === false) {
|
|
177
|
+
const text = group
|
|
178
|
+
.map((one, place) => `${assText(one.text)}${lines.has(place) ? "\\N" : place < group.length - 1 ? " " : ""}`)
|
|
179
|
+
.join("");
|
|
180
|
+
const from = group[0]?.start ?? last.start;
|
|
181
|
+
if (shown > from)
|
|
182
|
+
events.push(`Dialogue: 0,${assStamp(from)},${assStamp(shown)},Short,,0,0,0,,${position}${text}\n`);
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
176
185
|
for (const [index, word] of group.entries()) {
|
|
177
186
|
const from = word.start;
|
|
178
187
|
const to = group[index + 1]?.start ?? Math.max(shown, word.end);
|
|
@@ -115,7 +115,7 @@ const answerSchema = z.array(z.object({
|
|
|
115
115
|
hashtags: z.array(z.string()),
|
|
116
116
|
why: z.string().optional(),
|
|
117
117
|
}));
|
|
118
|
-
const fix = "
|
|
118
|
+
const fix = "Try again, or choose another model in Edit project → Providers.";
|
|
119
119
|
// Timing noise: the length rules are checked to the hundredth of a second.
|
|
120
120
|
const tolerance = 0.01;
|
|
121
121
|
// Reads the model's answer and keeps the clips that follow the rules: in range, the right
|
|
@@ -210,8 +210,8 @@ export function checkPicks(text, sentences, limits) {
|
|
|
210
210
|
export function noPicksMessage(problems, limits) {
|
|
211
211
|
const shortVideo = limits.durationSeconds < limits.minSeconds;
|
|
212
212
|
if (shortVideo)
|
|
213
|
-
return `The narration is ${String(Math.round(limits.durationSeconds))} seconds long, shorter than the ${String(limits.minSeconds)}-second minimum for a short, so there is nothing to cut. Lower the shortest length in Edit project → Shorts, then
|
|
214
|
-
return `The AI model didn't pick any clip Slopify could use as a short, twice (${problems[0] ?? "no clips were listed"}).
|
|
213
|
+
return `The narration is ${String(Math.round(limits.durationSeconds))} seconds long, shorter than the ${String(limits.minSeconds)}-second minimum for a short, so there is nothing to cut. Lower the shortest length in Edit project → Shorts, then Try again.`;
|
|
214
|
+
return `The AI model didn't pick any clip Slopify could use as a short, twice (${problems[0] ?? "no clips were listed"}). Try again; if it keeps happening, widen the length range in Edit project → Shorts, or choose another model in Edit project → Providers.`;
|
|
215
215
|
}
|
|
216
216
|
function round(seconds) {
|
|
217
217
|
return Math.round(seconds * 1000) / 1000;
|
|
@@ -31,7 +31,7 @@ export function imagePromptMessages(brief) {
|
|
|
31
31
|
},
|
|
32
32
|
];
|
|
33
33
|
}
|
|
34
|
-
const fix = "
|
|
34
|
+
const fix = "Try again, or choose another model in Edit project → Providers.";
|
|
35
35
|
// The reason is the sentence the stage shows; the provider wrapper's `check` asks the model
|
|
36
36
|
// again while attempts remain.
|
|
37
37
|
export function checkImagePrompts(text, count) {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { copyFileSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import { editListVersion } from "../video/edit-list.js";
|
|
3
|
+
import { editListVersion, } from "../video/edit-list.js";
|
|
4
4
|
import { runFfmpeg } from "../video/ffmpeg.js";
|
|
5
5
|
import { motionFor } from "../video/motion.js";
|
|
6
|
-
import { fps } from "../video/plan.js";
|
|
6
|
+
import { figureFrames, fps } from "../video/plan.js";
|
|
7
7
|
import { renderSlideshow } from "../video/slideshow.js";
|
|
8
8
|
import { fasterWords, shortCaptionsAss } from "./captions.js";
|
|
9
9
|
// A short is rendered in three steps. Its sound is cut out of the video's own timeline (the
|
|
@@ -46,7 +46,7 @@ export function shortAudioArgs(timeline, start, end, output, options = {}) {
|
|
|
46
46
|
count += 1;
|
|
47
47
|
}
|
|
48
48
|
if (count === 0 || first === undefined)
|
|
49
|
-
throw new Error("A short's clip falls outside the narration, so it has no sound to cut. Use Pick different moments in Edit project → Shorts, then
|
|
49
|
+
throw new Error("A short's clip falls outside the narration, so it has no sound to cut. Use Pick different moments in Edit project → Shorts, then Try again.");
|
|
50
50
|
const joined = Array.from({ length: count }, (_value, at) => `[a${String(at)}]`).join("");
|
|
51
51
|
chains.push(`${joined}concat=n=${String(count)}:v=0:a=1[joined]`);
|
|
52
52
|
const speed = options.speed ?? 1;
|
|
@@ -89,9 +89,46 @@ export function shortAudioArgs(timeline, start, end, output, options = {}) {
|
|
|
89
89
|
// moving as the video's shots move by their place.
|
|
90
90
|
export function shortEditList(input) {
|
|
91
91
|
if (input.images.length === 0)
|
|
92
|
-
throw new Error("A short has no images to show. Use Make this short again in Edit project → Shorts, then
|
|
92
|
+
throw new Error("A short has no images to show. Use Make this short again in Edit project → Shorts, then Try again.");
|
|
93
93
|
const total = Math.max(1, Math.round(input.seconds * fps));
|
|
94
94
|
const each = Math.max(1, Math.round(input.imageSeconds * fps));
|
|
95
|
+
const figures = figureFrames(input.figures ?? [], total);
|
|
96
|
+
if (figures.length > 0) {
|
|
97
|
+
// The short's images take turns around the cards, in order, starting over if the cards
|
|
98
|
+
// leave room for more shots than there are images.
|
|
99
|
+
const shots = [];
|
|
100
|
+
let cursor = 0;
|
|
101
|
+
let slot = 0;
|
|
102
|
+
const stretch = (to) => {
|
|
103
|
+
for (let from = cursor; from < to; from += each) {
|
|
104
|
+
const frames = Math.min(each, to - from);
|
|
105
|
+
shots.push({
|
|
106
|
+
source: { kind: "image", path: input.images[slot % input.images.length] ?? "" },
|
|
107
|
+
frames,
|
|
108
|
+
motion: motionFor(input.motionStyle, slot, input.zoomPercent),
|
|
109
|
+
});
|
|
110
|
+
slot += 1;
|
|
111
|
+
}
|
|
112
|
+
cursor = Math.max(cursor, to);
|
|
113
|
+
};
|
|
114
|
+
for (const figure of figures) {
|
|
115
|
+
stretch(figure.startFrame);
|
|
116
|
+
shots.push({
|
|
117
|
+
source: { kind: "image", path: figure.path },
|
|
118
|
+
frames: figure.frames,
|
|
119
|
+
motion: { kind: "still" },
|
|
120
|
+
});
|
|
121
|
+
cursor = figure.startFrame + figure.frames;
|
|
122
|
+
}
|
|
123
|
+
stretch(total);
|
|
124
|
+
return {
|
|
125
|
+
version: editListVersion,
|
|
126
|
+
...shortFrame,
|
|
127
|
+
fps,
|
|
128
|
+
audio: [{ kind: "body", path: input.audioPath, seconds: input.seconds }],
|
|
129
|
+
shots,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
95
132
|
const images = input.images.slice(0, Math.max(1, Math.ceil(total / each)));
|
|
96
133
|
const shots = images.flatMap((path, at) => {
|
|
97
134
|
const frames = at < images.length - 1 ? each : total - each * (images.length - 1);
|
|
@@ -113,6 +150,7 @@ export function shortEditList(input) {
|
|
|
113
150
|
shots,
|
|
114
151
|
};
|
|
115
152
|
}
|
|
153
|
+
// What the master measured, when the run asked for one.
|
|
116
154
|
export async function renderShort(run) {
|
|
117
155
|
const speed = run.speed ?? 1;
|
|
118
156
|
// The images keep their share of what is said, so at a higher speed each is on screen
|
|
@@ -134,20 +172,32 @@ export async function renderShort(run) {
|
|
|
134
172
|
writeFileSync(join(workspace, "subtitles.ass"), shortCaptionsAss(fasterWords(run.words, speed), {
|
|
135
173
|
...shortFrame,
|
|
136
174
|
fontName: run.font.assName,
|
|
175
|
+
...(run.wordByWord === false ? { wordByWord: false } : {}),
|
|
137
176
|
...(run.title === undefined ? {} : { title: { text: run.title, seconds } }),
|
|
138
177
|
}), { mode: 0o600 });
|
|
139
178
|
mkdirSync(join(workspace, "fonts"), { mode: 0o700 });
|
|
140
179
|
copyFileSync(run.font.path, join(workspace, "fonts", `selected${run.font.extension}`));
|
|
141
|
-
|
|
180
|
+
const edit = shortEditList({
|
|
181
|
+
audioPath: audio,
|
|
182
|
+
seconds,
|
|
183
|
+
images: run.images,
|
|
184
|
+
imageSeconds: run.imageSeconds / speed,
|
|
185
|
+
motionStyle: run.motionStyle,
|
|
186
|
+
zoomPercent: run.zoomPercent,
|
|
187
|
+
...(run.figures === undefined
|
|
188
|
+
? {}
|
|
189
|
+
: {
|
|
190
|
+
figures: run.figures.map((figure) => ({
|
|
191
|
+
...figure,
|
|
192
|
+
start: figure.start / speed,
|
|
193
|
+
end: figure.end / speed,
|
|
194
|
+
})),
|
|
195
|
+
}),
|
|
196
|
+
});
|
|
197
|
+
return await renderSlideshow({
|
|
142
198
|
bin: run.bin,
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
seconds,
|
|
146
|
-
images: run.images,
|
|
147
|
-
imageSeconds: run.imageSeconds / speed,
|
|
148
|
-
motionStyle: run.motionStyle,
|
|
149
|
-
zoomPercent: run.zoomPercent,
|
|
150
|
-
}),
|
|
199
|
+
master: run.master,
|
|
200
|
+
edit: run.frame === undefined ? edit : { ...edit, ...run.frame },
|
|
151
201
|
output: run.output,
|
|
152
202
|
burnSubtitles: true,
|
|
153
203
|
cwd: workspace,
|
|
@@ -16,6 +16,9 @@ export function assetOf(output) {
|
|
|
16
16
|
return `${asset}-${String(output.meta.short)}`;
|
|
17
17
|
if (output.role === "short_image" && output.meta.short !== undefined)
|
|
18
18
|
return `${asset}-${String(output.meta.short)}-${String(output.meta.index ?? 1)}`;
|
|
19
|
+
// A project can hold three thumbnails; the second and third carry their number.
|
|
20
|
+
if (output.role === "thumbnail" && output.meta.index !== undefined && output.meta.index > 1)
|
|
21
|
+
return `${asset}-${String(output.meta.index)}`;
|
|
19
22
|
if (output.role === "instructions") {
|
|
20
23
|
return `${output.stageKind}-${asset}`;
|
|
21
24
|
}
|
|
@@ -3,7 +3,7 @@ import { constants, lstatSync, readdirSync } from "node:fs";
|
|
|
3
3
|
import { open } from "node:fs/promises";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { transact } from "../../kernel/db/tx.js";
|
|
6
|
-
import { backupFormat, backupSchemaVersion, checksumsMember, fontMember, libraryMember, manifestMember, projectFileMember, projectMember, projectTables, safeRelativePath, stagedMember, usageMember, } from "./backup-format.js";
|
|
6
|
+
import { backupFormat, backupSchemaVersion, checksumsMember, fontMember, imageMember, libraryMember, manifestMember, projectFileMember, projectMember, projectTables, safeRelativePath, stagedMember, usageMember, } from "./backup-format.js";
|
|
7
7
|
import { projectDir, stagingPath } from "./layout.js";
|
|
8
8
|
import { exportableSettings, installedUploadedFontFiles } from "./portable.js";
|
|
9
9
|
import { tarEnd, tarHeader, tarMemberBytes, tarPadding } from "./tar.js";
|
|
@@ -25,6 +25,9 @@ export function busySentence(projects) {
|
|
|
25
25
|
const more = projects.length > 3 ? ` and ${projects.length - 3} more` : "";
|
|
26
26
|
return `Slopify can't export while projects are being made (${named}${more}): their files are still being written. Wait for them to finish, or pause them on their project page, then press Export everything again.`;
|
|
27
27
|
}
|
|
28
|
+
function memberSize(member) {
|
|
29
|
+
return member.kind === "file" ? member.size : member.content.byteLength;
|
|
30
|
+
}
|
|
28
31
|
export function busyProjects(deps) {
|
|
29
32
|
return deps.db
|
|
30
33
|
.prepare(`SELECT p.id AS id, p.title AS title,
|
|
@@ -84,6 +87,8 @@ export function planBackup(deps) {
|
|
|
84
87
|
path: stagingPath(deps.paths, staged.id),
|
|
85
88
|
size: staged.bytes,
|
|
86
89
|
});
|
|
90
|
+
for (const image of snapshot.library.images)
|
|
91
|
+
files.push({ name: imageMember(image.sha256), kind: "data", content: image.content });
|
|
87
92
|
const manifest = {
|
|
88
93
|
format: backupFormat,
|
|
89
94
|
schemaVersion: backupSchemaVersion,
|
|
@@ -97,7 +102,7 @@ export function planBackup(deps) {
|
|
|
97
102
|
bytes: project.part.files.reduce((sum, file) => sum + file.bytes, 0),
|
|
98
103
|
})),
|
|
99
104
|
files: files.length,
|
|
100
|
-
bytes: files.reduce((sum, file) => sum + (file
|
|
105
|
+
bytes: files.reduce((sum, file) => sum + memberSize(file), 0),
|
|
101
106
|
};
|
|
102
107
|
const members = [
|
|
103
108
|
json(manifestMember, manifest),
|
|
@@ -106,11 +111,7 @@ export function planBackup(deps) {
|
|
|
106
111
|
...snapshot.projects.map((project) => json(projectMember(project.part.id), project.part)),
|
|
107
112
|
...files,
|
|
108
113
|
];
|
|
109
|
-
const archiveBytes = members.reduce((sum, member) => sum +
|
|
110
|
-
tarMemberBytes({
|
|
111
|
-
name: member.name,
|
|
112
|
-
size: member.kind === "bytes" ? member.content.byteLength : member.size,
|
|
113
|
-
}), 0) +
|
|
114
|
+
const archiveBytes = members.reduce((sum, member) => sum + tarMemberBytes({ name: member.name, size: memberSize(member) }), 0) +
|
|
114
115
|
tarMemberBytes({
|
|
115
116
|
name: checksumsMember,
|
|
116
117
|
size: checksumsBytes(files, () => "0".repeat(64)).byteLength,
|
|
@@ -132,11 +133,16 @@ export async function* streamBackup(plan) {
|
|
|
132
133
|
const digests = new Map();
|
|
133
134
|
const files = [];
|
|
134
135
|
for (const member of plan.members) {
|
|
135
|
-
const size = member
|
|
136
|
+
const size = memberSize(member);
|
|
136
137
|
yield tarHeader({ name: member.name, size });
|
|
137
138
|
if (member.kind === "bytes") {
|
|
138
139
|
yield member.content;
|
|
139
140
|
}
|
|
141
|
+
else if (member.kind === "data") {
|
|
142
|
+
files.push(member);
|
|
143
|
+
digests.set(member.name, createHash("sha256").update(member.content).digest("hex"));
|
|
144
|
+
yield member.content;
|
|
145
|
+
}
|
|
140
146
|
else {
|
|
141
147
|
files.push(member);
|
|
142
148
|
const hash = createHash("sha256");
|
|
@@ -255,10 +261,27 @@ function librarySnapshot(deps) {
|
|
|
255
261
|
put("entries", rowsOf(db, "SELECT * FROM entries ORDER BY rowid"));
|
|
256
262
|
put("voices", rowsOf(db, "SELECT * FROM voices ORDER BY rowid"));
|
|
257
263
|
put("document_themes", rowsOf(db, "SELECT * FROM document_themes ORDER BY rowid"));
|
|
264
|
+
put("narration_aliases", rowsOf(db, "SELECT * FROM narration_aliases ORDER BY position,rowid"));
|
|
258
265
|
put("project_templates", rowsOf(db, "SELECT * FROM project_templates ORDER BY rowid"));
|
|
259
266
|
put("project_template_revisions", rowsOf(db, "SELECT * FROM project_template_revisions ORDER BY template_id,version"));
|
|
260
267
|
put("schedules", rowsOf(db, "SELECT * FROM schedules ORDER BY rowid"));
|
|
261
268
|
put("schedule_runs", rowsOf(db, "SELECT * FROM schedule_runs ORDER BY rowid"));
|
|
269
|
+
put("schedule_topics", rowsOf(db, "SELECT * FROM schedule_topics ORDER BY rowid"));
|
|
270
|
+
put("library_versions", rowsOf(db, "SELECT * FROM library_versions ORDER BY item_kind,item_id,version"));
|
|
271
|
+
put("channels", rowsOf(db, "SELECT * FROM channels ORDER BY rowid"));
|
|
272
|
+
put("cast_members", rowsOf(db, "SELECT * FROM cast_members ORDER BY rowid"));
|
|
273
|
+
put("cast_images", rowsOf(db, "SELECT * FROM cast_images ORDER BY rowid"));
|
|
274
|
+
put("episode_memories", rowsOf(db, "SELECT * FROM episode_memories ORDER BY rowid"));
|
|
275
|
+
put("channel_videos", rowsOf(db, "SELECT * FROM channel_videos ORDER BY rowid"));
|
|
276
|
+
put("standalone_usage", rowsOf(db, "SELECT * FROM standalone_usage ORDER BY rowid"));
|
|
277
|
+
// Every picture, not only the cast's: a project's config names the pictures it was started
|
|
278
|
+
// with by hash, and a Rebuild reads them from here.
|
|
279
|
+
const images = db
|
|
280
|
+
.prepare("SELECT sha256,mime,bytes FROM image_blobs ORDER BY sha256")
|
|
281
|
+
.all()
|
|
282
|
+
.flatMap((row) => row.bytes instanceof Uint8Array && row.bytes.byteLength > 0
|
|
283
|
+
? [{ sha256: String(row.sha256), mime: String(row.mime), content: row.bytes }]
|
|
284
|
+
: []);
|
|
262
285
|
// Play drafts are work the user typed and has not started yet, so they travel. A draft
|
|
263
286
|
// that already became projects is not: its projects carry it. Its uploads travel with it
|
|
264
287
|
// when they are complete on disk; one that is not comes back asking to be reattached,
|
|
@@ -293,7 +316,23 @@ function librarySnapshot(deps) {
|
|
|
293
316
|
name: font.name,
|
|
294
317
|
bytes: font.bytes,
|
|
295
318
|
}));
|
|
296
|
-
return {
|
|
319
|
+
return {
|
|
320
|
+
part: {
|
|
321
|
+
tables,
|
|
322
|
+
fonts,
|
|
323
|
+
staged,
|
|
324
|
+
...(images.length === 0
|
|
325
|
+
? {}
|
|
326
|
+
: {
|
|
327
|
+
images: images.map((image) => ({
|
|
328
|
+
sha256: image.sha256,
|
|
329
|
+
mime: image.mime,
|
|
330
|
+
bytes: image.content.byteLength,
|
|
331
|
+
})),
|
|
332
|
+
}),
|
|
333
|
+
},
|
|
334
|
+
images,
|
|
335
|
+
};
|
|
297
336
|
}
|
|
298
337
|
function fileBytes(path) {
|
|
299
338
|
try {
|