@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
package/dist/main.js
CHANGED
|
@@ -11,13 +11,15 @@ import { createHostCliClient } from "./adapters/host-cli/index.js";
|
|
|
11
11
|
import { nodeRunCli } from "./adapters/llm/run-cli.js";
|
|
12
12
|
import { curateRegistry } from "./catalog/registry.js";
|
|
13
13
|
import { createCatalogueStore } from "./catalog/store.js";
|
|
14
|
-
import {
|
|
14
|
+
import { createAutostart } from "./edge/autostart/index.js";
|
|
15
|
+
import { dockerActivationCommitted, dockerFilesLayout, dockerFolderConfiguration, } from "./edge/docker-install/activation.js";
|
|
15
16
|
import { createHub, observedHub } from "./edge/events/hub.js";
|
|
16
17
|
import { currentProjectEvent } from "./edge/events/visibility.js";
|
|
17
18
|
import { createApp } from "./edge/http/app.js";
|
|
18
19
|
import { createMutationLifecycle, drainMutationsWithDeadline } from "./edge/http/mutations.js";
|
|
19
20
|
import { limitRequestTimes } from "./edge/http/timeouts.js";
|
|
20
21
|
import { openFolder } from "./edge/open-folder.js";
|
|
22
|
+
import { readHostLogin } from "./host-cli/status.js";
|
|
21
23
|
import { createAudioPreviewStore } from "./kernel/audio-preview.js";
|
|
22
24
|
import { systemClock } from "./kernel/clock.js";
|
|
23
25
|
import { openDb } from "./kernel/db/index.js";
|
|
@@ -27,38 +29,58 @@ import { causedBy } from "./kernel/errors.js";
|
|
|
27
29
|
import { ulidIds } from "./kernel/ids.js";
|
|
28
30
|
import { acquireInstanceLock } from "./kernel/lock.js";
|
|
29
31
|
import { openLog } from "./kernel/log.js";
|
|
30
|
-
import { ensureDirs, layout, subtitleModelDir } from "./kernel/paths.js";
|
|
32
|
+
import { ensureDataDirs, ensureDirs, layout, repoint, subtitleModelDir } from "./kernel/paths.js";
|
|
31
33
|
import { stageKinds } from "./kernel/pipeline.js";
|
|
32
34
|
import { sqliteAttempts } from "./kernel/runner/attempt-repo.js";
|
|
35
|
+
import { auditionVoice } from "./kernel/runner/audition.js";
|
|
33
36
|
import { createCheckpointAuthority, } from "./kernel/runner/checkpoint-authority.js";
|
|
34
37
|
import { createRunner } from "./kernel/runner/index.js";
|
|
35
38
|
import { stageProviders } from "./kernel/runner/providers.js";
|
|
36
39
|
import { createProviderQueue } from "./kernel/runner/queue.js";
|
|
40
|
+
import { standaloneImage, standaloneLlm } from "./kernel/runner/standalone.js";
|
|
37
41
|
import { readVersion } from "./kernel/version.js";
|
|
38
42
|
import { modelSources } from "./model-catalog.js";
|
|
39
|
-
import { projectById, projectPaused } from "./slices/admission/repo.js";
|
|
43
|
+
import { projectById, projectPaused, projectTrashed } from "./slices/admission/repo.js";
|
|
40
44
|
import { createBackupService } from "./slices/backups/service.js";
|
|
41
45
|
import { pumpQueue, queueWaiting } from "./slices/batch/index.js";
|
|
46
|
+
import { settleInterruptedCastImages } from "./slices/channels/cast-images.js";
|
|
42
47
|
import { approveCheckpoint } from "./slices/checkpoints/index.js";
|
|
43
48
|
import { checkpointDecisionForWork, recoverCheckpointWork, settleReleasedCheckpoints, } from "./slices/checkpoints/recovery.js";
|
|
49
|
+
import { createEpisodeMemoryWatcher, } from "./slices/episodes/summarize.js";
|
|
44
50
|
import { resolveFont } from "./slices/fonts/index.js";
|
|
51
|
+
import { decodePeaks } from "./slices/narration/peaks.js";
|
|
45
52
|
import { createRunNotifier } from "./slices/notifications/notifier.js";
|
|
53
|
+
import { projectLink } from "./slices/notifications/rules.js";
|
|
46
54
|
import { createNotificationSender } from "./slices/notifications/send.js";
|
|
47
55
|
import { readNotificationUrl } from "./slices/notifications/settings.js";
|
|
56
|
+
import { seedSamples } from "./slices/onboarding/sample.js";
|
|
48
57
|
import { templateById } from "./slices/project-templates/repo.js";
|
|
58
|
+
import { recoverProject } from "./slices/rebuild/recovery.js";
|
|
49
59
|
import { claimWork, finishWork, maySubmit } from "./slices/rebuild/repo.js";
|
|
60
|
+
import { waitToRetry, wakeRetries } from "./slices/rebuild/retry.js";
|
|
61
|
+
import { createReviewRedos, reviewHold } from "./slices/rebuild/review-redo.js";
|
|
50
62
|
import { materializeAdmittedWork } from "./slices/rebuild/runtime-materialize.js";
|
|
51
63
|
import { runRevisionInvocation } from "./slices/rebuild/runtime-run.js";
|
|
52
64
|
import { executionStages, executionStandings, invocationReady, projectStandings, recordWorkProgress, } from "./slices/rebuild/runtime-store.js";
|
|
65
|
+
import { currentRevisionId } from "./slices/revisions/repo.js";
|
|
66
|
+
import { createLimitGate, resumeAfterRestart } from "./slices/run-cost/limits.js";
|
|
67
|
+
import { createStandaloneMeter, createUsageMeter } from "./slices/run-cost/meter.js";
|
|
53
68
|
import { settleTerminalScheduleRuns } from "./slices/schedules/repo.js";
|
|
54
69
|
import { createScheduleRunner } from "./slices/schedules/scheduler.js";
|
|
55
70
|
import { nodeCliProbe } from "./slices/settings/cli-status.js";
|
|
56
71
|
import { isLocalCliProvider, localCliConcurrency } from "./slices/settings/model.js";
|
|
57
72
|
import { providerStatuses } from "./slices/settings/readiness.js";
|
|
73
|
+
import { busyProjects } from "./slices/storage/backup-export.js";
|
|
74
|
+
import { documentsDir, nodeDocumentsHost } from "./slices/storage/documents.js";
|
|
75
|
+
import { createFilesService, ensureFilesFolders, filesLayoutOf, settleFilesLocation, } from "./slices/storage/files-location.js";
|
|
58
76
|
import { reconcileStorage } from "./slices/storage/reconcile.js";
|
|
77
|
+
import { previewPictures } from "./slices/style-preview/images.js";
|
|
78
|
+
import { ffmpegStylePreview } from "./slices/style-preview/render.js";
|
|
79
|
+
import { createStylePreviews, stylePreviewDir } from "./slices/style-preview/service.js";
|
|
59
80
|
import { collectorEndpoint, httpPostEvents } from "./slices/telemetry/collector-client.js";
|
|
60
81
|
import { createFlusher } from "./slices/telemetry/flush.js";
|
|
61
82
|
import { record } from "./slices/telemetry/record.js";
|
|
83
|
+
import { createTrashPurge } from "./slices/trash/service.js";
|
|
62
84
|
import { probeDurationMs } from "./slices/video/ffmpeg.js";
|
|
63
85
|
import { watchActivation } from "./updater/candidate.js";
|
|
64
86
|
import { launchUpdate } from "./updater/install.js";
|
|
@@ -125,11 +147,12 @@ export async function boot(config, options = {}) {
|
|
|
125
147
|
if (dockerState !== undefined &&
|
|
126
148
|
(process.env.SLOPIFY_CONTAINER !== "1" ||
|
|
127
149
|
dockerState !== "/opt/slopify-install/activation.json"))
|
|
128
|
-
throw new Error("This container was started with Slopify's Docker settings in the wrong place (SLOPIFY_DOCKER_INSTALL_STATE). Start it with
|
|
150
|
+
throw new Error("This container was started with Slopify's Docker settings in the wrong place (SLOPIFY_DOCKER_INSTALL_STATE must be /opt/slopify-install/activation.json). Start it with Slopify's compose.yaml, or install it with npx @gentbajko/slopify --docker");
|
|
129
151
|
const candidateToken = process.env.SLOPIFY_UPDATE_TOKEN ?? "";
|
|
130
152
|
const pendingActivation = isUpdateToken(candidateToken) && process.env.SLOPIFY_UPDATE_PENDING === "1";
|
|
131
|
-
|
|
153
|
+
ensureDataDirs(paths, { mode: 0o700 });
|
|
132
154
|
const lock = acquireInstanceLock(paths.lock);
|
|
155
|
+
const freshInstall = !existsSync(paths.db);
|
|
133
156
|
let db;
|
|
134
157
|
try {
|
|
135
158
|
const ffmpeg = await prepareFfmpeg({
|
|
@@ -140,10 +163,29 @@ export async function boot(config, options = {}) {
|
|
|
140
163
|
});
|
|
141
164
|
db = openDb(paths.db);
|
|
142
165
|
migrate(db, clock);
|
|
166
|
+
// Where the projects, backups and exports live. A container's are the installer's mounts;
|
|
167
|
+
// a native install decides once and remembers (slices/storage/files-location.ts).
|
|
168
|
+
const container = process.env.SLOPIFY_CONTAINER === "1";
|
|
169
|
+
const files = container
|
|
170
|
+
? dockerFilesLayout(process.env, paths.dataDir)
|
|
171
|
+
: filesLayoutOf(await settleFilesLocation({
|
|
172
|
+
db,
|
|
173
|
+
dataDir: paths.dataDir,
|
|
174
|
+
fresh: freshInstall,
|
|
175
|
+
documents: options.filesInDocuments === true
|
|
176
|
+
? () => documentsDir(nodeDocumentsHost())
|
|
177
|
+
: undefined,
|
|
178
|
+
}), paths.dataDir);
|
|
179
|
+
repoint(paths, files);
|
|
180
|
+
if (files.exports === null)
|
|
181
|
+
ensureDirs(paths, { mode: 0o700 });
|
|
182
|
+
else
|
|
183
|
+
await ensureFilesFolders(files);
|
|
143
184
|
const runtimeDb = db;
|
|
144
185
|
const interrupted = markInterruptedStages(db, clock);
|
|
145
186
|
recoverCheckpointWork(db);
|
|
146
187
|
const settledSchedules = settleTerminalScheduleRuns(db, clock.now().toISOString());
|
|
188
|
+
settleInterruptedCastImages({ db });
|
|
147
189
|
// The updater can restore the database after a failed candidate boot, but it cannot
|
|
148
190
|
// restore files deleted by reconciliation. Leave the filesystem untouched until the
|
|
149
191
|
// candidate's committed activation pointer has been verified.
|
|
@@ -164,14 +206,22 @@ export async function boot(config, options = {}) {
|
|
|
164
206
|
? undefined
|
|
165
207
|
: { title: project.title, makesVideo: project.config.sources.video !== "off" };
|
|
166
208
|
},
|
|
209
|
+
// Read when a notification goes out, by which time the server is listening.
|
|
210
|
+
link: (projectId) => projectLink(config.host, listeningPort, projectId),
|
|
167
211
|
send: sendNotification,
|
|
168
212
|
log,
|
|
169
213
|
});
|
|
214
|
+
// Episode memory summarises a project that has just finished; wired once the providers
|
|
215
|
+
// exist, below.
|
|
216
|
+
let episodes;
|
|
170
217
|
const hub = observedHub(createHub({
|
|
171
218
|
ids,
|
|
172
219
|
log,
|
|
173
220
|
acceptEvent: (event) => currentProjectEvent(eventDb, event),
|
|
174
|
-
}),
|
|
221
|
+
}), (event) => {
|
|
222
|
+
notifier.observe(event);
|
|
223
|
+
episodes?.observe(event);
|
|
224
|
+
});
|
|
175
225
|
const version = readVersion();
|
|
176
226
|
const telemetry = { db, ids, clock, log, appVersion: version };
|
|
177
227
|
const flusher = createFlusher({
|
|
@@ -191,9 +241,28 @@ export async function boot(config, options = {}) {
|
|
|
191
241
|
clock,
|
|
192
242
|
probe: nodeCliProbe,
|
|
193
243
|
hostCli,
|
|
244
|
+
ffmpeg,
|
|
194
245
|
}), catalogue);
|
|
246
|
+
// Calls that belong to no project (topic generation, episode summaries, cast pictures)
|
|
247
|
+
// still go through the attempt wrapper, and are metered against their schedule or channel.
|
|
248
|
+
const standalone = {
|
|
249
|
+
registry,
|
|
250
|
+
clock,
|
|
251
|
+
log,
|
|
252
|
+
meter: createStandaloneMeter({ db, ids, clock, catalogue: () => catalogue.read() }),
|
|
253
|
+
};
|
|
254
|
+
episodes = createEpisodeMemoryWatcher({
|
|
255
|
+
db,
|
|
256
|
+
paths,
|
|
257
|
+
clock,
|
|
258
|
+
log,
|
|
259
|
+
uuid: randomUUID,
|
|
260
|
+
llm: (call) => standaloneLlm(standalone, call),
|
|
261
|
+
});
|
|
195
262
|
const audioPreviews = createAudioPreviewStore();
|
|
263
|
+
const reviewRedos = createReviewRedos();
|
|
196
264
|
const runner = wireRunner({
|
|
265
|
+
onFinished: (work) => reviewRedos.kick(work.projectId),
|
|
197
266
|
db,
|
|
198
267
|
paths,
|
|
199
268
|
clock,
|
|
@@ -257,7 +326,7 @@ export async function boot(config, options = {}) {
|
|
|
257
326
|
? Promise.resolve(version)
|
|
258
327
|
: publishedVersion(globalThis.fetch),
|
|
259
328
|
unsupported: () => process.env.SLOPIFY_DISABLE_UPDATES === "1"
|
|
260
|
-
? "
|
|
329
|
+
? "Slopify in Docker is updated from the terminal: npx @gentbajko/slopify@latest update. It waits for running work, keeps a recovery copy of your data and puts the previous version back if the new one doesn't start."
|
|
261
330
|
: !existsSync(oldEntry) || !existsSync(workerEntry)
|
|
262
331
|
? "Run Slopify from its installed package to use in-app updates."
|
|
263
332
|
: npm === undefined
|
|
@@ -269,6 +338,18 @@ export async function boot(config, options = {}) {
|
|
|
269
338
|
.prepare("SELECT id FROM projects")
|
|
270
339
|
.all()
|
|
271
340
|
.some((row) => typeof row.id === "string" && runner.hasInflight?.(row.id) === true),
|
|
341
|
+
busyWith: () => {
|
|
342
|
+
const running = updateDb
|
|
343
|
+
.prepare("SELECT p.title FROM stages s JOIN projects p ON p.id=s.project_id WHERE s.state='running' LIMIT 1")
|
|
344
|
+
.get();
|
|
345
|
+
if (typeof running?.title === "string")
|
|
346
|
+
return running.title;
|
|
347
|
+
const inflight = updateDb
|
|
348
|
+
.prepare("SELECT id,title FROM projects")
|
|
349
|
+
.all()
|
|
350
|
+
.find((row) => typeof row.id === "string" && runner.hasInflight?.(row.id) === true);
|
|
351
|
+
return typeof inflight?.title === "string" ? inflight.title : undefined;
|
|
352
|
+
},
|
|
272
353
|
report: (message) => log.write("warn", "update", { detail: message }),
|
|
273
354
|
install: async (next, restarting) => {
|
|
274
355
|
if (npm === undefined)
|
|
@@ -302,6 +383,21 @@ export async function boot(config, options = {}) {
|
|
|
302
383
|
modelsFor: modelSources(registry).modelsFor,
|
|
303
384
|
emit: (projectId, event) => hub.emit(projectId, event),
|
|
304
385
|
};
|
|
386
|
+
// Redos a review asked for before the last shutdown start now.
|
|
387
|
+
reviewRedos.bind(rebuild);
|
|
388
|
+
reviewRedos.kick();
|
|
389
|
+
// A stage that was waiting for a CLI's plan limits when the app stopped carries on waiting.
|
|
390
|
+
void resumeAfterRestart(db, async (projectId) => {
|
|
391
|
+
const baseRevisionId = currentRevisionId(runtimeDb, projectId);
|
|
392
|
+
if (baseRevisionId === undefined)
|
|
393
|
+
return false;
|
|
394
|
+
const result = await recoverProject(rebuild, projectId, {
|
|
395
|
+
baseRevisionId,
|
|
396
|
+
idempotencyKey: randomUUID(),
|
|
397
|
+
action: { kind: "resume" },
|
|
398
|
+
});
|
|
399
|
+
return result.ok;
|
|
400
|
+
}, log);
|
|
305
401
|
const draftDeps = {
|
|
306
402
|
db,
|
|
307
403
|
paths,
|
|
@@ -321,11 +417,20 @@ export async function boot(config, options = {}) {
|
|
|
321
417
|
flusher.soon();
|
|
322
418
|
},
|
|
323
419
|
};
|
|
324
|
-
const
|
|
420
|
+
const scheduleRunnerDeps = {
|
|
325
421
|
...draftDeps,
|
|
326
422
|
template: (id, templateVersion) => templateById(runtimeDb, id, templateVersion),
|
|
423
|
+
topicLlm: (call) => standaloneLlm(standalone, call),
|
|
424
|
+
topicsWaiting: (event) => {
|
|
425
|
+
hub.emitGlobal(event);
|
|
426
|
+
notifier.observeTopics(event);
|
|
427
|
+
},
|
|
428
|
+
};
|
|
429
|
+
const scheduleRunner = createScheduleRunner(scheduleRunnerDeps);
|
|
430
|
+
const scheduleDeps = {
|
|
431
|
+
...scheduleRunnerDeps,
|
|
432
|
+
requestTopics: scheduleRunner.requestTopics,
|
|
327
433
|
};
|
|
328
|
-
const scheduleRunner = createScheduleRunner(scheduleDeps);
|
|
329
434
|
scheduleRunner.recover(clock.now());
|
|
330
435
|
const scheduleTicks = createScheduleTickLifecycle({
|
|
331
436
|
beginMutation: updater.beginMutation,
|
|
@@ -336,6 +441,23 @@ export async function boot(config, options = {}) {
|
|
|
336
441
|
});
|
|
337
442
|
const mutations = createMutationLifecycle();
|
|
338
443
|
const folderConfiguration = await dockerFolderConfiguration(process.env, paths.projects);
|
|
444
|
+
const filesService = createFilesService({
|
|
445
|
+
db,
|
|
446
|
+
paths,
|
|
447
|
+
documents: () => documentsDir(nodeDocumentsHost()),
|
|
448
|
+
busy: () => busyProjects({ db: runtimeDb, hasInflight: runner.hasInflight }),
|
|
449
|
+
openFolder,
|
|
450
|
+
beginMutation: updater.beginMutation,
|
|
451
|
+
...(container
|
|
452
|
+
? {
|
|
453
|
+
docker: {
|
|
454
|
+
hostProjects: folderConfiguration.hostProjects,
|
|
455
|
+
hostBackups: folderConfiguration.hostBackups,
|
|
456
|
+
...(hostCli === undefined ? {} : { openOnHost: hostCli.openFolder }),
|
|
457
|
+
},
|
|
458
|
+
}
|
|
459
|
+
: {}),
|
|
460
|
+
});
|
|
339
461
|
const backups = createBackupService({
|
|
340
462
|
db,
|
|
341
463
|
paths,
|
|
@@ -348,12 +470,34 @@ export async function boot(config, options = {}) {
|
|
|
348
470
|
bootedAt: clock.now(),
|
|
349
471
|
beginMutation: updater.beginMutation,
|
|
350
472
|
});
|
|
473
|
+
// Before the server answers, so the first page already lists the samples. A failure costs
|
|
474
|
+
// only the samples: the log says why and Settings → Restore samples tries again.
|
|
475
|
+
if (options.seedSample === true && !pendingActivation)
|
|
476
|
+
await seedSamples({ db, paths, clock, ids, log, appVersion: version }).catch((error) => {
|
|
477
|
+
log.write("warn", "sample.seed", { detail: causedBy(error) });
|
|
478
|
+
});
|
|
479
|
+
const autostart = await createAutostart({
|
|
480
|
+
db,
|
|
481
|
+
dataDir: paths.dataDir,
|
|
482
|
+
logs: paths.logs,
|
|
483
|
+
port: config.port,
|
|
484
|
+
host: config.host,
|
|
485
|
+
version,
|
|
486
|
+
env: process.env,
|
|
487
|
+
});
|
|
488
|
+
if (options.refreshAutostart === true)
|
|
489
|
+
void autostart.refresh().catch((error) => {
|
|
490
|
+
log.write("warn", "autostart.refresh", { detail: causedBy(error) });
|
|
491
|
+
});
|
|
351
492
|
const app = createApp({
|
|
493
|
+
autostart,
|
|
352
494
|
rebuild,
|
|
353
495
|
drafts: draftDeps,
|
|
354
496
|
schedules: scheduleDeps,
|
|
355
497
|
backups,
|
|
498
|
+
files: filesService,
|
|
356
499
|
...(rebuild.measureAudio === undefined ? {} : { measureAudio: rebuild.measureAudio }),
|
|
500
|
+
decodePeaks: (path, signal) => decodePeaks(ffmpeg, path, signal),
|
|
357
501
|
openFolder,
|
|
358
502
|
folderLocation: {
|
|
359
503
|
...folderConfiguration,
|
|
@@ -368,16 +512,39 @@ export async function boot(config, options = {}) {
|
|
|
368
512
|
mutations,
|
|
369
513
|
sendNotification,
|
|
370
514
|
audioPreviews,
|
|
515
|
+
stylePreviews: createStylePreviews({
|
|
516
|
+
dir: stylePreviewDir(paths.dataDir),
|
|
517
|
+
render: ffmpegStylePreview({ ffmpeg, paths, log, dir: stylePreviewDir(paths.dataDir) }),
|
|
518
|
+
pictures: previewPictures({ db, paths }),
|
|
519
|
+
log,
|
|
520
|
+
}),
|
|
371
521
|
...modelSources(registry),
|
|
522
|
+
audition: (call, signal) => auditionVoice({ registry, clock, log }, call, signal),
|
|
523
|
+
voiceLanguages: async (provider, voiceId, signal) => {
|
|
524
|
+
try {
|
|
525
|
+
return await registry.tts(provider).voiceLanguages?.(voiceId, signal);
|
|
526
|
+
}
|
|
527
|
+
catch {
|
|
528
|
+
return undefined;
|
|
529
|
+
}
|
|
530
|
+
},
|
|
372
531
|
catalogue,
|
|
373
532
|
clock,
|
|
374
533
|
ids,
|
|
375
534
|
log,
|
|
376
535
|
version,
|
|
377
536
|
webDist: fileURLToPath(new URL("../dist/web", import.meta.url)),
|
|
537
|
+
extensionDist: fileURLToPath(new URL("../dist/extension", import.meta.url)),
|
|
378
538
|
flushSoon: flusher.soon,
|
|
379
539
|
probe: nodeCliProbe,
|
|
380
540
|
hostCliStatus: hostCli?.status,
|
|
541
|
+
generateCastImage: ({ channelId, ...call }) => standaloneImage(standalone, {
|
|
542
|
+
...call,
|
|
543
|
+
owner: { kind: "channel", id: channelId },
|
|
544
|
+
purpose: "cast-image",
|
|
545
|
+
}),
|
|
546
|
+
fetch: globalThis.fetch,
|
|
547
|
+
...(hostCli === undefined ? { cliLogin: readHostLogin } : {}),
|
|
381
548
|
});
|
|
382
549
|
const server = await listen(app, config, log);
|
|
383
550
|
const queueTimer = setInterval(() => {
|
|
@@ -397,14 +564,70 @@ export async function boot(config, options = {}) {
|
|
|
397
564
|
const scheduleTimer = setInterval(() => {
|
|
398
565
|
void scheduleTicks.tick();
|
|
399
566
|
}, 15_000);
|
|
567
|
+
// A step waiting out a rate limit or a timeout runs again once its wait is over; the wait
|
|
568
|
+
// is in the database, so this also picks up the ones a restart left waiting.
|
|
569
|
+
const retryTimer = setInterval(() => {
|
|
570
|
+
const release = updater.beginMutation();
|
|
571
|
+
if (!release)
|
|
572
|
+
return;
|
|
573
|
+
try {
|
|
574
|
+
wakeRetries(updateDb, clock, runner);
|
|
575
|
+
}
|
|
576
|
+
catch (error) {
|
|
577
|
+
log.write("error", "stage.retry", { detail: causedBy(error) });
|
|
578
|
+
}
|
|
579
|
+
finally {
|
|
580
|
+
release();
|
|
581
|
+
}
|
|
582
|
+
}, 5_000);
|
|
400
583
|
void scheduleTicks.tick();
|
|
401
584
|
// Once a minute is plenty for a daily slot; the decision itself (slices/backups/schedule)
|
|
402
585
|
// holds the first run back for a couple of minutes after a start.
|
|
586
|
+
// The model catalogue keeps itself current: once at start, then whenever a day has passed
|
|
587
|
+
// since the last check (looked at hourly, so a laptop that slept catches up).
|
|
588
|
+
const syncModels = () => {
|
|
589
|
+
if (!options.refreshModels || !catalogue.sync)
|
|
590
|
+
return;
|
|
591
|
+
catalogue.sync().then((status) => {
|
|
592
|
+
if (status.warning !== null)
|
|
593
|
+
log.write("warn", "model-catalog.sync", { detail: status.warning });
|
|
594
|
+
}, (error) => log.write("warn", "model-catalog.sync", { detail: causedBy(error) }));
|
|
595
|
+
};
|
|
596
|
+
syncModels();
|
|
597
|
+
const modelTimer = setInterval(() => {
|
|
598
|
+
if (catalogue.syncDue?.(Date.now()) === true)
|
|
599
|
+
syncModels();
|
|
600
|
+
}, 60 * 60_000);
|
|
403
601
|
const backupTimer = setInterval(() => {
|
|
404
602
|
backups.tick().catch((error) => {
|
|
405
603
|
log.write("error", "backups.tick", { detail: causedBy(error) });
|
|
406
604
|
});
|
|
407
605
|
}, 60_000);
|
|
606
|
+
// Settings → Trash keeps deleted items 30 days; once a day (looked at hourly, and once at
|
|
607
|
+
// start) whatever is older goes for good, a project's folder with it.
|
|
608
|
+
const trashPurge = createTrashPurge({
|
|
609
|
+
db: updateDb,
|
|
610
|
+
paths,
|
|
611
|
+
clock,
|
|
612
|
+
log,
|
|
613
|
+
hasInflight: runner.hasInflight,
|
|
614
|
+
});
|
|
615
|
+
const purgeTrash = () => {
|
|
616
|
+
const release = updater.beginMutation();
|
|
617
|
+
if (!release)
|
|
618
|
+
return;
|
|
619
|
+
try {
|
|
620
|
+
trashPurge.tick();
|
|
621
|
+
}
|
|
622
|
+
catch (error) {
|
|
623
|
+
log.write("error", "trash.purge", { detail: causedBy(error) });
|
|
624
|
+
}
|
|
625
|
+
finally {
|
|
626
|
+
release();
|
|
627
|
+
}
|
|
628
|
+
};
|
|
629
|
+
purgeTrash();
|
|
630
|
+
const trashTimer = setInterval(purgeTrash, 60 * 60_000);
|
|
408
631
|
listeningPort = portOf(server) ?? config.port;
|
|
409
632
|
// Whatever last run left queued goes out at start. Nothing waits for
|
|
410
633
|
// it, and an unreachable collector costs one refused socket.
|
|
@@ -427,11 +650,16 @@ export async function boot(config, options = {}) {
|
|
|
427
650
|
shutdown = () => {
|
|
428
651
|
stopping ??= (async () => {
|
|
429
652
|
notifier.close();
|
|
653
|
+
episodes?.close();
|
|
430
654
|
clearInterval(queueTimer);
|
|
431
655
|
clearInterval(scheduleTimer);
|
|
656
|
+
clearInterval(retryTimer);
|
|
432
657
|
clearInterval(backupTimer);
|
|
658
|
+
clearInterval(trashTimer);
|
|
659
|
+
clearInterval(modelTimer);
|
|
433
660
|
const mutationDrain = mutations.stop();
|
|
434
661
|
const scheduleDrain = scheduleTicks.stop();
|
|
662
|
+
const topicDrain = scheduleRunner.stop();
|
|
435
663
|
// A backup being written is stopped and its partial file removed, not waited for.
|
|
436
664
|
const backupDrain = backups.stop();
|
|
437
665
|
const serverClose = beginServerClose(server);
|
|
@@ -443,8 +671,10 @@ export async function boot(config, options = {}) {
|
|
|
443
671
|
// database until their response settles; anything racing shutdown receives 503.
|
|
444
672
|
await drainMutationsWithDeadline(mutationDrain, serverClose.terminate, mutationDrainTimeoutMs);
|
|
445
673
|
await scheduleDrain;
|
|
674
|
+
await topicDrain;
|
|
446
675
|
await backupDrain;
|
|
447
676
|
await runner.abortAll();
|
|
677
|
+
await episodes?.settled();
|
|
448
678
|
}
|
|
449
679
|
finally {
|
|
450
680
|
serverClose.terminate();
|
|
@@ -468,7 +698,7 @@ export async function boot(config, options = {}) {
|
|
|
468
698
|
};
|
|
469
699
|
if (pendingActivation)
|
|
470
700
|
stopActivation = watchActivation(updater, candidateToken, shutdown, (message) => log.write("warn", "update", { detail: message }));
|
|
471
|
-
return { paths, url: urlOf(config.host, listeningPort), stop: shutdown };
|
|
701
|
+
return { paths, url: urlOf(config.host, listeningPort), stop: shutdown, autostart };
|
|
472
702
|
}
|
|
473
703
|
catch (error) {
|
|
474
704
|
db?.close();
|
|
@@ -476,7 +706,7 @@ export async function boot(config, options = {}) {
|
|
|
476
706
|
throw error;
|
|
477
707
|
}
|
|
478
708
|
}
|
|
479
|
-
export function wireRunner({ audioPreviews, db, paths, clock, ids, log, hub, telemetry, flusher, registry, catalogue, ffmpeg, }) {
|
|
709
|
+
export function wireRunner({ onFinished, audioPreviews, db, paths, clock, ids, log, hub, telemetry, flusher, registry, catalogue, ffmpeg, alignSubtitles: aligner = alignSubtitles, }) {
|
|
480
710
|
// A stage counts what it did and the queue is flushed after each new event. `record`
|
|
481
711
|
// swallows its own failures, so this can neither fail a stage nor widen what leaves the
|
|
482
712
|
// machine - the payload allow-list is checked inside it.
|
|
@@ -484,7 +714,17 @@ export function wireRunner({ audioPreviews, db, paths, clock, ids, log, hub, tel
|
|
|
484
714
|
record(telemetry, type, counters);
|
|
485
715
|
flusher.soon();
|
|
486
716
|
};
|
|
487
|
-
const execution = {
|
|
717
|
+
const execution = {
|
|
718
|
+
db,
|
|
719
|
+
paths,
|
|
720
|
+
ids,
|
|
721
|
+
clock,
|
|
722
|
+
log,
|
|
723
|
+
ffmpeg,
|
|
724
|
+
alignSubtitles: aligner,
|
|
725
|
+
audioPreviews,
|
|
726
|
+
count,
|
|
727
|
+
};
|
|
488
728
|
// A stage slice is handed the wrapped calls, never the registry: every provider call
|
|
489
729
|
// it makes is already inside the retry policy (kernel/runner/providers.ts).
|
|
490
730
|
const providers = {
|
|
@@ -492,12 +732,23 @@ export function wireRunner({ audioPreviews, db, paths, clock, ids, log, hub, tel
|
|
|
492
732
|
attempts: sqliteAttempts(db, ids),
|
|
493
733
|
clock,
|
|
494
734
|
log,
|
|
735
|
+
meter: createUsageMeter({ db, ids, clock, catalogue: () => catalogue.read() }),
|
|
736
|
+
limits: createLimitGate({
|
|
737
|
+
db,
|
|
738
|
+
clock,
|
|
739
|
+
log,
|
|
740
|
+
changed: (projectId) => hub.emit(projectId, { type: "project.updated", projectId }),
|
|
741
|
+
}),
|
|
495
742
|
queue: createProviderQueue((provider) => isLocalCliProvider(provider)
|
|
496
743
|
? localCliConcurrency(provider)
|
|
497
744
|
: (catalogue.read().providers[provider]?.maxConcurrent ?? 1)),
|
|
498
745
|
};
|
|
499
746
|
const checkpoints = createCheckpointAuthority({
|
|
500
|
-
|
|
747
|
+
// A review waiting to send its item back holds the item's dependents like a checkpoint.
|
|
748
|
+
decide: (work) => {
|
|
749
|
+
const review = reviewHold(execution, work);
|
|
750
|
+
return review.kind === "held" ? review : checkpointDecisionForWork(execution, work);
|
|
751
|
+
},
|
|
501
752
|
approve: (projectId, checkpointId, identity) => approveCheckpoint(db, {
|
|
502
753
|
...identity,
|
|
503
754
|
projectId,
|
|
@@ -518,20 +769,30 @@ export function wireRunner({ audioPreviews, db, paths, clock, ids, log, hub, tel
|
|
|
518
769
|
},
|
|
519
770
|
standingsOf: (projectId) => executionStandings(execution, projectId),
|
|
520
771
|
ready: (work) => invocationReady(execution, work),
|
|
521
|
-
|
|
772
|
+
// A project in the trash (Settings → Trash) holds like a paused one until it is restored.
|
|
773
|
+
paused: (projectId) => projectPaused(db, projectId) ||
|
|
774
|
+
queueWaiting(db, projectId) ||
|
|
775
|
+
projectTrashed(db, projectId),
|
|
522
776
|
claim: (work) => {
|
|
523
777
|
const claimed = claimWork(db, work);
|
|
524
778
|
projectStandings(execution, work.projectId);
|
|
525
779
|
return claimed;
|
|
526
780
|
},
|
|
527
781
|
maySubmit: (work, pieceId) => maySubmit(db, work, pieceId),
|
|
528
|
-
|
|
782
|
+
waitToRetry: (work, fault, reason) => transact(db, () => {
|
|
783
|
+
const at = waitToRetry(execution, work, fault, reason, Math.random);
|
|
784
|
+
if (at !== undefined)
|
|
785
|
+
projectStandings(execution, work.projectId);
|
|
786
|
+
return at;
|
|
787
|
+
}),
|
|
788
|
+
finish: (work, state, reason, kind) => {
|
|
529
789
|
transact(db, () => {
|
|
530
|
-
finishWork(db, work, state, reason);
|
|
790
|
+
finishWork(db, work, state, reason, kind ?? null);
|
|
531
791
|
materializeAdmittedWork(execution, work.projectId);
|
|
532
792
|
projectStandings(execution, work.projectId);
|
|
533
793
|
settleReleasedCheckpoints(execution, work.projectId);
|
|
534
794
|
});
|
|
795
|
+
onFinished?.(work);
|
|
535
796
|
},
|
|
536
797
|
},
|
|
537
798
|
runs: Object.fromEntries(stageKinds.map((kind) => [
|
package/dist/model-catalog.js
CHANGED
|
@@ -4,6 +4,7 @@ import { openAiImageModels } from "./adapters/image/openai.js";
|
|
|
4
4
|
import { replicateModels } from "./adapters/image/replicate.js";
|
|
5
5
|
import { cartesiaModels } from "./adapters/tts/cartesia.js";
|
|
6
6
|
import { elevenLabsModels } from "./adapters/tts/elevenlabs.js";
|
|
7
|
+
import { geminiTtsModels } from "./adapters/tts/gemini.js";
|
|
7
8
|
import { inworldModels } from "./adapters/tts/inworld.js";
|
|
8
9
|
import { openAiTtsModels } from "./adapters/tts/openai.js";
|
|
9
10
|
// Composition keeps provider-specific fallback data out of the HTTP and settings layers.
|
|
@@ -13,6 +14,7 @@ export function modelSources(registry) {
|
|
|
13
14
|
"openai-tts": openAiTtsModels,
|
|
14
15
|
cartesia: cartesiaModels,
|
|
15
16
|
inworld: inworldModels,
|
|
17
|
+
"google-tts": geminiTtsModels,
|
|
16
18
|
fal: falModels,
|
|
17
19
|
replicate: replicateModels,
|
|
18
20
|
"openai-image": openAiImageModels,
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Slopify 0.4.2 to 1.0
|
|
2
|
+
|
|
3
|
+
Versions 0.4.3 to 1.0.0, released 9 to 13 September 2026 (0.4.2 shipped on 3 September).
|
|
4
|
+
|
|
5
|
+
This is the stretch where Slopify went from a pipeline that ran once, start to finish, to something you can live with: pause it, change your mind halfway, edit a finished project without paying for everything twice, and leave it running on a schedule. It also picked up free local captions, the Gemini CLI, Inworld voices and an updater. 1.0.0 is the first release where a finished project is never a dead end.
|
|
6
|
+
|
|
7
|
+
## Highlights
|
|
8
|
+
|
|
9
|
+
- Captions, made on your machine for free: English word timing with a 95 MB speech model, no caption service and no key.
|
|
10
|
+
- Edit any finished project. Slopify keeps every version and remakes only what your change affects.
|
|
11
|
+
- Pause and resume a run, and switch its providers while it is paused.
|
|
12
|
+
- A cost review before anything starts, with the exact inputs, prices and a readiness check.
|
|
13
|
+
- Review checkpoints hold Audio, Images or Video until you approve them, while the rest keeps going.
|
|
14
|
+
- Templates for setups you reuse, and daily, weekly or one-off schedules that run them.
|
|
15
|
+
- Gemini CLI joins Claude Code and Codex as a text provider, using the login you already have.
|
|
16
|
+
- Inworld TTS-2 and TTS-2 Flash voices.
|
|
17
|
+
- Update from inside the app with one click.
|
|
18
|
+
|
|
19
|
+
## What's new
|
|
20
|
+
|
|
21
|
+
### Getting started
|
|
22
|
+
- An interactive tutorial walks from API keys to a downloaded video in 18 steps (19 from 0.5.1), highlighting the real controls on each screen. Open it from Tutorial in the navigation or Start tutorial on an empty Projects page.
|
|
23
|
+
- The tutorial remembers where you were across reloads and never starts a run for you.
|
|
24
|
+
- Two ways to install on slopify.stream: `npx @gentbajko/slopify@latest` with nothing to install, or `npm install -g @gentbajko/slopify` and then `slopify`.
|
|
25
|
+
|
|
26
|
+
### Runs
|
|
27
|
+
- Pause and Resume. Pausing stops active work, keeps every finished piece and survives an app restart.
|
|
28
|
+
- Article is the only required stage. Research, Audio, Images, Thumbnail and Video can each be Off.
|
|
29
|
+
- With Images off and Audio on, the run exports a 48 kHz stereo WAV. With Audio off, it renders a silent MP4 at 5 seconds per image.
|
|
30
|
+
- Images from a saved prompt and prewritten thumbnails start straight away, beside research and writing.
|
|
31
|
+
- Uploaded narration is used as it is.
|
|
32
|
+
- Play is four sections, with a preview of the output shape and caption style next to the controls.
|
|
33
|
+
- Play drafts save to this computer as you type, uploads included, and come back after a restart.
|
|
34
|
+
- Review shows the exact inputs, prices and catalogue date before you start. Nothing runs until Start.
|
|
35
|
+
- Review also checks that every provider, model and voice the run needs is ready.
|
|
36
|
+
- Batches: several title or keyword variants from one setup, run one after another.
|
|
37
|
+
- Provider-aware queues cap how many requests go to each provider at once.
|
|
38
|
+
|
|
39
|
+
### Editing finished projects
|
|
40
|
+
- Change the settings, article, narration, images or captions of any project, finished or not.
|
|
41
|
+
- Every save is a new version in History. Restore any earlier one.
|
|
42
|
+
- Before a rebuild, Slopify lists the outputs your change affects and what they cost. Work that is unaffected is reused, not paid for again.
|
|
43
|
+
- Each project keeps its own copy of the prompts it was made with, so editing a Library prompt never changes an old project.
|
|
44
|
+
- Edit a single image, a caption line, or the article text by hand.
|
|
45
|
+
|
|
46
|
+
### Review checkpoints
|
|
47
|
+
- Hold Audio, Images or Video/export for approval. Independent work carries on while one part waits.
|
|
48
|
+
- Approve held work from the project's progress view. Approvals survive restarts and later edits.
|
|
49
|
+
|
|
50
|
+
### Templates and schedules
|
|
51
|
+
- Save a project's setup as a named template and apply it in Play. Templates keep versions, so an old draft is never changed under you.
|
|
52
|
+
- Schedules run a template once, daily or weekly, in your time zone, with up to 49 keyword variants and an optional spend limit.
|
|
53
|
+
- Each schedule has a run history and Pause, Resume, Cancel and Delete. Missed runs follow a policy you choose, and a run never overlaps the one before it.
|
|
54
|
+
|
|
55
|
+
### Captions and fonts
|
|
56
|
+
- Free English captions for new and existing narrated projects, timed word by word to the actual speech.
|
|
57
|
+
- SRT, VTT and ASS files, and captions burned into the video.
|
|
58
|
+
- Five caption positions, a font size and a live preview in the real output shape.
|
|
59
|
+
- Bundled Barlow, your installed fonts, or upload a TTF or OTF up to 32 MiB.
|
|
60
|
+
- Changing only the caption style reuses the narration and the WAV instead of making them again.
|
|
61
|
+
- When the narration skips a short passage, captions resync on the following words and add a note beside the caption download saying what was missed.
|
|
62
|
+
|
|
63
|
+
### Narration
|
|
64
|
+
- Inworld TTS-2 and TTS-2 Flash. TTS-2 takes up to 100,000 characters per job.
|
|
65
|
+
- Split narration by characters as well as by paragraph or word count: 3,000 characters by default, always on a sentence end.
|
|
66
|
+
- Hear narration while it is still being made, and watch the article write itself.
|
|
67
|
+
|
|
68
|
+
### Providers and models
|
|
69
|
+
- Gemini CLI as a text and research provider, signed in with the login you already have.
|
|
70
|
+
- Set the path to each CLI in Settings when Slopify can't find it on your PATH.
|
|
71
|
+
- Model lists load from the provider or the installed CLI, with a refresh button and space to type a model ID.
|
|
72
|
+
- A curated model catalogue with prices, context sizes and thinking settings, refreshed from GitHub.
|
|
73
|
+
|
|
74
|
+
### Project page
|
|
75
|
+
- A focused workspace: stage list on the left, the output of the chosen stage beside it, and a direct download of the final file.
|
|
76
|
+
- Overall progress next to each stage's own progress.
|
|
77
|
+
- Open folder next to every download.
|
|
78
|
+
|
|
79
|
+
### Updates and upkeep
|
|
80
|
+
- An update button checks every 15 minutes and installs on one click. It waits for active work, checks the new version starts, and rolls the database back if it doesn't.
|
|
81
|
+
- Settings exports and imports a backup of your templates, prompts, voices and settings. Provider keys are never included.
|
|
82
|
+
- Disk usage per project, cleanup of files no project uses, and a diagnostics download with no secrets in it.
|
|
83
|
+
|
|
84
|
+
## Fixes
|
|
85
|
+
|
|
86
|
+
- A missing ffmpeg is downloaded into the app's data folder and checked before any provider work starts.
|
|
87
|
+
- Claude Code no longer applies your personal coding setup to article writing.
|
|
88
|
+
- Google's image errors are read correctly; an image quota of zero fails once with a clear message instead of retrying.
|
|
89
|
+
- Slideshow zoom is faster and travels from 100% to 122.5%.
|
|
90
|
+
- The update indicator no longer sticks on Updating after a check finds nothing new.
|
|
91
|
+
- A stalled or interrupted update recovers its status on the next start.
|
|
92
|
+
- Many fixes to editing and rebuilds: regenerated articles open current, uploaded audio can be replaced by generated narration, and interrupted rebuilds recover their unfinished outputs.
|
|
93
|
+
- Many fixes to Play drafts: unfinished uploads, image counts, empty chunk sizes and discarded drafts are all kept or recovered correctly.
|