@gentbajko/slopify 2.5.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -21
- package/NOTICE +12 -0
- package/README.md +21 -40
- package/SUBTITLES.md +1 -1
- package/dist/adapter-registry.js +3 -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 +36 -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/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/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/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/catalog/merge.js +132 -0
- package/dist/catalog/registry.js +13 -13
- package/dist/catalog/schema.js +2 -0
- package/dist/catalog/store.js +147 -8
- package/dist/compose.yaml +71 -0
- package/dist/edge/autostart/docker-record.js +32 -0
- package/dist/edge/autostart/docker.js +50 -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 +66 -0
- package/dist/edge/autostart/service.js +81 -0
- package/dist/edge/cli.js +57 -51
- 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 +154 -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/host-cli.js +1 -1
- package/dist/edge/http/actions.js +32 -10
- package/dist/edge/http/app.js +58 -2
- package/dist/edge/http/auditions.js +70 -0
- package/dist/edge/http/autostart.js +37 -0
- package/dist/edge/http/channel-memory.js +92 -0
- package/dist/edge/http/channels.js +128 -0
- package/dist/edge/http/entries.js +22 -3
- package/dist/edge/http/files.js +1 -1
- 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 +152 -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/projects.js +31 -99
- package/dist/edge/http/prompts.js +40 -3
- package/dist/edge/http/pronunciations.js +23 -4
- package/dist/edge/http/providers.js +86 -0
- package/dist/edge/http/reviews.js +121 -0
- package/dist/edge/http/revision-files.js +1 -1
- package/dist/edge/http/revisions.js +1 -1
- package/dist/edge/http/run-cost.js +25 -0
- package/dist/edge/http/schedules.js +111 -6
- package/dist/edge/http/settings.js +84 -6
- package/dist/edge/http/storage-files.js +50 -0
- package/dist/edge/http/storage.js +29 -2
- package/dist/edge/http/studio.js +207 -0
- package/dist/edge/http/style-preview.js +100 -0
- package/dist/edge/http/subtitles.js +1 -1
- package/dist/edge/http/trash.js +69 -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 +83 -0
- package/dist/edge/native-update.js +62 -0
- package/dist/host-cli/open-folder.js +10 -10
- package/dist/host-cli/runtime.js +9 -1
- 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/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 +5 -2
- package/dist/kernel/ports/narration-aliases.js +60 -0
- package/dist/kernel/ports/plan-limits.js +36 -0
- package/dist/kernel/runner/attempt.js +17 -8
- package/dist/kernel/runner/audition.js +36 -0
- package/dist/kernel/runner/graph.js +24 -5
- package/dist/kernel/runner/index.js +35 -5
- 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 +272 -16
- 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/index.json +26 -0
- package/dist/slices/admission/model.js +27 -0
- package/dist/slices/admission/repo.js +24 -4
- package/dist/slices/admission/rules.js +79 -1
- package/dist/slices/admission/schema.js +58 -1
- package/dist/slices/admission/short-mode.js +47 -0
- package/dist/slices/admission/start.js +41 -5
- 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 +218 -0
- package/dist/slices/channels/runs.js +129 -0
- package/dist/slices/channels/schema.js +129 -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 +2 -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 +60 -2
- 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 +183 -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 +21 -3
- package/dist/slices/notifications/rules.js +22 -1
- package/dist/slices/onboarding/copy.js +143 -0
- package/dist/slices/onboarding/first-run.js +46 -0
- package/dist/slices/onboarding/install.js +119 -0
- package/dist/slices/onboarding/model.js +64 -0
- package/dist/slices/onboarding/packs.js +491 -0
- package/dist/slices/onboarding/quick-short.js +129 -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 +125 -6
- package/dist/slices/play-drafts/readiness.js +3 -0
- 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 +50 -4
- package/dist/slices/project-templates/one-off.js +26 -0
- package/dist/slices/project-templates/repo.js +2 -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 +62 -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 +110 -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 +237 -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 +39 -10
- 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/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 +106 -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 +234 -0
- package/dist/slices/run-cost/pricing.js +92 -0
- package/dist/slices/run-cost/week.js +108 -0
- package/dist/slices/schedules/agenda.js +158 -0
- package/dist/slices/schedules/repo.js +58 -18
- package/dist/slices/schedules/scheduler.js +67 -16
- package/dist/slices/schedules/schema.js +138 -1
- package/dist/slices/schedules/service.js +43 -4
- package/dist/slices/schedules/similar.js +90 -0
- package/dist/slices/schedules/topic-list.js +163 -0
- package/dist/slices/schedules/topics.js +422 -0
- package/dist/slices/settings/first-run.js +102 -0
- package/dist/slices/settings/health.js +190 -0
- package/dist/slices/settings/key-guides.js +128 -0
- package/dist/slices/settings/key-test.js +85 -0
- package/dist/slices/settings/model.js +6 -1
- package/dist/slices/settings/playback.js +15 -0
- package/dist/slices/settings/repo.js +17 -3
- 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 +54 -5
- 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 +79 -0
- 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 +92 -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 +28 -0
- package/dist/slices/studio/pack.js +209 -0
- package/dist/slices/studio/settings.js +133 -0
- package/dist/slices/style-preview/images.js +61 -0
- package/dist/slices/style-preview/render.js +161 -0
- package/dist/slices/style-preview/schema.js +45 -0
- package/dist/slices/style-preview/service.js +93 -0
- package/dist/slices/style-preview/settings.js +89 -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/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 +82 -0
- package/dist/slices/voices/audition.js +25 -0
- package/dist/slices/voices/cast.js +56 -0
- package/dist/slices/voices/grouping.js +49 -0
- package/dist/slices/voices/join.js +35 -0
- package/dist/slices/voices/languages.js +25 -0
- package/dist/slices/voices/model.js +179 -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 +69 -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/updater/service.js +66 -21
- package/dist/web/assets/index-CUA2sUxU.css +1 -0
- package/dist/web/assets/index-CX590kE-.js +303 -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-BG8n_uc5.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +4 -4
- 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,57 @@ 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";
|
|
46
53
|
import { createNotificationSender } from "./slices/notifications/send.js";
|
|
47
54
|
import { readNotificationUrl } from "./slices/notifications/settings.js";
|
|
55
|
+
import { seedSamples } from "./slices/onboarding/sample.js";
|
|
48
56
|
import { templateById } from "./slices/project-templates/repo.js";
|
|
57
|
+
import { recoverProject } from "./slices/rebuild/recovery.js";
|
|
49
58
|
import { claimWork, finishWork, maySubmit } from "./slices/rebuild/repo.js";
|
|
59
|
+
import { waitToRetry, wakeRetries } from "./slices/rebuild/retry.js";
|
|
60
|
+
import { createReviewRedos, reviewHold } from "./slices/rebuild/review-redo.js";
|
|
50
61
|
import { materializeAdmittedWork } from "./slices/rebuild/runtime-materialize.js";
|
|
51
62
|
import { runRevisionInvocation } from "./slices/rebuild/runtime-run.js";
|
|
52
63
|
import { executionStages, executionStandings, invocationReady, projectStandings, recordWorkProgress, } from "./slices/rebuild/runtime-store.js";
|
|
64
|
+
import { currentRevisionId } from "./slices/revisions/repo.js";
|
|
65
|
+
import { createLimitGate, resumeAfterRestart } from "./slices/run-cost/limits.js";
|
|
66
|
+
import { createStandaloneMeter, createUsageMeter } from "./slices/run-cost/meter.js";
|
|
53
67
|
import { settleTerminalScheduleRuns } from "./slices/schedules/repo.js";
|
|
54
68
|
import { createScheduleRunner } from "./slices/schedules/scheduler.js";
|
|
55
69
|
import { nodeCliProbe } from "./slices/settings/cli-status.js";
|
|
56
70
|
import { isLocalCliProvider, localCliConcurrency } from "./slices/settings/model.js";
|
|
57
71
|
import { providerStatuses } from "./slices/settings/readiness.js";
|
|
72
|
+
import { busyProjects } from "./slices/storage/backup-export.js";
|
|
73
|
+
import { documentsDir, nodeDocumentsHost } from "./slices/storage/documents.js";
|
|
74
|
+
import { createFilesService, ensureFilesFolders, filesLayoutOf, settleFilesLocation, } from "./slices/storage/files-location.js";
|
|
58
75
|
import { reconcileStorage } from "./slices/storage/reconcile.js";
|
|
76
|
+
import { previewPictures } from "./slices/style-preview/images.js";
|
|
77
|
+
import { ffmpegStylePreview } from "./slices/style-preview/render.js";
|
|
78
|
+
import { createStylePreviews, stylePreviewDir } from "./slices/style-preview/service.js";
|
|
59
79
|
import { collectorEndpoint, httpPostEvents } from "./slices/telemetry/collector-client.js";
|
|
60
80
|
import { createFlusher } from "./slices/telemetry/flush.js";
|
|
61
81
|
import { record } from "./slices/telemetry/record.js";
|
|
82
|
+
import { createTrashPurge } from "./slices/trash/service.js";
|
|
62
83
|
import { probeDurationMs } from "./slices/video/ffmpeg.js";
|
|
63
84
|
import { watchActivation } from "./updater/candidate.js";
|
|
64
85
|
import { launchUpdate } from "./updater/install.js";
|
|
@@ -125,11 +146,12 @@ export async function boot(config, options = {}) {
|
|
|
125
146
|
if (dockerState !== undefined &&
|
|
126
147
|
(process.env.SLOPIFY_CONTAINER !== "1" ||
|
|
127
148
|
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
|
|
149
|
+
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
150
|
const candidateToken = process.env.SLOPIFY_UPDATE_TOKEN ?? "";
|
|
130
151
|
const pendingActivation = isUpdateToken(candidateToken) && process.env.SLOPIFY_UPDATE_PENDING === "1";
|
|
131
|
-
|
|
152
|
+
ensureDataDirs(paths, { mode: 0o700 });
|
|
132
153
|
const lock = acquireInstanceLock(paths.lock);
|
|
154
|
+
const freshInstall = !existsSync(paths.db);
|
|
133
155
|
let db;
|
|
134
156
|
try {
|
|
135
157
|
const ffmpeg = await prepareFfmpeg({
|
|
@@ -140,10 +162,29 @@ export async function boot(config, options = {}) {
|
|
|
140
162
|
});
|
|
141
163
|
db = openDb(paths.db);
|
|
142
164
|
migrate(db, clock);
|
|
165
|
+
// Where the projects, backups and exports live. A container's are the installer's mounts;
|
|
166
|
+
// a native install decides once and remembers (slices/storage/files-location.ts).
|
|
167
|
+
const container = process.env.SLOPIFY_CONTAINER === "1";
|
|
168
|
+
const files = container
|
|
169
|
+
? dockerFilesLayout(process.env, paths.dataDir)
|
|
170
|
+
: filesLayoutOf(await settleFilesLocation({
|
|
171
|
+
db,
|
|
172
|
+
dataDir: paths.dataDir,
|
|
173
|
+
fresh: freshInstall,
|
|
174
|
+
documents: options.filesInDocuments === true
|
|
175
|
+
? () => documentsDir(nodeDocumentsHost())
|
|
176
|
+
: undefined,
|
|
177
|
+
}), paths.dataDir);
|
|
178
|
+
repoint(paths, files);
|
|
179
|
+
if (files.exports === null)
|
|
180
|
+
ensureDirs(paths, { mode: 0o700 });
|
|
181
|
+
else
|
|
182
|
+
await ensureFilesFolders(files);
|
|
143
183
|
const runtimeDb = db;
|
|
144
184
|
const interrupted = markInterruptedStages(db, clock);
|
|
145
185
|
recoverCheckpointWork(db);
|
|
146
186
|
const settledSchedules = settleTerminalScheduleRuns(db, clock.now().toISOString());
|
|
187
|
+
settleInterruptedCastImages({ db });
|
|
147
188
|
// The updater can restore the database after a failed candidate boot, but it cannot
|
|
148
189
|
// restore files deleted by reconciliation. Leave the filesystem untouched until the
|
|
149
190
|
// candidate's committed activation pointer has been verified.
|
|
@@ -167,11 +208,17 @@ export async function boot(config, options = {}) {
|
|
|
167
208
|
send: sendNotification,
|
|
168
209
|
log,
|
|
169
210
|
});
|
|
211
|
+
// Episode memory summarises a project that has just finished; wired once the providers
|
|
212
|
+
// exist, below.
|
|
213
|
+
let episodes;
|
|
170
214
|
const hub = observedHub(createHub({
|
|
171
215
|
ids,
|
|
172
216
|
log,
|
|
173
217
|
acceptEvent: (event) => currentProjectEvent(eventDb, event),
|
|
174
|
-
}),
|
|
218
|
+
}), (event) => {
|
|
219
|
+
notifier.observe(event);
|
|
220
|
+
episodes?.observe(event);
|
|
221
|
+
});
|
|
175
222
|
const version = readVersion();
|
|
176
223
|
const telemetry = { db, ids, clock, log, appVersion: version };
|
|
177
224
|
const flusher = createFlusher({
|
|
@@ -192,8 +239,26 @@ export async function boot(config, options = {}) {
|
|
|
192
239
|
probe: nodeCliProbe,
|
|
193
240
|
hostCli,
|
|
194
241
|
}), catalogue);
|
|
242
|
+
// Calls that belong to no project (topic generation, episode summaries, cast pictures)
|
|
243
|
+
// still go through the attempt wrapper, and are metered against their schedule or channel.
|
|
244
|
+
const standalone = {
|
|
245
|
+
registry,
|
|
246
|
+
clock,
|
|
247
|
+
log,
|
|
248
|
+
meter: createStandaloneMeter({ db, ids, clock, catalogue: () => catalogue.read() }),
|
|
249
|
+
};
|
|
250
|
+
episodes = createEpisodeMemoryWatcher({
|
|
251
|
+
db,
|
|
252
|
+
paths,
|
|
253
|
+
clock,
|
|
254
|
+
log,
|
|
255
|
+
uuid: randomUUID,
|
|
256
|
+
llm: (call) => standaloneLlm(standalone, call),
|
|
257
|
+
});
|
|
195
258
|
const audioPreviews = createAudioPreviewStore();
|
|
259
|
+
const reviewRedos = createReviewRedos();
|
|
196
260
|
const runner = wireRunner({
|
|
261
|
+
onFinished: (work) => reviewRedos.kick(work.projectId),
|
|
197
262
|
db,
|
|
198
263
|
paths,
|
|
199
264
|
clock,
|
|
@@ -257,7 +322,7 @@ export async function boot(config, options = {}) {
|
|
|
257
322
|
? Promise.resolve(version)
|
|
258
323
|
: publishedVersion(globalThis.fetch),
|
|
259
324
|
unsupported: () => process.env.SLOPIFY_DISABLE_UPDATES === "1"
|
|
260
|
-
? "
|
|
325
|
+
? "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
326
|
: !existsSync(oldEntry) || !existsSync(workerEntry)
|
|
262
327
|
? "Run Slopify from its installed package to use in-app updates."
|
|
263
328
|
: npm === undefined
|
|
@@ -269,6 +334,18 @@ export async function boot(config, options = {}) {
|
|
|
269
334
|
.prepare("SELECT id FROM projects")
|
|
270
335
|
.all()
|
|
271
336
|
.some((row) => typeof row.id === "string" && runner.hasInflight?.(row.id) === true),
|
|
337
|
+
busyWith: () => {
|
|
338
|
+
const running = updateDb
|
|
339
|
+
.prepare("SELECT p.title FROM stages s JOIN projects p ON p.id=s.project_id WHERE s.state='running' LIMIT 1")
|
|
340
|
+
.get();
|
|
341
|
+
if (typeof running?.title === "string")
|
|
342
|
+
return running.title;
|
|
343
|
+
const inflight = updateDb
|
|
344
|
+
.prepare("SELECT id,title FROM projects")
|
|
345
|
+
.all()
|
|
346
|
+
.find((row) => typeof row.id === "string" && runner.hasInflight?.(row.id) === true);
|
|
347
|
+
return typeof inflight?.title === "string" ? inflight.title : undefined;
|
|
348
|
+
},
|
|
272
349
|
report: (message) => log.write("warn", "update", { detail: message }),
|
|
273
350
|
install: async (next, restarting) => {
|
|
274
351
|
if (npm === undefined)
|
|
@@ -302,6 +379,21 @@ export async function boot(config, options = {}) {
|
|
|
302
379
|
modelsFor: modelSources(registry).modelsFor,
|
|
303
380
|
emit: (projectId, event) => hub.emit(projectId, event),
|
|
304
381
|
};
|
|
382
|
+
// Redos a review asked for before the last shutdown start now.
|
|
383
|
+
reviewRedos.bind(rebuild);
|
|
384
|
+
reviewRedos.kick();
|
|
385
|
+
// A stage that was waiting for a CLI's plan limits when the app stopped carries on waiting.
|
|
386
|
+
void resumeAfterRestart(db, async (projectId) => {
|
|
387
|
+
const baseRevisionId = currentRevisionId(runtimeDb, projectId);
|
|
388
|
+
if (baseRevisionId === undefined)
|
|
389
|
+
return false;
|
|
390
|
+
const result = await recoverProject(rebuild, projectId, {
|
|
391
|
+
baseRevisionId,
|
|
392
|
+
idempotencyKey: randomUUID(),
|
|
393
|
+
action: { kind: "resume" },
|
|
394
|
+
});
|
|
395
|
+
return result.ok;
|
|
396
|
+
}, log);
|
|
305
397
|
const draftDeps = {
|
|
306
398
|
db,
|
|
307
399
|
paths,
|
|
@@ -321,11 +413,20 @@ export async function boot(config, options = {}) {
|
|
|
321
413
|
flusher.soon();
|
|
322
414
|
},
|
|
323
415
|
};
|
|
324
|
-
const
|
|
416
|
+
const scheduleRunnerDeps = {
|
|
325
417
|
...draftDeps,
|
|
326
418
|
template: (id, templateVersion) => templateById(runtimeDb, id, templateVersion),
|
|
419
|
+
topicLlm: (call) => standaloneLlm(standalone, call),
|
|
420
|
+
topicsWaiting: (event) => {
|
|
421
|
+
hub.emitGlobal(event);
|
|
422
|
+
notifier.observeTopics(event);
|
|
423
|
+
},
|
|
424
|
+
};
|
|
425
|
+
const scheduleRunner = createScheduleRunner(scheduleRunnerDeps);
|
|
426
|
+
const scheduleDeps = {
|
|
427
|
+
...scheduleRunnerDeps,
|
|
428
|
+
requestTopics: scheduleRunner.requestTopics,
|
|
327
429
|
};
|
|
328
|
-
const scheduleRunner = createScheduleRunner(scheduleDeps);
|
|
329
430
|
scheduleRunner.recover(clock.now());
|
|
330
431
|
const scheduleTicks = createScheduleTickLifecycle({
|
|
331
432
|
beginMutation: updater.beginMutation,
|
|
@@ -336,6 +437,23 @@ export async function boot(config, options = {}) {
|
|
|
336
437
|
});
|
|
337
438
|
const mutations = createMutationLifecycle();
|
|
338
439
|
const folderConfiguration = await dockerFolderConfiguration(process.env, paths.projects);
|
|
440
|
+
const filesService = createFilesService({
|
|
441
|
+
db,
|
|
442
|
+
paths,
|
|
443
|
+
documents: () => documentsDir(nodeDocumentsHost()),
|
|
444
|
+
busy: () => busyProjects({ db: runtimeDb, hasInflight: runner.hasInflight }),
|
|
445
|
+
openFolder,
|
|
446
|
+
beginMutation: updater.beginMutation,
|
|
447
|
+
...(container
|
|
448
|
+
? {
|
|
449
|
+
docker: {
|
|
450
|
+
hostProjects: folderConfiguration.hostProjects,
|
|
451
|
+
hostBackups: folderConfiguration.hostBackups,
|
|
452
|
+
...(hostCli === undefined ? {} : { openOnHost: hostCli.openFolder }),
|
|
453
|
+
},
|
|
454
|
+
}
|
|
455
|
+
: {}),
|
|
456
|
+
});
|
|
339
457
|
const backups = createBackupService({
|
|
340
458
|
db,
|
|
341
459
|
paths,
|
|
@@ -348,12 +466,34 @@ export async function boot(config, options = {}) {
|
|
|
348
466
|
bootedAt: clock.now(),
|
|
349
467
|
beginMutation: updater.beginMutation,
|
|
350
468
|
});
|
|
469
|
+
// Before the server answers, so the first page already lists the samples. A failure costs
|
|
470
|
+
// only the samples: the log says why and Settings → Restore samples tries again.
|
|
471
|
+
if (options.seedSample === true && !pendingActivation)
|
|
472
|
+
await seedSamples({ db, paths, clock, ids, log, appVersion: version }).catch((error) => {
|
|
473
|
+
log.write("warn", "sample.seed", { detail: causedBy(error) });
|
|
474
|
+
});
|
|
475
|
+
const autostart = await createAutostart({
|
|
476
|
+
db,
|
|
477
|
+
dataDir: paths.dataDir,
|
|
478
|
+
logs: paths.logs,
|
|
479
|
+
port: config.port,
|
|
480
|
+
host: config.host,
|
|
481
|
+
version,
|
|
482
|
+
env: process.env,
|
|
483
|
+
});
|
|
484
|
+
if (options.refreshAutostart === true)
|
|
485
|
+
void autostart.refresh().catch((error) => {
|
|
486
|
+
log.write("warn", "autostart.refresh", { detail: causedBy(error) });
|
|
487
|
+
});
|
|
351
488
|
const app = createApp({
|
|
489
|
+
autostart,
|
|
352
490
|
rebuild,
|
|
353
491
|
drafts: draftDeps,
|
|
354
492
|
schedules: scheduleDeps,
|
|
355
493
|
backups,
|
|
494
|
+
files: filesService,
|
|
356
495
|
...(rebuild.measureAudio === undefined ? {} : { measureAudio: rebuild.measureAudio }),
|
|
496
|
+
decodePeaks: (path, signal) => decodePeaks(ffmpeg, path, signal),
|
|
357
497
|
openFolder,
|
|
358
498
|
folderLocation: {
|
|
359
499
|
...folderConfiguration,
|
|
@@ -368,7 +508,22 @@ export async function boot(config, options = {}) {
|
|
|
368
508
|
mutations,
|
|
369
509
|
sendNotification,
|
|
370
510
|
audioPreviews,
|
|
511
|
+
stylePreviews: createStylePreviews({
|
|
512
|
+
dir: stylePreviewDir(paths.dataDir),
|
|
513
|
+
render: ffmpegStylePreview({ ffmpeg, paths, log, dir: stylePreviewDir(paths.dataDir) }),
|
|
514
|
+
pictures: previewPictures({ db, paths }),
|
|
515
|
+
log,
|
|
516
|
+
}),
|
|
371
517
|
...modelSources(registry),
|
|
518
|
+
audition: (call, signal) => auditionVoice({ registry, clock, log }, call, signal),
|
|
519
|
+
voiceLanguages: async (provider, voiceId, signal) => {
|
|
520
|
+
try {
|
|
521
|
+
return await registry.tts(provider).voiceLanguages?.(voiceId, signal);
|
|
522
|
+
}
|
|
523
|
+
catch {
|
|
524
|
+
return undefined;
|
|
525
|
+
}
|
|
526
|
+
},
|
|
372
527
|
catalogue,
|
|
373
528
|
clock,
|
|
374
529
|
ids,
|
|
@@ -378,6 +533,13 @@ export async function boot(config, options = {}) {
|
|
|
378
533
|
flushSoon: flusher.soon,
|
|
379
534
|
probe: nodeCliProbe,
|
|
380
535
|
hostCliStatus: hostCli?.status,
|
|
536
|
+
generateCastImage: ({ channelId, ...call }) => standaloneImage(standalone, {
|
|
537
|
+
...call,
|
|
538
|
+
owner: { kind: "channel", id: channelId },
|
|
539
|
+
purpose: "cast-image",
|
|
540
|
+
}),
|
|
541
|
+
fetch: globalThis.fetch,
|
|
542
|
+
...(hostCli === undefined ? { cliLogin: readHostLogin } : {}),
|
|
381
543
|
});
|
|
382
544
|
const server = await listen(app, config, log);
|
|
383
545
|
const queueTimer = setInterval(() => {
|
|
@@ -397,14 +559,70 @@ export async function boot(config, options = {}) {
|
|
|
397
559
|
const scheduleTimer = setInterval(() => {
|
|
398
560
|
void scheduleTicks.tick();
|
|
399
561
|
}, 15_000);
|
|
562
|
+
// A step waiting out a rate limit or a timeout runs again once its wait is over; the wait
|
|
563
|
+
// is in the database, so this also picks up the ones a restart left waiting.
|
|
564
|
+
const retryTimer = setInterval(() => {
|
|
565
|
+
const release = updater.beginMutation();
|
|
566
|
+
if (!release)
|
|
567
|
+
return;
|
|
568
|
+
try {
|
|
569
|
+
wakeRetries(updateDb, clock, runner);
|
|
570
|
+
}
|
|
571
|
+
catch (error) {
|
|
572
|
+
log.write("error", "stage.retry", { detail: causedBy(error) });
|
|
573
|
+
}
|
|
574
|
+
finally {
|
|
575
|
+
release();
|
|
576
|
+
}
|
|
577
|
+
}, 5_000);
|
|
400
578
|
void scheduleTicks.tick();
|
|
401
579
|
// Once a minute is plenty for a daily slot; the decision itself (slices/backups/schedule)
|
|
402
580
|
// holds the first run back for a couple of minutes after a start.
|
|
581
|
+
// The model catalogue keeps itself current: once at start, then whenever a day has passed
|
|
582
|
+
// since the last check (looked at hourly, so a laptop that slept catches up).
|
|
583
|
+
const syncModels = () => {
|
|
584
|
+
if (!options.refreshModels || !catalogue.sync)
|
|
585
|
+
return;
|
|
586
|
+
catalogue.sync().then((status) => {
|
|
587
|
+
if (status.warning !== null)
|
|
588
|
+
log.write("warn", "model-catalog.sync", { detail: status.warning });
|
|
589
|
+
}, (error) => log.write("warn", "model-catalog.sync", { detail: causedBy(error) }));
|
|
590
|
+
};
|
|
591
|
+
syncModels();
|
|
592
|
+
const modelTimer = setInterval(() => {
|
|
593
|
+
if (catalogue.syncDue?.(Date.now()) === true)
|
|
594
|
+
syncModels();
|
|
595
|
+
}, 60 * 60_000);
|
|
403
596
|
const backupTimer = setInterval(() => {
|
|
404
597
|
backups.tick().catch((error) => {
|
|
405
598
|
log.write("error", "backups.tick", { detail: causedBy(error) });
|
|
406
599
|
});
|
|
407
600
|
}, 60_000);
|
|
601
|
+
// Settings → Trash keeps deleted items 30 days; once a day (looked at hourly, and once at
|
|
602
|
+
// start) whatever is older goes for good, a project's folder with it.
|
|
603
|
+
const trashPurge = createTrashPurge({
|
|
604
|
+
db: updateDb,
|
|
605
|
+
paths,
|
|
606
|
+
clock,
|
|
607
|
+
log,
|
|
608
|
+
hasInflight: runner.hasInflight,
|
|
609
|
+
});
|
|
610
|
+
const purgeTrash = () => {
|
|
611
|
+
const release = updater.beginMutation();
|
|
612
|
+
if (!release)
|
|
613
|
+
return;
|
|
614
|
+
try {
|
|
615
|
+
trashPurge.tick();
|
|
616
|
+
}
|
|
617
|
+
catch (error) {
|
|
618
|
+
log.write("error", "trash.purge", { detail: causedBy(error) });
|
|
619
|
+
}
|
|
620
|
+
finally {
|
|
621
|
+
release();
|
|
622
|
+
}
|
|
623
|
+
};
|
|
624
|
+
purgeTrash();
|
|
625
|
+
const trashTimer = setInterval(purgeTrash, 60 * 60_000);
|
|
408
626
|
listeningPort = portOf(server) ?? config.port;
|
|
409
627
|
// Whatever last run left queued goes out at start. Nothing waits for
|
|
410
628
|
// it, and an unreachable collector costs one refused socket.
|
|
@@ -427,11 +645,16 @@ export async function boot(config, options = {}) {
|
|
|
427
645
|
shutdown = () => {
|
|
428
646
|
stopping ??= (async () => {
|
|
429
647
|
notifier.close();
|
|
648
|
+
episodes?.close();
|
|
430
649
|
clearInterval(queueTimer);
|
|
431
650
|
clearInterval(scheduleTimer);
|
|
651
|
+
clearInterval(retryTimer);
|
|
432
652
|
clearInterval(backupTimer);
|
|
653
|
+
clearInterval(trashTimer);
|
|
654
|
+
clearInterval(modelTimer);
|
|
433
655
|
const mutationDrain = mutations.stop();
|
|
434
656
|
const scheduleDrain = scheduleTicks.stop();
|
|
657
|
+
const topicDrain = scheduleRunner.stop();
|
|
435
658
|
// A backup being written is stopped and its partial file removed, not waited for.
|
|
436
659
|
const backupDrain = backups.stop();
|
|
437
660
|
const serverClose = beginServerClose(server);
|
|
@@ -443,8 +666,10 @@ export async function boot(config, options = {}) {
|
|
|
443
666
|
// database until their response settles; anything racing shutdown receives 503.
|
|
444
667
|
await drainMutationsWithDeadline(mutationDrain, serverClose.terminate, mutationDrainTimeoutMs);
|
|
445
668
|
await scheduleDrain;
|
|
669
|
+
await topicDrain;
|
|
446
670
|
await backupDrain;
|
|
447
671
|
await runner.abortAll();
|
|
672
|
+
await episodes?.settled();
|
|
448
673
|
}
|
|
449
674
|
finally {
|
|
450
675
|
serverClose.terminate();
|
|
@@ -468,7 +693,7 @@ export async function boot(config, options = {}) {
|
|
|
468
693
|
};
|
|
469
694
|
if (pendingActivation)
|
|
470
695
|
stopActivation = watchActivation(updater, candidateToken, shutdown, (message) => log.write("warn", "update", { detail: message }));
|
|
471
|
-
return { paths, url: urlOf(config.host, listeningPort), stop: shutdown };
|
|
696
|
+
return { paths, url: urlOf(config.host, listeningPort), stop: shutdown, autostart };
|
|
472
697
|
}
|
|
473
698
|
catch (error) {
|
|
474
699
|
db?.close();
|
|
@@ -476,7 +701,7 @@ export async function boot(config, options = {}) {
|
|
|
476
701
|
throw error;
|
|
477
702
|
}
|
|
478
703
|
}
|
|
479
|
-
export function wireRunner({ audioPreviews, db, paths, clock, ids, log, hub, telemetry, flusher, registry, catalogue, ffmpeg, }) {
|
|
704
|
+
export function wireRunner({ onFinished, audioPreviews, db, paths, clock, ids, log, hub, telemetry, flusher, registry, catalogue, ffmpeg, alignSubtitles: aligner = alignSubtitles, }) {
|
|
480
705
|
// A stage counts what it did and the queue is flushed after each new event. `record`
|
|
481
706
|
// swallows its own failures, so this can neither fail a stage nor widen what leaves the
|
|
482
707
|
// machine - the payload allow-list is checked inside it.
|
|
@@ -484,7 +709,17 @@ export function wireRunner({ audioPreviews, db, paths, clock, ids, log, hub, tel
|
|
|
484
709
|
record(telemetry, type, counters);
|
|
485
710
|
flusher.soon();
|
|
486
711
|
};
|
|
487
|
-
const execution = {
|
|
712
|
+
const execution = {
|
|
713
|
+
db,
|
|
714
|
+
paths,
|
|
715
|
+
ids,
|
|
716
|
+
clock,
|
|
717
|
+
log,
|
|
718
|
+
ffmpeg,
|
|
719
|
+
alignSubtitles: aligner,
|
|
720
|
+
audioPreviews,
|
|
721
|
+
count,
|
|
722
|
+
};
|
|
488
723
|
// A stage slice is handed the wrapped calls, never the registry: every provider call
|
|
489
724
|
// it makes is already inside the retry policy (kernel/runner/providers.ts).
|
|
490
725
|
const providers = {
|
|
@@ -492,12 +727,23 @@ export function wireRunner({ audioPreviews, db, paths, clock, ids, log, hub, tel
|
|
|
492
727
|
attempts: sqliteAttempts(db, ids),
|
|
493
728
|
clock,
|
|
494
729
|
log,
|
|
730
|
+
meter: createUsageMeter({ db, ids, clock, catalogue: () => catalogue.read() }),
|
|
731
|
+
limits: createLimitGate({
|
|
732
|
+
db,
|
|
733
|
+
clock,
|
|
734
|
+
log,
|
|
735
|
+
changed: (projectId) => hub.emit(projectId, { type: "project.updated", projectId }),
|
|
736
|
+
}),
|
|
495
737
|
queue: createProviderQueue((provider) => isLocalCliProvider(provider)
|
|
496
738
|
? localCliConcurrency(provider)
|
|
497
739
|
: (catalogue.read().providers[provider]?.maxConcurrent ?? 1)),
|
|
498
740
|
};
|
|
499
741
|
const checkpoints = createCheckpointAuthority({
|
|
500
|
-
|
|
742
|
+
// A review waiting to send its item back holds the item's dependents like a checkpoint.
|
|
743
|
+
decide: (work) => {
|
|
744
|
+
const review = reviewHold(execution, work);
|
|
745
|
+
return review.kind === "held" ? review : checkpointDecisionForWork(execution, work);
|
|
746
|
+
},
|
|
501
747
|
approve: (projectId, checkpointId, identity) => approveCheckpoint(db, {
|
|
502
748
|
...identity,
|
|
503
749
|
projectId,
|
|
@@ -518,20 +764,30 @@ export function wireRunner({ audioPreviews, db, paths, clock, ids, log, hub, tel
|
|
|
518
764
|
},
|
|
519
765
|
standingsOf: (projectId) => executionStandings(execution, projectId),
|
|
520
766
|
ready: (work) => invocationReady(execution, work),
|
|
521
|
-
|
|
767
|
+
// A project in the trash (Settings → Trash) holds like a paused one until it is restored.
|
|
768
|
+
paused: (projectId) => projectPaused(db, projectId) ||
|
|
769
|
+
queueWaiting(db, projectId) ||
|
|
770
|
+
projectTrashed(db, projectId),
|
|
522
771
|
claim: (work) => {
|
|
523
772
|
const claimed = claimWork(db, work);
|
|
524
773
|
projectStandings(execution, work.projectId);
|
|
525
774
|
return claimed;
|
|
526
775
|
},
|
|
527
776
|
maySubmit: (work, pieceId) => maySubmit(db, work, pieceId),
|
|
528
|
-
|
|
777
|
+
waitToRetry: (work, fault, reason) => transact(db, () => {
|
|
778
|
+
const at = waitToRetry(execution, work, fault, reason, Math.random);
|
|
779
|
+
if (at !== undefined)
|
|
780
|
+
projectStandings(execution, work.projectId);
|
|
781
|
+
return at;
|
|
782
|
+
}),
|
|
783
|
+
finish: (work, state, reason, kind) => {
|
|
529
784
|
transact(db, () => {
|
|
530
|
-
finishWork(db, work, state, reason);
|
|
785
|
+
finishWork(db, work, state, reason, kind ?? null);
|
|
531
786
|
materializeAdmittedWork(execution, work.projectId);
|
|
532
787
|
projectStandings(execution, work.projectId);
|
|
533
788
|
settleReleasedCheckpoints(execution, work.projectId);
|
|
534
789
|
});
|
|
790
|
+
onFinished?.(work);
|
|
535
791
|
},
|
|
536
792
|
},
|
|
537
793
|
runs: Object.fromEntries(stageKinds.map((kind) => [
|
|
@@ -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.
|