@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
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { projectPaused } from "../admission/repo.js";
|
|
2
|
-
|
|
2
|
+
//
|
|
3
|
+
// An automatic redo (`pendingSuperseded`) runs while the rest of the run waits: admitted work
|
|
4
|
+
// that has not started is replaced by the redo's own, so only running work and accepted jobs
|
|
5
|
+
// stand in its way.
|
|
6
|
+
export function activeConflict(deps, projectId, keys, pendingSuperseded = false) {
|
|
3
7
|
const rows = deps.db
|
|
4
8
|
.prepare("SELECT work_key,logical_key,live FROM (SELECT p.work_key,r.logical_key," +
|
|
5
9
|
"(w.state='running' OR (? AND w.state='pending' AND w.dispatch_state='allowed' AND NOT EXISTS(" +
|
|
@@ -10,7 +14,7 @@ export function activeConflict(deps, projectId, keys) {
|
|
|
10
14
|
"FROM revision_work w JOIN revision_work_pieces p ON p.work_id=w.id " +
|
|
11
15
|
"LEFT JOIN revision_work_reservations r ON r.work_id=w.id AND r.piece_id=p.id " +
|
|
12
16
|
"WHERE w.project_id=?) WHERE live OR accepted")
|
|
13
|
-
.all(projectPaused(deps.db, projectId) ? 0 : 1, projectId)
|
|
17
|
+
.all(pendingSuperseded || projectPaused(deps.db, projectId) ? 0 : 1, projectId)
|
|
14
18
|
.filter((row) => keys.some((key) => row.work_key === key ||
|
|
15
19
|
row.logical_key === key ||
|
|
16
20
|
(key.endsWith(":future") && String(row.work_key).startsWith(key.slice(0, -6)))));
|
|
@@ -18,7 +22,7 @@ export function activeConflict(deps, projectId, keys) {
|
|
|
18
22
|
return undefined;
|
|
19
23
|
return rows.some((row) => Number(row.live) === 1) ? "running" : "accepted-job";
|
|
20
24
|
}
|
|
21
|
-
export const acceptedJobMessage = "An accepted provider job for this section is waiting to be collected. Use
|
|
25
|
+
export const acceptedJobMessage = "An accepted provider job for this section is waiting to be collected. Use Try again or Continue the run, then rerun.";
|
|
22
26
|
export function conflictRefusal(conflict) {
|
|
23
27
|
return conflict === "running"
|
|
24
28
|
? { ok: false, reason: "running" }
|
|
@@ -6,6 +6,9 @@ export const recoveryActionSchema = z.discriminatedUnion("kind", [
|
|
|
6
6
|
z.object({ kind: z.literal("resume") }).strict(),
|
|
7
7
|
z.object({ kind: z.literal("retry"), stage: z.enum(stageKinds) }).strict(),
|
|
8
8
|
z.object({ kind: z.literal("rerun"), stage: z.enum(stageKinds) }).strict(),
|
|
9
|
+
// Make one reviewed item again (`slices/reviews`): an image, the thumbnail, the article, the
|
|
10
|
+
// narration or one short, and everything that depends on it, its review included.
|
|
11
|
+
z.object({ kind: z.literal("redo"), item: z.string().min(1).max(200) }).strict(),
|
|
9
12
|
]);
|
|
10
13
|
export const recoveryRequestSchema = revisionControlSchema
|
|
11
14
|
.extend({
|
|
@@ -9,19 +9,45 @@ export function sectionRoots(view, plan, stage) {
|
|
|
9
9
|
return false;
|
|
10
10
|
if (stage === "video")
|
|
11
11
|
return (row.input.kind === "local" &&
|
|
12
|
-
(row.key.startsWith("export:") ||
|
|
12
|
+
(row.key.startsWith("export:") ||
|
|
13
|
+
row.key === "subtitles:files" ||
|
|
14
|
+
row.key === "voices:files"));
|
|
13
15
|
if (stage === "audio")
|
|
14
16
|
return (row.input.kind === "tts" ||
|
|
15
17
|
(row.input.kind === "deferred" &&
|
|
16
18
|
["body-narration", "intro-narration", "outro-narration"].includes(row.input.operation)));
|
|
17
19
|
if (stage === "article")
|
|
18
|
-
return row.key === "article:body" && row.kind === "provider";
|
|
20
|
+
return ((row.key === "article:body" || row.key === "script:attribute") && row.kind === "provider");
|
|
19
21
|
if (stage === "document")
|
|
20
22
|
return row.key === "document:pdf";
|
|
21
23
|
return row.kind === "provider";
|
|
22
24
|
})
|
|
23
25
|
.map((row) => row.key);
|
|
24
26
|
}
|
|
27
|
+
// What making one reviewed item again regenerates and where its rebuild starts. The article
|
|
28
|
+
// and the narration are redone like More → make it again in its section on their stage; an image or the thumbnail
|
|
29
|
+
// by its own token, like Regenerate; a short by "Make this short again"'s token.
|
|
30
|
+
export function redoTarget(view, plan, item) {
|
|
31
|
+
const section = (stage) => {
|
|
32
|
+
const edit = regenerationEdit(view, plan, stage);
|
|
33
|
+
return edit === undefined ? undefined : { roots: sectionRoots(view, plan, stage), edit };
|
|
34
|
+
};
|
|
35
|
+
if (item === "narration")
|
|
36
|
+
return section("audio");
|
|
37
|
+
if (item === "article:body")
|
|
38
|
+
return section("article");
|
|
39
|
+
const own = (roots) => ({
|
|
40
|
+
roots,
|
|
41
|
+
edit: { config: view.revision.config, content: view.revision.content, regenerate: [item] },
|
|
42
|
+
});
|
|
43
|
+
if (/^shorts:\d+$/.test(item))
|
|
44
|
+
return plan.recipes.some((row) => row.key === `${item}:prompts`)
|
|
45
|
+
? own([`${item}:prompts`])
|
|
46
|
+
: undefined;
|
|
47
|
+
return plan.recipes.some((row) => row.key === item && row.kind === "provider")
|
|
48
|
+
? own([item])
|
|
49
|
+
: undefined;
|
|
50
|
+
}
|
|
25
51
|
export function dependentClosure(recipes, roots) {
|
|
26
52
|
const selected = new Set(roots);
|
|
27
53
|
let changed = true;
|
|
@@ -10,7 +10,7 @@ import { recipeProviderChoice } from "./recipe-provider-choice.js";
|
|
|
10
10
|
import { acceptedJobMessage, activeConflict, conflictRefusal } from "./recovery-conflict.js";
|
|
11
11
|
import { recoveryRequestSchema, } from "./recovery-model.js";
|
|
12
12
|
import { readRecovery, recoveryAuthority, recoveryKey, rememberRecovery, reserveRecovery, } from "./recovery-repo.js";
|
|
13
|
-
import { dependentClosure, recoverySelection, regenerationEdit, sectionRoots, } from "./recovery-selection.js";
|
|
13
|
+
import { dependentClosure, recoverySelection, redoTarget, regenerationEdit, sectionRoots, } from "./recovery-selection.js";
|
|
14
14
|
import { previewById, storePreview } from "./repo.js";
|
|
15
15
|
import { admitCheckedPreview, wakeRebuild } from "./service.js";
|
|
16
16
|
import { checkReadiness } from "./service-readiness.js";
|
|
@@ -27,7 +27,10 @@ function credentialsStamp(deps, plan, view) {
|
|
|
27
27
|
.filter((row) => providers.has(String(row.provider)));
|
|
28
28
|
return requestHash("recovery-credentials", view.revision.id, metadata);
|
|
29
29
|
}
|
|
30
|
-
export async function recoverProject(deps, projectId, raw
|
|
30
|
+
export async function recoverProject(deps, projectId, raw,
|
|
31
|
+
// Set only by an automatic review redo (`review-redo.ts`): see `activeConflict`.
|
|
32
|
+
options = {}) {
|
|
33
|
+
const superseded = options.pendingSuperseded === true;
|
|
31
34
|
const parsed = recoveryRequestSchema.safeParse(raw);
|
|
32
35
|
if (!parsed.success)
|
|
33
36
|
return {
|
|
@@ -39,6 +42,12 @@ export async function recoverProject(deps, projectId, raw) {
|
|
|
39
42
|
})),
|
|
40
43
|
};
|
|
41
44
|
const input = parsed.data;
|
|
45
|
+
// Where a rerun or a redo starts: the stage's generated work, or the reviewed item's.
|
|
46
|
+
const rootsOf = (view, plan) => input.action.kind === "rerun"
|
|
47
|
+
? sectionRoots(view, plan, input.action.stage)
|
|
48
|
+
: input.action.kind === "redo"
|
|
49
|
+
? (redoTarget(view, plan, input.action.item)?.roots ?? [])
|
|
50
|
+
: [];
|
|
42
51
|
const prepared = await withProjectControl(deps.db, projectId, async () => {
|
|
43
52
|
const found = readRecovery(deps, projectId, input);
|
|
44
53
|
if (found !== undefined && "ok" in found)
|
|
@@ -54,7 +63,11 @@ export async function recoverProject(deps, projectId, raw) {
|
|
|
54
63
|
if (!base.current)
|
|
55
64
|
return { ok: false, reason: "conflict" };
|
|
56
65
|
const plan = retainedPreviewPlan(deps, base, deps.catalogue.read());
|
|
57
|
-
const edit = input.action.kind === "rerun"
|
|
66
|
+
const edit = input.action.kind === "rerun"
|
|
67
|
+
? regenerationEdit(base, plan, input.action.stage)
|
|
68
|
+
: input.action.kind === "redo"
|
|
69
|
+
? redoTarget(base, plan, input.action.item)?.edit
|
|
70
|
+
: null;
|
|
58
71
|
if (edit === undefined)
|
|
59
72
|
return {
|
|
60
73
|
ok: false,
|
|
@@ -62,7 +75,9 @@ export async function recoverProject(deps, projectId, raw) {
|
|
|
62
75
|
fields: [
|
|
63
76
|
{
|
|
64
77
|
field: "stage",
|
|
65
|
-
message:
|
|
78
|
+
message: input.action.kind === "redo"
|
|
79
|
+
? "This item is no longer made by the project (it was removed, or its source changed), so it can't be made again. Reload the project page."
|
|
80
|
+
: "This section has no generated work to rerun. Use Edit project to change its source.",
|
|
66
81
|
},
|
|
67
82
|
],
|
|
68
83
|
};
|
|
@@ -76,14 +91,15 @@ export async function recoverProject(deps, projectId, raw) {
|
|
|
76
91
|
let view = base;
|
|
77
92
|
if (record.edit !== null) {
|
|
78
93
|
const prior = retainedPreviewPlan(deps, base, deps.catalogue.read());
|
|
79
|
-
|
|
80
|
-
if (action.kind !== "rerun")
|
|
94
|
+
if (input.action.kind !== "rerun" && input.action.kind !== "redo")
|
|
81
95
|
throw new Error("Slopify hit an internal error (a recovery edit has nothing to re-run). Try again; if it happens again, use Download diagnostics in Settings and report it.");
|
|
82
|
-
const affected = dependentClosure(prior.recipes,
|
|
96
|
+
const affected = dependentClosure(prior.recipes, rootsOf(base, prior));
|
|
83
97
|
const savedIntent = deps.db
|
|
84
98
|
.prepare("SELECT result_revision_id FROM revision_mutations WHERE project_id=? AND idempotency_key=?")
|
|
85
99
|
.get(projectId, recoveryKey(input, "save"));
|
|
86
|
-
const conflict = savedIntent === undefined
|
|
100
|
+
const conflict = savedIntent === undefined
|
|
101
|
+
? activeConflict(deps, projectId, affected, superseded)
|
|
102
|
+
: undefined;
|
|
87
103
|
if (conflict !== undefined)
|
|
88
104
|
return finish(conflictRefusal(conflict));
|
|
89
105
|
const saved = await saveRevision(deps, {
|
|
@@ -92,7 +108,7 @@ export async function recoverProject(deps, projectId, raw) {
|
|
|
92
108
|
idempotencyKey: recoveryKey(input, "save"),
|
|
93
109
|
edit: record.edit,
|
|
94
110
|
beforeCommit: () => {
|
|
95
|
-
const late = activeConflict(deps, projectId, affected);
|
|
111
|
+
const late = activeConflict(deps, projectId, affected, superseded);
|
|
96
112
|
return late === undefined
|
|
97
113
|
? undefined
|
|
98
114
|
: {
|
|
@@ -121,10 +137,10 @@ export async function recoverProject(deps, projectId, raw) {
|
|
|
121
137
|
if (currentRevisionId(deps.db, projectId) !== view.revision.id)
|
|
122
138
|
return finish({ ok: false, reason: "conflict" });
|
|
123
139
|
const plan = retainedPreviewPlan(deps, view, deps.catalogue.read());
|
|
124
|
-
const rerunKeys = input.action.kind === "rerun"
|
|
125
|
-
? dependentClosure(plan.recipes,
|
|
140
|
+
const rerunKeys = input.action.kind === "rerun" || input.action.kind === "redo"
|
|
141
|
+
? dependentClosure(plan.recipes, rootsOf(view, plan))
|
|
126
142
|
: [];
|
|
127
|
-
const selection = input.action.kind === "rerun"
|
|
143
|
+
const selection = input.action.kind === "rerun" || input.action.kind === "redo"
|
|
128
144
|
? { kind: "selected", workKeys: rerunKeys }
|
|
129
145
|
: recoverySelection(plan, input.action.kind === "retry" ? input.action.stage : undefined);
|
|
130
146
|
if (selection.kind === "selected" && selection.workKeys.length === 0)
|
|
@@ -167,15 +183,15 @@ export async function recoverProject(deps, projectId, raw) {
|
|
|
167
183
|
input.action.stage === "video"
|
|
168
184
|
? {
|
|
169
185
|
field: "video",
|
|
170
|
-
message: "Source media is incomplete. Use
|
|
186
|
+
message: "Source media is incomplete. Use Continue the run to recover it before rerunning the export.",
|
|
171
187
|
}
|
|
172
188
|
: {
|
|
173
189
|
field: "document",
|
|
174
|
-
message: "The article or thumbnail the document is made from isn't finished. Use
|
|
190
|
+
message: "The article or thumbnail the document is made from isn't finished. Use Continue the run to finish it, then More → make it again in its section on Document.",
|
|
175
191
|
},
|
|
176
192
|
],
|
|
177
193
|
});
|
|
178
|
-
const conflict = rerunKeys.length > 0 ? activeConflict(deps, projectId, rerunKeys) : undefined;
|
|
194
|
+
const conflict = rerunKeys.length > 0 ? activeConflict(deps, projectId, rerunKeys, superseded) : undefined;
|
|
179
195
|
if (conflict !== undefined)
|
|
180
196
|
return finish(conflictRefusal(conflict));
|
|
181
197
|
storePreview(deps, preview.value.preview, preview.value.execution);
|
|
@@ -199,7 +215,9 @@ export async function recoverProject(deps, projectId, raw) {
|
|
|
199
215
|
catch {
|
|
200
216
|
deps.log.write("warn", "project.recovery", {
|
|
201
217
|
projectId,
|
|
202
|
-
...(input.action.kind === "resume"
|
|
218
|
+
...(input.action.kind === "resume" || input.action.kind === "redo"
|
|
219
|
+
? {}
|
|
220
|
+
: { stage: input.action.stage }),
|
|
203
221
|
detail: "Provider readiness could not be loaded.",
|
|
204
222
|
});
|
|
205
223
|
ready = {
|
|
@@ -232,7 +250,7 @@ export async function recoverProject(deps, projectId, raw) {
|
|
|
232
250
|
if (ready.fields.length > 0)
|
|
233
251
|
return finish({ ok: false, reason: "readiness", fields: [...ready.fields] });
|
|
234
252
|
const conflict = prepared.rerunKeys.length > 0
|
|
235
|
-
? activeConflict(deps, projectId, prepared.rerunKeys)
|
|
253
|
+
? activeConflict(deps, projectId, prepared.rerunKeys, superseded)
|
|
236
254
|
: undefined;
|
|
237
255
|
if (conflict !== undefined)
|
|
238
256
|
return finish(conflictRefusal(conflict));
|
|
@@ -7,7 +7,7 @@ export { transitionRevisionWork } from "./transition-repo.js";
|
|
|
7
7
|
export function publicationTargets(db, publication, workKey, workFingerprint) {
|
|
8
8
|
const { work, pieceId, publicationId } = publication;
|
|
9
9
|
if (publicationId !== (pieceId ?? work.workId))
|
|
10
|
-
throw new Error("Slopify hit an internal error (a finished result is filed under the wrong step).
|
|
10
|
+
throw new Error("Slopify hit an internal error (a finished result is filed under the wrong step). Try again; if it happens again, use Download diagnostics in Settings and report it.");
|
|
11
11
|
if (!workExists(db, work))
|
|
12
12
|
return [];
|
|
13
13
|
if (pieceId !== null) {
|
|
@@ -15,9 +15,9 @@ export function publicationTargets(db, publication, workKey, workFingerprint) {
|
|
|
15
15
|
.prepare("SELECT work_key,fingerprint FROM revision_work_pieces WHERE id=? AND work_id=?")
|
|
16
16
|
.get(pieceId, work.workId);
|
|
17
17
|
if (piece === undefined)
|
|
18
|
-
throw new Error("Slopify hit an internal error (a finished result belongs to a different step).
|
|
18
|
+
throw new Error("Slopify hit an internal error (a finished result belongs to a different step). Try again; if it happens again, use Download diagnostics in Settings and report it.");
|
|
19
19
|
if (piece.work_key !== workKey || piece.fingerprint !== workFingerprint)
|
|
20
|
-
throw new Error("Slopify hit an internal error (a finished result doesn't match its saved step).
|
|
20
|
+
throw new Error("Slopify hit an internal error (a finished result doesn't match its saved step). Try again; if it happens again, use Download diagnostics in Settings and report it.");
|
|
21
21
|
}
|
|
22
22
|
const currentId = currentRevisionId(db, work.projectId);
|
|
23
23
|
const owns = (revisionId) => {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { autoRetryDelay } from "../../kernel/runner/retry-policy.js";
|
|
2
|
+
import { autoRetriesOf, deferWork, dueRetries } from "../../kernel/runner/work-authority.js";
|
|
3
|
+
// The runner's store for the second tier of the retry policy (`kernel/runner/retry-policy.ts`):
|
|
4
|
+
// a failure time can fix puts the step back to wait, with the time it may run again.
|
|
5
|
+
export function waitToRetry(deps, work, fault, reason, random) {
|
|
6
|
+
const delay = autoRetryDelay(fault, autoRetriesOf(deps.db, work), random);
|
|
7
|
+
if (delay === undefined || fault === undefined)
|
|
8
|
+
return undefined;
|
|
9
|
+
const at = new Date(deps.clock.now().getTime() + delay).toISOString();
|
|
10
|
+
return deferWork(deps.db, work, at, reason, fault.kind) ? at : undefined;
|
|
11
|
+
}
|
|
12
|
+
// Ticks every project with a wait that has run out. Returns them, for the log and tests.
|
|
13
|
+
export function wakeRetries(db, clock, runner) {
|
|
14
|
+
const due = dueRetries(db, clock.now().toISOString());
|
|
15
|
+
for (const projectId of due)
|
|
16
|
+
runner.tick(projectId);
|
|
17
|
+
return due;
|
|
18
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { reviewKey } from "../reviews/model.js";
|
|
3
|
+
import { pendingRedos, setRedoState } from "../reviews/repo.js";
|
|
4
|
+
import { currentRevisionId } from "../revisions/repo.js";
|
|
5
|
+
import { recoverProject } from "./recovery.js";
|
|
6
|
+
import { dependentClosure } from "./recovery-selection.js";
|
|
7
|
+
import { executionPlan, executionView, savedCatalogue } from "./runtime-plan.js";
|
|
8
|
+
import { workPieces } from "./work-records.js";
|
|
9
|
+
// While a review's verdict waits to send its item back, what depends on the item waits too:
|
|
10
|
+
// the automatic redo replaces that work, and starting it on the failed item would be wasted.
|
|
11
|
+
// The same claim-time hold a review checkpoint uses, answered by the reviewer.
|
|
12
|
+
export function reviewHold(deps, work) {
|
|
13
|
+
const waiting = pendingRedos(deps.db, work.projectId).filter((row) => row.revisionId === work.revisionId);
|
|
14
|
+
if (waiting.length === 0)
|
|
15
|
+
return { kind: "eligible" };
|
|
16
|
+
const view = executionView(deps, work.projectId, work.revisionId);
|
|
17
|
+
const row = deps.db
|
|
18
|
+
.prepare("SELECT recipe_context FROM revision_work WHERE id=?")
|
|
19
|
+
.get(work.workId);
|
|
20
|
+
if (view === undefined || row === undefined)
|
|
21
|
+
return { kind: "eligible" };
|
|
22
|
+
const plan = executionPlan(deps, view, savedCatalogue(row.recipe_context));
|
|
23
|
+
const keys = new Set(workPieces(deps.db, work.workId).map((piece) => piece.key));
|
|
24
|
+
const held = waiting.filter((verdict) => {
|
|
25
|
+
const review = reviewKey(verdict.itemKey);
|
|
26
|
+
return dependentClosure(plan.recipes, [review]).some((key) => key !== review && keys.has(key));
|
|
27
|
+
});
|
|
28
|
+
return held.length === 0
|
|
29
|
+
? { kind: "eligible" }
|
|
30
|
+
: { kind: "held", checkpointIds: held.map((verdict) => `review:${verdict.id}`) };
|
|
31
|
+
}
|
|
32
|
+
// Starts the redos reviews asked for, once their review step has finished: the item is made
|
|
33
|
+
// again through Re-run's path (a new regeneration token saved as a new project version, then
|
|
34
|
+
// its dependents rebuilt), so the redone item, its new review and everything after it are
|
|
35
|
+
// ordinary recipe work. One at a time per verdict; a redo that cannot start leaves the item
|
|
36
|
+
// flagged with the reason and releases what was waiting on it.
|
|
37
|
+
export function createReviewRedos() {
|
|
38
|
+
let bound;
|
|
39
|
+
const running = new Set();
|
|
40
|
+
const kick = (projectId) => {
|
|
41
|
+
const deps = bound;
|
|
42
|
+
if (deps === undefined)
|
|
43
|
+
return;
|
|
44
|
+
for (const verdict of pendingRedos(deps.db, projectId)) {
|
|
45
|
+
if (running.has(verdict.id))
|
|
46
|
+
continue;
|
|
47
|
+
running.add(verdict.id);
|
|
48
|
+
void startRedo(deps, verdict)
|
|
49
|
+
.catch((error) => {
|
|
50
|
+
deps.log.write("error", "review.redo", {
|
|
51
|
+
projectId: verdict.projectId,
|
|
52
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
53
|
+
});
|
|
54
|
+
setRedoState(deps.db, verdict.id, "failed", "Slopify hit an internal error while making this item again. Use Redo beside it; if it happens again, use Download diagnostics in Settings and report it.");
|
|
55
|
+
})
|
|
56
|
+
.finally(() => {
|
|
57
|
+
running.delete(verdict.id);
|
|
58
|
+
try {
|
|
59
|
+
deps.runner.tick(verdict.projectId);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// The next tick of this project picks the released work up.
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
return {
|
|
68
|
+
bind: (deps) => {
|
|
69
|
+
bound = deps;
|
|
70
|
+
},
|
|
71
|
+
kick,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
// The verdicts whose redo is being started right now. Overrule may call off a redo only while
|
|
75
|
+
// it waits, not once Slopify has begun making the item again.
|
|
76
|
+
const starting = new Set();
|
|
77
|
+
export function redoStarting(verdictId) {
|
|
78
|
+
return starting.has(verdictId);
|
|
79
|
+
}
|
|
80
|
+
export async function startRedo(deps, verdict) {
|
|
81
|
+
starting.add(verdict.id);
|
|
82
|
+
try {
|
|
83
|
+
await makeAgain(deps, verdict);
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
starting.delete(verdict.id);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async function makeAgain(deps, verdict) {
|
|
90
|
+
const base = currentRevisionId(deps.db, verdict.projectId);
|
|
91
|
+
if (base === undefined) {
|
|
92
|
+
setRedoState(deps.db, verdict.id, "failed", "The project no longer exists.");
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const result = await recoverProject(deps, verdict.projectId, {
|
|
96
|
+
baseRevisionId: base,
|
|
97
|
+
idempotencyKey: redoKey(verdict.id),
|
|
98
|
+
action: { kind: "redo", item: verdict.itemKey },
|
|
99
|
+
}, { pendingSuperseded: true });
|
|
100
|
+
if (result.ok)
|
|
101
|
+
setRedoState(deps.db, verdict.id, "started");
|
|
102
|
+
else
|
|
103
|
+
setRedoState(deps.db, verdict.id, "failed", redoRefusal(result));
|
|
104
|
+
deps.emit(verdict.projectId, { type: "project.updated", projectId: verdict.projectId });
|
|
105
|
+
}
|
|
106
|
+
// A stable request identity per verdict, so a restart that starts the same redo again gets
|
|
107
|
+
// the first attempt's answer instead of a second redo.
|
|
108
|
+
export function redoKey(verdictId) {
|
|
109
|
+
const hex = createHash("sha256").update(`review-redo:${verdictId}`).digest("hex");
|
|
110
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-4${hex.slice(13, 16)}-8${hex.slice(17, 20)}-${hex.slice(20, 32)}`;
|
|
111
|
+
}
|
|
112
|
+
export function redoRefusal(result) {
|
|
113
|
+
const detail = result.fields?.map((field) => field.message).join(" ");
|
|
114
|
+
switch (result.reason) {
|
|
115
|
+
case "running":
|
|
116
|
+
return "Slopify couldn't make this item again automatically because work that depends on it was already running. Use Redo beside it once the run has finished.";
|
|
117
|
+
case "readiness":
|
|
118
|
+
return `Slopify couldn't make this item again automatically: ${detail ?? "a provider isn't ready"}. Fix that in Settings → Providers, then use Redo beside it.`;
|
|
119
|
+
case "conflict":
|
|
120
|
+
case "control-changed":
|
|
121
|
+
return "Slopify couldn't make this item again automatically because the project changed at the same moment (an edit, Pause or Cancel). Use Redo beside it.";
|
|
122
|
+
default:
|
|
123
|
+
return `Slopify couldn't make this item again automatically${detail ? `: ${detail}` : "."} Use Redo beside it.`;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -15,6 +15,11 @@ export async function revisionAction(deps, projectId, action) {
|
|
|
15
15
|
? view.outputs.find((row) => row.selected &&
|
|
16
16
|
row.output.id === action.outputId &&
|
|
17
17
|
(row.output.role === "image" ||
|
|
18
|
+
// One of the generated thumbnails is made again on its own, the others kept.
|
|
19
|
+
(action.kind === "regenerate-image" &&
|
|
20
|
+
row.output.role === "thumbnail" &&
|
|
21
|
+
(config.sources.thumbnail === "from_prompt" ||
|
|
22
|
+
config.sources.thumbnail === "prompt_by_llm")) ||
|
|
18
23
|
// A generated establishing image is made again like any image; it is never deleted
|
|
19
24
|
// on its own (Edit project sets it Off), and an uploaded one has nothing to redo.
|
|
20
25
|
(action.kind === "regenerate-image" &&
|
|
@@ -12,7 +12,10 @@ export function reservationKey(recipe, view) {
|
|
|
12
12
|
? `audio:${recipe.input.segment}:future`
|
|
13
13
|
: recipe.input.kind === "llm" && recipe.input.preparation
|
|
14
14
|
? `narration:prepare:${recipe.input.preparation.segment}:future`
|
|
15
|
-
:
|
|
15
|
+
: (recipe.input.kind === "llm" && recipe.input.describe) ||
|
|
16
|
+
recipe.key.startsWith("figure:card:")
|
|
17
|
+
? "narration:describe:future"
|
|
18
|
+
: key;
|
|
16
19
|
return view.revision.fingerprints[future] === undefined ? key : future;
|
|
17
20
|
}
|
|
18
21
|
export function admitInitialRevision(deps, view, catalogue) {
|
|
@@ -22,7 +22,7 @@ export async function executeAnimateRecipe(deps, context, providers, piece) {
|
|
|
22
22
|
return "done";
|
|
23
23
|
const input = piece.input;
|
|
24
24
|
if (input.kind !== "image" || input.animate === undefined)
|
|
25
|
-
throw new Error("Slopify hit an internal error (an animated image was set up wrongly). Use
|
|
25
|
+
throw new Error("Slopify hit an internal error (an animated image was set up wrongly). Use Try again; if it happens again, use Download diagnostics in Settings and report it.");
|
|
26
26
|
const imageKey = piece.key.slice("animate:".length);
|
|
27
27
|
const view = executionView(deps, context.work.projectId, context.work.revisionId);
|
|
28
28
|
const index = (view?.revision.content.imageOrder.indexOf(imageKey) ?? -1) + 1;
|
|
@@ -35,10 +35,10 @@ export async function executeAnimateRecipe(deps, context, providers, piece) {
|
|
|
35
35
|
return "done";
|
|
36
36
|
};
|
|
37
37
|
if (still === undefined)
|
|
38
|
-
return fallback("its image wasn't ready to animate. Use
|
|
38
|
+
return fallback("its image wasn't ready to animate. Use More → Render the video again in the Video section after the image is made.");
|
|
39
39
|
const animate = providers.forPiece(piece.id).animate;
|
|
40
40
|
if (animate === undefined)
|
|
41
|
-
throw new Error("Slopify hit an internal error (animating images isn't wired up in this build). Use
|
|
41
|
+
throw new Error("Slopify hit an internal error (animating images isn't wired up in this build). Use Try again; if it happens again, use Download diagnostics in Settings and report it.");
|
|
42
42
|
if (!context.maySubmit(piece.id))
|
|
43
43
|
return "held";
|
|
44
44
|
let made;
|
|
@@ -55,7 +55,7 @@ export async function executeAnimateRecipe(deps, context, providers, piece) {
|
|
|
55
55
|
catch (error) {
|
|
56
56
|
if (context.signal.aborted)
|
|
57
57
|
throw error;
|
|
58
|
-
return fallback(`${error instanceof Error ? error.message : String(error)} To try again, use
|
|
58
|
+
return fallback(`${error instanceof Error ? error.message : String(error)} To try again, use More → Render the video again in the Video section.`);
|
|
59
59
|
}
|
|
60
60
|
if (!made.ok)
|
|
61
61
|
return "held";
|
|
@@ -12,14 +12,14 @@ const answerSchema = z.object({
|
|
|
12
12
|
});
|
|
13
13
|
export async function executeArticleRequests(deps, context, providers, initial) {
|
|
14
14
|
if (initial.input.kind !== "llm")
|
|
15
|
-
throw new Error("Slopify hit an internal error (the article step has no AI request).
|
|
15
|
+
throw new Error("Slopify hit an internal error (the article step has no AI request). Try again; if it happens again, use Download diagnostics in Settings and report it.");
|
|
16
16
|
let piece = initial;
|
|
17
17
|
let text = "";
|
|
18
18
|
let inputTokens = 0;
|
|
19
19
|
let outputTokens = 0;
|
|
20
20
|
for (let part = 0; part <= continuationLimit; part += 1) {
|
|
21
21
|
if (piece.input.kind !== "llm")
|
|
22
|
-
throw new Error("Slopify hit an internal error (an article continuation has no AI request).
|
|
22
|
+
throw new Error("Slopify hit an internal error (an article continuation has no AI request). Try again; if it happens again, use Download diagnostics in Settings and report it.");
|
|
23
23
|
const saved = deps.db
|
|
24
24
|
.prepare("SELECT result_json FROM revision_work_pieces WHERE id=? AND work_id=?")
|
|
25
25
|
.get(piece.id, context.work.workId);
|
|
@@ -38,9 +38,9 @@ export async function executeArticleRequests(deps, context, providers, initial)
|
|
|
38
38
|
webSearch: input.webSearch,
|
|
39
39
|
previewLabel: part === 0 ? "Article" : `Continuation ${part}`,
|
|
40
40
|
check: (value) => value.text.trim() === ""
|
|
41
|
-
? "The AI model returned an empty article.
|
|
41
|
+
? "The AI model returned an empty article. Try again; if it keeps happening, choose a different model in Edit project → Providers."
|
|
42
42
|
: part === continuationLimit && value.finishReason === "length"
|
|
43
|
-
? `The article was still unfinished after ${continuationLimit} continuations because the AI model kept stopping at its length limit. Ask for a shorter article in Edit project → Prompts, or choose a model with a larger output limit in Edit project → Providers, then
|
|
43
|
+
? `The article was still unfinished after ${continuationLimit} continuations because the AI model kept stopping at its length limit. Ask for a shorter article in Edit project → Prompts, or choose a model with a larger output limit in Edit project → Providers, then Try again.`
|
|
44
44
|
: undefined,
|
|
45
45
|
}, (event) => {
|
|
46
46
|
if (event.type === "delta")
|
|
@@ -72,7 +72,7 @@ export async function executeArticleRequests(deps, context, providers, initial)
|
|
|
72
72
|
return { ok: false, reason: "held" };
|
|
73
73
|
piece = next;
|
|
74
74
|
}
|
|
75
|
-
throw new Error(`The article was still unfinished after ${continuationLimit} continuations because the AI model kept stopping at its length limit. Ask for a shorter article in Edit project → Prompts, or choose a model with a larger output limit in Edit project → Providers, then
|
|
75
|
+
throw new Error(`The article was still unfinished after ${continuationLimit} continuations because the AI model kept stopping at its length limit. Ask for a shorter article in Edit project → Prompts, or choose a model with a larger output limit in Edit project → Providers, then Try again.`);
|
|
76
76
|
}
|
|
77
77
|
function continuationPiece(deps, context, initial, part, text) {
|
|
78
78
|
return transact(deps.db, () => {
|
|
@@ -28,7 +28,7 @@ export async function executeDocumentRecipe(deps, context, piece) {
|
|
|
28
28
|
? view.articleMarkdown
|
|
29
29
|
: text("article_md", "article:body");
|
|
30
30
|
if (article === null)
|
|
31
|
-
throw new Error("The article isn't finished yet, so there's nothing to make the document from. Let the Article stage finish (Resume, or
|
|
31
|
+
throw new Error("The article isn't finished yet, so there's nothing to make the document from. Let the Article stage finish (Resume, or Try again on Article), then use Try again on Document.");
|
|
32
32
|
const thumbnail = ready(view, "thumbnail", "thumbnail:image");
|
|
33
33
|
let cover = null;
|
|
34
34
|
if (config.sources.thumbnail !== "off" && thumbnail !== undefined) {
|
|
@@ -36,7 +36,7 @@ export async function executeDocumentRecipe(deps, context, piece) {
|
|
|
36
36
|
cover = new Uint8Array(readFileSync(outputPath(deps.paths, projectId, thumbnail.output.path)));
|
|
37
37
|
}
|
|
38
38
|
catch (error) {
|
|
39
|
-
throw new Error(`The document's cover is the project's thumbnail, and its file can't be read (${error instanceof Error ? error.message : String(error)}). Regenerate or upload the thumbnail again (
|
|
39
|
+
throw new Error(`The document's cover is the project's thumbnail, and its file can't be read (${error instanceof Error ? error.message : String(error)}). Regenerate or upload the thumbnail again (More → make it again in its section on Thumbnail, or Edit project → Thumbnail), then use Try again on Document.`, { cause: error });
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
const rendered = renderDocument({
|
|
@@ -69,10 +69,10 @@ function documentView(deps, context, piece) {
|
|
|
69
69
|
.prepare("SELECT recipe_context FROM revision_work WHERE id=?")
|
|
70
70
|
.get(context.work.workId);
|
|
71
71
|
if (view === undefined || row === undefined)
|
|
72
|
-
throw new Error("Slopify hit an internal error (the project version the document belongs to is missing). Use
|
|
72
|
+
throw new Error("Slopify hit an internal error (the project version the document belongs to is missing). Use Try again on Document; if it happens again, use Download diagnostics in Settings and report it.");
|
|
73
73
|
const recipe = executionPlan(deps, view, savedCatalogue(row.recipe_context)).recipes.find((one) => one.key === piece.key && one.fingerprint === piece.fingerprint);
|
|
74
74
|
if (recipe === undefined || recipe.unresolved)
|
|
75
|
-
throw new Error("The project changed after this document was queued, so it no longer matches the saved article. Use
|
|
75
|
+
throw new Error("The project changed after this document was queued, so it no longer matches the saved article. Use Choose what to remake (in the project's More menu) (or Try again on Document) to make it from the current version.");
|
|
76
76
|
return view;
|
|
77
77
|
}
|
|
78
78
|
function ready(view, role, workKey) {
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { outputPath } from "../storage/layout.js";
|
|
3
|
+
import { usesAmbientBed } from "../video/ambient-bed.js";
|
|
4
|
+
import { probeDurationMs } from "../video/ffmpeg.js";
|
|
5
|
+
// The long video's ambient bed as the plan takes it (`video/ambient-bed.ts`), or undefined for
|
|
6
|
+
// none. The user's own file is checked readable before the render, so a bad file is named as
|
|
7
|
+
// the ambient sound rather than as a failed render.
|
|
8
|
+
export async function exportBed(deps, context, view) {
|
|
9
|
+
const config = view.revision.config;
|
|
10
|
+
const bed = config.ambientBed;
|
|
11
|
+
if (bed === undefined || !usesAmbientBed(config))
|
|
12
|
+
return undefined;
|
|
13
|
+
const settings = {
|
|
14
|
+
levelDb: bed.levelDb,
|
|
15
|
+
fadeInSeconds: bed.fadeInSeconds,
|
|
16
|
+
tailSeconds: bed.tailSeconds,
|
|
17
|
+
};
|
|
18
|
+
if (bed.source !== "upload")
|
|
19
|
+
return { source: { kind: "noise", preset: bed.source }, ...settings };
|
|
20
|
+
const fix = "Make the video again from Play with another file under Video and style → Ambient sound, or pick Rain, Fireplace or Wind there instead.";
|
|
21
|
+
const assetId = view.revision.content.ambientBed;
|
|
22
|
+
const row = z
|
|
23
|
+
.object({ path: z.string() })
|
|
24
|
+
.safeParse(assetId === undefined
|
|
25
|
+
? undefined
|
|
26
|
+
: deps.db
|
|
27
|
+
.prepare("SELECT path FROM project_assets WHERE project_id=? AND id=?")
|
|
28
|
+
.get(context.work.projectId, assetId));
|
|
29
|
+
if (!row.success)
|
|
30
|
+
throw new Error(`The ambient sound's audio file is no longer in the project. ${fix}`);
|
|
31
|
+
const path = outputPath(deps.paths, context.work.projectId, row.data.path);
|
|
32
|
+
let durationMs;
|
|
33
|
+
try {
|
|
34
|
+
durationMs = await probeDurationMs(deps.ffmpeg, path, context.signal, deps.log);
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
context.signal.throwIfAborted();
|
|
38
|
+
throw new Error(`The ambient sound's audio file couldn't be read as audio (${error instanceof Error ? error.message : String(error)}). ${fix}`);
|
|
39
|
+
}
|
|
40
|
+
if (durationMs <= 0)
|
|
41
|
+
throw new Error(`The ambient sound's audio file holds no sound. ${fix}`);
|
|
42
|
+
return { source: { kind: "file", path }, ...settings };
|
|
43
|
+
}
|