@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,5 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
|
-
import { chmod, lchown, lstat, mkdir, readdir, rm
|
|
2
|
+
import { chmod, lchown, lstat, mkdir, readdir, rm } from "node:fs/promises";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { isMissing, syncDirectory, treeDigest } from "./tree.js";
|
|
@@ -78,10 +78,6 @@ export async function volumeOperation(args) {
|
|
|
78
78
|
await lchown("/data/home", uid, gid);
|
|
79
79
|
return { ok: true };
|
|
80
80
|
}
|
|
81
|
-
if (operation === "probe") {
|
|
82
|
-
await writeFile("/probe/container", "slopify ownership probe", { flag: "wx", mode: 0o600 });
|
|
83
|
-
return { uid: process.getuid?.(), gid: process.getgid?.() };
|
|
84
|
-
}
|
|
85
81
|
throw new Error("Unknown fixed Docker volume operation.");
|
|
86
82
|
}
|
|
87
83
|
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
package/dist/edge/docker.js
CHANGED
|
@@ -6,16 +6,21 @@ import { installHostPackage } from "../host-cli/install.js";
|
|
|
6
6
|
import { ensureBridgeToken, hasCode, prepareHostPaths } from "../host-cli/paths.js";
|
|
7
7
|
import { ensureHostService, privateRead, privateWrite } from "../host-cli/service.js";
|
|
8
8
|
import { hostCommandName, resolveHostCommand } from "../host-cli/status.js";
|
|
9
|
-
import {
|
|
9
|
+
import { hostLlmIds } from "../kernel/ports/host-cli.js";
|
|
10
10
|
export function assertManagedDockerHost(platform, uid, gid) {
|
|
11
11
|
if (platform !== "linux")
|
|
12
|
-
throw new Error("The --docker
|
|
12
|
+
throw new Error("The --docker install only works on Linux. On this system, run Slopify without Docker (npx @gentbajko/slopify), or run the compose.yaml from the Slopify repository yourself (docker compose up -d).");
|
|
13
13
|
if (uid === undefined || gid === undefined || uid === 0)
|
|
14
14
|
throw new Error("Don't run the --docker launcher with sudo or as root. Run it as your normal user, and if Docker then says permission denied, give your user access: sudo usermod -aG docker $USER, then log out and back in.");
|
|
15
15
|
}
|
|
16
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Decides whether the Docker installation uses this machine's AI CLIs, asking once for consent,
|
|
18
|
+
* before anything changes. `ensure` then installs or updates the bridge: a private helper, run as
|
|
19
|
+
* the systemd user service slopify-cli-bridge.service, at the same version as the image.
|
|
20
|
+
*/
|
|
21
|
+
export async function planDockerHostCli(options) {
|
|
17
22
|
if (options.disabled)
|
|
18
|
-
return {};
|
|
23
|
+
return { enabled: false };
|
|
19
24
|
let installed = false;
|
|
20
25
|
for (const id of hostLlmIds) {
|
|
21
26
|
try {
|
|
@@ -28,7 +33,7 @@ export async function prepareDockerHostCli(options) {
|
|
|
28
33
|
}
|
|
29
34
|
}
|
|
30
35
|
if (!installed)
|
|
31
|
-
return {};
|
|
36
|
+
return { enabled: false };
|
|
32
37
|
if (process.platform !== "linux")
|
|
33
38
|
throw new Error("Using this machine's AI CLIs from Docker needs Linux with systemd. Start with --host-cli=off to use API keys only.");
|
|
34
39
|
const uid = process.getuid?.();
|
|
@@ -53,25 +58,12 @@ export async function prepareDockerHostCli(options) {
|
|
|
53
58
|
if (!approved)
|
|
54
59
|
throw new Error("You declined, so Slopify did not set up your host AI CLIs and nothing was changed. Run the launcher again and answer y to allow it, or add --host-cli=off to start with API keys only.");
|
|
55
60
|
}
|
|
56
|
-
const inspect = () => options.runner.exec("docker", [
|
|
57
|
-
"image",
|
|
58
|
-
"inspect",
|
|
59
|
-
"--format",
|
|
60
|
-
'{{index .Config.Labels "io.slopify.host-cli-protocol"}}',
|
|
61
|
-
options.image,
|
|
62
|
-
], options.signal);
|
|
63
|
-
let image = await inspect();
|
|
64
|
-
if (image.code !== 0 || image.stdout.trim() !== String(hostCliProtocol)) {
|
|
65
|
-
const pulled = await options.runner.exec("docker", ["pull", options.image], options.signal);
|
|
66
|
-
if (pulled.code !== 0)
|
|
67
|
-
throw new Error(`Could not download the Slopify Docker image ${options.image}. Check your internet connection and that Docker is running (docker info), then try again. Nothing was changed.`);
|
|
68
|
-
image = await inspect();
|
|
69
|
-
if (image.code !== 0 || image.stdout.trim() !== String(hostCliProtocol))
|
|
70
|
-
throw new Error(`The Docker image ${options.image} is too old to use the AI CLIs on this machine. Use the latest image (unset SLOPIFY_DOCKER_IMAGE, or point it to a current release) and try again, or start with --host-cli=off.`);
|
|
71
|
-
}
|
|
72
61
|
const supported = await options.runner.exec("systemctl", ["--user", "list-units", "--no-legend", "--no-pager", "slopify-cli-bridge.service"], options.signal);
|
|
73
62
|
if (supported.code !== 0)
|
|
74
63
|
throw new Error("Using this machine's AI CLIs from Docker needs systemd user services (systemctl --user), which aren't available here. Start with --host-cli=off to use API keys only, or run Slopify without Docker: npx @gentbajko/slopify.");
|
|
64
|
+
return { enabled: true, ensure: () => ensureDockerHostCli(options, uid, receiptPath) };
|
|
65
|
+
}
|
|
66
|
+
async function ensureDockerHostCli(options, uid, receiptPath) {
|
|
75
67
|
const paths = await prepareHostPaths(options.root);
|
|
76
68
|
const lock = join(paths.root, "setup.lock");
|
|
77
69
|
const deadline = Date.now() + 30_000;
|
|
@@ -107,7 +99,7 @@ export async function prepareDockerHostCli(options) {
|
|
|
107
99
|
env: options.env,
|
|
108
100
|
});
|
|
109
101
|
await privateWrite(receiptPath, JSON.stringify({ version: 1, automaticStartup: true }));
|
|
110
|
-
return
|
|
102
|
+
return paths.share;
|
|
111
103
|
}
|
|
112
104
|
finally {
|
|
113
105
|
await rmdir(lock);
|
package/dist/edge/events/hub.js
CHANGED
|
@@ -72,7 +72,9 @@ export function createHub(deps) {
|
|
|
72
72
|
for (const subscriber of projects.get(projectId) ?? []) {
|
|
73
73
|
send(subscriber, event);
|
|
74
74
|
}
|
|
75
|
-
if (event.type === "project.state" ||
|
|
75
|
+
if (event.type === "project.state" ||
|
|
76
|
+
event.type === "project.updated" ||
|
|
77
|
+
event.type === "review.flagged") {
|
|
76
78
|
for (const subscriber of globals)
|
|
77
79
|
send(subscriber, event);
|
|
78
80
|
}
|
package/dist/edge/host-cli.js
CHANGED
|
@@ -12,7 +12,7 @@ import { startHostServer } from "../host-cli/server.js";
|
|
|
12
12
|
import { resolveHostCommand } from "../host-cli/status.js";
|
|
13
13
|
import { readVersion } from "../kernel/version.js";
|
|
14
14
|
import { nodeCliProbe } from "../slices/settings/cli-status.js";
|
|
15
|
-
import { dockerRoot } from "./docker-
|
|
15
|
+
import { dockerRoot } from "./docker-install/state.js";
|
|
16
16
|
const { values } = parseArgs({ options: { "state-dir": { type: "string" } }, strict: true });
|
|
17
17
|
if (!values["state-dir"])
|
|
18
18
|
throw new Error("A host helper state directory is required.");
|
|
@@ -11,6 +11,7 @@ import { revisionControlSchema, } from "../../slices/control/revision-control-sc
|
|
|
11
11
|
import { recoverProject } from "../../slices/rebuild/recovery.js";
|
|
12
12
|
import { recoveryResultSchema } from "../../slices/rebuild/recovery-model.js";
|
|
13
13
|
import { resumable } from "../../slices/rebuild/recovery-repo.js";
|
|
14
|
+
import { clearSoftening, refusedKeys, requestSoftening } from "../../slices/rebuild/soften.js";
|
|
14
15
|
import { adoptBaseline } from "../../slices/revisions/adopt.js";
|
|
15
16
|
import { currentRevisionId } from "../../slices/revisions/repo.js";
|
|
16
17
|
import { allowsCustomModel } from "../../slices/settings/models.js";
|
|
@@ -59,7 +60,12 @@ export function actionRoutes(deps) {
|
|
|
59
60
|
...reruns,
|
|
60
61
|
runner: deps.runner,
|
|
61
62
|
emit: (projectId, event) => deps.hub.emit(projectId, event),
|
|
62
|
-
providers: () => providerStatuses({
|
|
63
|
+
providers: () => providerStatuses({
|
|
64
|
+
db: deps.db,
|
|
65
|
+
probe: deps.probe,
|
|
66
|
+
hostCliStatus: deps.hostCliStatus,
|
|
67
|
+
host: deps.speechHost,
|
|
68
|
+
}),
|
|
63
69
|
allowsCustomModels: deps.catalogue ? () => false : allowsCustomModel,
|
|
64
70
|
modelsFor: deps.modelsFor ?? (() => Promise.reject(new Error("Model catalog unavailable"))),
|
|
65
71
|
};
|
|
@@ -99,7 +105,7 @@ export function actionRoutes(deps) {
|
|
|
99
105
|
"revision-required": "This page is out of date. Reload the page, then try again.",
|
|
100
106
|
conflict: "This project changed since the page loaded. Reload the page to see the latest, then try again.",
|
|
101
107
|
"idempotency-conflict": "This action was already sent with different details. Reload the page, then try again.",
|
|
102
|
-
"rebuild-required": "This change needs some outputs to be made again. In the Edit tab, use
|
|
108
|
+
"rebuild-required": "This change needs some outputs to be made again. In the Edit tab, use Choose what to remake (in the project's More menu) to see what will be regenerated and what it costs.",
|
|
103
109
|
};
|
|
104
110
|
return problem(c, {
|
|
105
111
|
status: codes[result.reason],
|
|
@@ -130,14 +136,14 @@ export function actionRoutes(deps) {
|
|
|
130
136
|
const messages = {
|
|
131
137
|
"no-project": "This project no longer exists. Go back to Projects to pick another.",
|
|
132
138
|
"no-revision": "The project's saved settings changed since the page loaded. Reload the page, then try again.",
|
|
133
|
-
conflict: "This project changed since the page loaded. Reload the page to see the latest, then use
|
|
139
|
+
conflict: "This project changed since the page loaded. Reload the page to see the latest, then use Continue the run again.",
|
|
134
140
|
"idempotency-conflict": "This action was already sent with different details. Reload the page, then try again.",
|
|
135
141
|
"invalid-edit": "This section has settings that need fixing first. Open the Edit tab, choose Edit project and correct this section, then try again.",
|
|
136
|
-
"stale-preview": "What needs to be made changed since the page loaded. Reload the page, then use
|
|
142
|
+
"stale-preview": "What needs to be made changed since the page loaded. Reload the page, then use Continue the run again.",
|
|
137
143
|
"invalid-selection": "This section has nothing generated to re-run because its content was supplied by you. To change it, open the Edit tab and choose Edit project.",
|
|
138
|
-
"review-required": "Content you supplied or captions you edited by hand would be replaced. Change them in the Edit tab with Edit project, or use
|
|
139
|
-
"cost-ack-required": "This re-run needs you to review its cost first. In the Edit tab, use
|
|
140
|
-
readiness: "Slopify cannot run this yet. Check the provider, model, voice and any files you supplied, then use
|
|
144
|
+
"review-required": "Content you supplied or captions you edited by hand would be replaced. Change them in the Edit tab with Edit project, or use Choose what to remake (in the project's More menu) to confirm the replacement.",
|
|
145
|
+
"cost-ack-required": "This re-run needs you to review its cost first. In the Edit tab, use Choose what to remake (in the project's More menu).",
|
|
146
|
+
readiness: "Slopify cannot run this yet. Check the provider, model, voice and any files you supplied, then use Continue the run.",
|
|
141
147
|
running: "This section is still running. Wait for it to finish, or Pause the project, before re-running it.",
|
|
142
148
|
"accepted-job": "This section has a finished provider job that Slopify has not collected yet.",
|
|
143
149
|
"control-changed": "Another action on this project (such as Pause or Cancel) happened at the same time. Reload the page to see its current state, then try again.",
|
|
@@ -148,7 +154,7 @@ export function actionRoutes(deps) {
|
|
|
148
154
|
...(result.intentRevisionId === undefined
|
|
149
155
|
? []
|
|
150
156
|
: [
|
|
151
|
-
"Your re-run request was saved. Use
|
|
157
|
+
"Your re-run request was saved. Use Continue the run to continue it instead of re-running again.",
|
|
152
158
|
]),
|
|
153
159
|
].join(" ");
|
|
154
160
|
return problem(c, {
|
|
@@ -184,7 +190,7 @@ export function actionRoutes(deps) {
|
|
|
184
190
|
? parsed.data
|
|
185
191
|
: controlled(c, id, { ok: false, reason: "revision-required" });
|
|
186
192
|
};
|
|
187
|
-
return new Hono()
|
|
193
|
+
return (new Hono()
|
|
188
194
|
.post("/:id/pause", zValidator("param", idParam, onInvalid), async (c) => {
|
|
189
195
|
const { id } = c.req.valid("param");
|
|
190
196
|
const input = await controlInput(c, id);
|
|
@@ -208,19 +214,40 @@ export function actionRoutes(deps) {
|
|
|
208
214
|
kind: "retry",
|
|
209
215
|
stage: c.req.valid("param").kind,
|
|
210
216
|
}))
|
|
217
|
+
// Soften and retry: the refused image prompts of this stage are reworded by the project's
|
|
218
|
+
// AI model when the retry draws them again (`slices/rebuild/soften.ts`).
|
|
219
|
+
.post("/:id/stages/:kind/soften", zValidator("param", stageParam, onInvalid), zValidator("json", revisionControlSchema, onInvalid), async (c) => {
|
|
220
|
+
const { id, kind } = c.req.valid("param");
|
|
221
|
+
const keys = kind === "images" || kind === "thumbnail" ? refusedKeys(deps.db, id, kind) : [];
|
|
222
|
+
if (keys.length === 0)
|
|
223
|
+
return problem(c, {
|
|
224
|
+
status: 409,
|
|
225
|
+
title: titleOf(409),
|
|
226
|
+
detail: "Nothing in this section was refused by a content filter, so there is no prompt to soften. Reload the page; if the section still shows an error, use Try again.",
|
|
227
|
+
extensions: { reason: "nothing-refused" },
|
|
228
|
+
});
|
|
229
|
+
requestSoftening(deps.db, id, keys, deps.clock.now().toISOString());
|
|
230
|
+
const response = await recovery(c, id, c.req.valid("json"), {
|
|
231
|
+
kind: "retry",
|
|
232
|
+
stage: kind,
|
|
233
|
+
});
|
|
234
|
+
if (response.status !== 202)
|
|
235
|
+
clearSoftening(deps.db, id, keys);
|
|
236
|
+
return response;
|
|
237
|
+
})
|
|
211
238
|
.post("/:id/stages/:kind/rerun", zValidator("param", stageParam, onInvalid), zValidator("json", revisionControlSchema, onInvalid), (c) => recovery(c, c.req.valid("param").id, c.req.valid("json"), {
|
|
212
239
|
kind: "rerun",
|
|
213
240
|
stage: c.req.valid("param").kind,
|
|
214
241
|
}))
|
|
215
242
|
.put("/:id/article", zValidator("param", idParam, onInvalid), revisionRequired)
|
|
216
243
|
.delete("/:id/images/:outputId", zValidator("param", imageParam, onInvalid), revisionRequired)
|
|
217
|
-
.post("/:id/images/:outputId/regenerate", zValidator("param", imageParam, onInvalid), revisionRequired);
|
|
244
|
+
.post("/:id/images/:outputId/regenerate", zValidator("param", imageParam, onInvalid), revisionRequired));
|
|
218
245
|
}
|
|
219
246
|
function revisionRequired(c) {
|
|
220
247
|
return problem(c, {
|
|
221
248
|
status: 409,
|
|
222
249
|
title: titleOf(409),
|
|
223
|
-
detail: "This change is now made from the Edit tab. Choose Edit project, save your change, then use
|
|
250
|
+
detail: "This change is now made from the Edit tab. Choose Edit project, save your change, then use Choose what to remake (in the project's More menu).",
|
|
224
251
|
extensions: { reason: "revision-required" },
|
|
225
252
|
});
|
|
226
253
|
}
|
package/dist/edge/http/app.js
CHANGED
|
@@ -2,9 +2,14 @@ import { existsSync } from "node:fs";
|
|
|
2
2
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
3
3
|
import { Hono } from "hono";
|
|
4
4
|
import { streamSSE } from "hono/streaming";
|
|
5
|
+
import { isSampleProject } from "../../slices/onboarding/sample.js";
|
|
5
6
|
import { actionRoutes } from "./actions.js";
|
|
6
7
|
import { audioPreviewRoutes } from "./audio-preview.js";
|
|
8
|
+
import { auditionRoutes } from "./auditions.js";
|
|
9
|
+
import { autostartRoutes } from "./autostart.js";
|
|
7
10
|
import { backupRoutes } from "./backups.js";
|
|
11
|
+
import { channelMemoryRoutes } from "./channel-memory.js";
|
|
12
|
+
import { channelRoutes } from "./channels.js";
|
|
8
13
|
import { checkpointRoutes } from "./checkpoints.js";
|
|
9
14
|
import { diagnosticsRoutes } from "./diagnostics.js";
|
|
10
15
|
import { documentThemeRoutes } from "./document-themes.js";
|
|
@@ -12,7 +17,11 @@ import { draftRoutes } from "./drafts.js";
|
|
|
12
17
|
import { entryRoutes } from "./entries.js";
|
|
13
18
|
import { fileRoutes } from "./files.js";
|
|
14
19
|
import { fontsRoutes } from "./fonts.js";
|
|
20
|
+
import { homeRoutes, uploadedRoutes } from "./home.js";
|
|
21
|
+
import { narrationPeakRoutes } from "./narration-peaks.js";
|
|
22
|
+
import { onboardingRoutes } from "./onboarding.js";
|
|
15
23
|
import { openFolderRoutes } from "./open-folder.js";
|
|
24
|
+
import { patchNotesRoutes } from "./patch-notes.js";
|
|
16
25
|
import { planningRoutes } from "./planning.js";
|
|
17
26
|
import { problem, problemFromError, titleOf } from "./problem.js";
|
|
18
27
|
import { projectTemplateRoutes } from "./project-templates.js";
|
|
@@ -20,17 +29,26 @@ import { projectRoutes } from "./projects.js";
|
|
|
20
29
|
import { promptRoutes } from "./prompts.js";
|
|
21
30
|
import { pronunciationRoutes } from "./pronunciations.js";
|
|
22
31
|
import { providerRoutes } from "./providers.js";
|
|
32
|
+
import { reviewRoutes } from "./reviews.js";
|
|
23
33
|
import { revisionFileRoutes, revisionFolderRoutes } from "./revision-files.js";
|
|
24
34
|
import { revisionRoutes } from "./revisions.js";
|
|
25
|
-
import {
|
|
35
|
+
import { runCostRoutes } from "./run-cost.js";
|
|
36
|
+
import { calendarRoutes, scheduleRoutes } from "./schedules.js";
|
|
26
37
|
import { settingsRoutes } from "./settings.js";
|
|
27
38
|
import { stagingRoutes } from "./staging.js";
|
|
28
39
|
import { storageRoutes } from "./storage.js";
|
|
40
|
+
import { filesRoutes } from "./storage-files.js";
|
|
41
|
+
import { studioRoutes } from "./studio.js";
|
|
42
|
+
import { stylePreviewRoutes } from "./style-preview.js";
|
|
29
43
|
import { subtitleRoutes } from "./subtitles.js";
|
|
30
44
|
import { telemetryRoutes } from "./telemetry.js";
|
|
45
|
+
import { trashRoutes } from "./trash.js";
|
|
31
46
|
import { tutorialRoutes } from "./tutorial.js";
|
|
47
|
+
import { tutorialPagesRoutes } from "./tutorials.js";
|
|
32
48
|
import { updateRoutes } from "./update.js";
|
|
33
49
|
import { usageRoutes } from "./usage.js";
|
|
50
|
+
import { whatsNewRoutes } from "./whats-new.js";
|
|
51
|
+
import { youtubeEditRoutes } from "./youtube-edits.js";
|
|
34
52
|
const notBuilt = "The Slopify UI is not built yet. The API is running: try /api/health.\n" +
|
|
35
53
|
"Build the SPA with `npm run build` at the repository root.\n";
|
|
36
54
|
function apiRoutes(deps, startedAt) {
|
|
@@ -41,34 +59,53 @@ function apiRoutes(deps, startedAt) {
|
|
|
41
59
|
uptimeMs: deps.clock.now().getTime() - startedAt,
|
|
42
60
|
}))
|
|
43
61
|
.route("/staging", stagingRoutes(deps))
|
|
62
|
+
.route("/storage/files", filesRoutes(deps))
|
|
44
63
|
.route("/storage", storageRoutes(deps))
|
|
45
64
|
.route("/backups", backupRoutes(deps.backups))
|
|
65
|
+
.route("/trash", trashRoutes(deps))
|
|
46
66
|
.route("/drafts", draftRoutes(deps.drafts))
|
|
47
67
|
.route("/diagnostics", diagnosticsRoutes(deps))
|
|
48
68
|
.route("/project-templates", projectTemplateRoutes(deps.drafts))
|
|
49
69
|
.route("/schedules", scheduleRoutes(deps.schedules))
|
|
70
|
+
.route("/channels", channelRoutes(deps))
|
|
71
|
+
.route("/channels", channelMemoryRoutes(deps))
|
|
72
|
+
.route("/calendar", calendarRoutes(deps.schedules))
|
|
50
73
|
.route("/projects", planningRoutes(deps))
|
|
51
74
|
.route("/projects", projectRoutes(deps))
|
|
52
75
|
.route("/projects", checkpointRoutes(deps))
|
|
76
|
+
.route("/projects", reviewRoutes(deps))
|
|
53
77
|
.route("/projects", revisionRoutes(deps))
|
|
54
78
|
.route("/projects", revisionFolderRoutes(deps))
|
|
55
79
|
.route("/projects", openFolderRoutes(deps))
|
|
56
80
|
.route("/projects", audioPreviewRoutes(deps))
|
|
81
|
+
.route("/projects", narrationPeakRoutes(deps))
|
|
82
|
+
.route("/projects", runCostRoutes(deps))
|
|
83
|
+
.route("/projects", uploadedRoutes(deps))
|
|
84
|
+
.route("/home", homeRoutes(deps))
|
|
57
85
|
.route("/update", updateRoutes(deps))
|
|
58
86
|
// The re-run and cancel actions sit on the same prefix as the project itself; they
|
|
59
87
|
// are their own router because they are their own concern.
|
|
60
88
|
.route("/projects", actionRoutes(deps))
|
|
61
89
|
.route("/projects", subtitleRoutes(deps))
|
|
90
|
+
.route("/projects", youtubeEditRoutes(deps))
|
|
62
91
|
.route("/fonts", fontsRoutes(deps))
|
|
63
92
|
.route("/prompts", promptRoutes(deps))
|
|
64
93
|
.route("/entries", entryRoutes(deps))
|
|
65
94
|
.route("/document-themes", documentThemeRoutes(deps))
|
|
66
95
|
.route("/pronunciations", pronunciationRoutes(deps))
|
|
96
|
+
.route("/auditions", auditionRoutes(deps))
|
|
67
97
|
.route("/telemetry", telemetryRoutes(deps))
|
|
68
98
|
.route("/usage", usageRoutes(deps))
|
|
99
|
+
.route("/settings/autostart", autostartRoutes(deps.autostart))
|
|
69
100
|
.route("/settings", settingsRoutes(deps))
|
|
101
|
+
.route("/studio", studioRoutes(deps))
|
|
102
|
+
.route("/style-preview", stylePreviewRoutes(deps))
|
|
70
103
|
.route("/tutorial", tutorialRoutes(deps))
|
|
71
|
-
.route("/
|
|
104
|
+
.route("/whats-new", whatsNewRoutes(deps))
|
|
105
|
+
.route("/patch-notes", patchNotesRoutes(deps))
|
|
106
|
+
.route("/tutorials", tutorialPagesRoutes(deps))
|
|
107
|
+
.route("/providers", providerRoutes(deps))
|
|
108
|
+
.route("/onboarding", onboardingRoutes(deps)));
|
|
72
109
|
}
|
|
73
110
|
export function createApp(deps) {
|
|
74
111
|
const startedAt = deps.clock.now().getTime();
|
|
@@ -117,6 +154,24 @@ export function createApp(deps) {
|
|
|
117
154
|
releaseUpdate?.();
|
|
118
155
|
releaseRequest?.();
|
|
119
156
|
}
|
|
157
|
+
})
|
|
158
|
+
// The bundled sample is read-only, so exploring it never reaches a paid provider: only
|
|
159
|
+
// what reads or previews it is let through.
|
|
160
|
+
.use("/api/projects/:id/*", async (c, next) => {
|
|
161
|
+
if (["GET", "HEAD", "OPTIONS"].includes(c.req.method))
|
|
162
|
+
return next();
|
|
163
|
+
const id = c.req.param("id");
|
|
164
|
+
// Deleting the sample is allowed: Settings → Restore sample brings it back.
|
|
165
|
+
if (!isSampleProject(deps.db, id) ||
|
|
166
|
+
sampleReadOnlyExempt.test(c.req.path) ||
|
|
167
|
+
c.req.path.replace(/\/$/, "") === `/api/projects/${id}`)
|
|
168
|
+
return next();
|
|
169
|
+
return problem(c, {
|
|
170
|
+
status: 409,
|
|
171
|
+
title: titleOf(409),
|
|
172
|
+
detail: "This is the sample project, which is read-only so trying things on it never spends anything. Press Make my own copy at the top of the sample's page, then edit and rebuild the copy.",
|
|
173
|
+
extensions: { reason: "sample-read-only" },
|
|
174
|
+
});
|
|
120
175
|
})
|
|
121
176
|
.onError((error, c) => problemFromError(c, error, deps))
|
|
122
177
|
.notFound(missing)
|
|
@@ -141,6 +196,9 @@ export function createApp(deps) {
|
|
|
141
196
|
}
|
|
142
197
|
return app;
|
|
143
198
|
}
|
|
199
|
+
// What the sample's page may still do: prepare its revision view, preview a rebuild (which
|
|
200
|
+
// starts nothing) and open its folder.
|
|
201
|
+
const sampleReadOnlyExempt = /\/(revisions\/prepare|rebuild\/preview|open-folder)\/?$/;
|
|
144
202
|
function missing(c) {
|
|
145
203
|
return problem(c, {
|
|
146
204
|
status: 404,
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { zValidator } from "@hono/zod-validator";
|
|
2
|
+
import { Hono } from "hono";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { isProviderError } from "../../kernel/ports/model.js";
|
|
5
|
+
import { estimateRequests } from "../../slices/estimate/index.js";
|
|
6
|
+
import { auditionLineMax } from "../../slices/voices/audition.js";
|
|
7
|
+
import { onInvalid, problem } from "./problem.js";
|
|
8
|
+
const line = z.object({
|
|
9
|
+
provider: z.string().min(1).max(100),
|
|
10
|
+
model: z.string().min(1).max(200),
|
|
11
|
+
text: z.string().trim().min(1).max(auditionLineMax),
|
|
12
|
+
});
|
|
13
|
+
const quoteBody = z.object({
|
|
14
|
+
lines: z
|
|
15
|
+
.array(line.extend({ speaker: z.string().max(100) }))
|
|
16
|
+
.min(1)
|
|
17
|
+
.max(10),
|
|
18
|
+
});
|
|
19
|
+
const speakBody = line.extend({
|
|
20
|
+
voice: z.string().min(1).max(200),
|
|
21
|
+
// The page sends this only from the Audition button, after showing the price.
|
|
22
|
+
confirmed: z.literal(true),
|
|
23
|
+
});
|
|
24
|
+
export function auditionRoutes(deps) {
|
|
25
|
+
return new Hono()
|
|
26
|
+
.post("/quote", zValidator("json", quoteBody, onInvalid), (c) => {
|
|
27
|
+
const { lines } = c.req.valid("json");
|
|
28
|
+
if (deps.catalogue === undefined)
|
|
29
|
+
return c.json({ estimate: null });
|
|
30
|
+
const estimate = estimateRequests(lines.map((one) => ({
|
|
31
|
+
kind: "tts",
|
|
32
|
+
stage: one.speaker,
|
|
33
|
+
provider: one.provider,
|
|
34
|
+
model: one.model,
|
|
35
|
+
text: one.text,
|
|
36
|
+
})), deps.catalogue.read());
|
|
37
|
+
return c.json({ estimate });
|
|
38
|
+
})
|
|
39
|
+
.post("/", zValidator("json", speakBody, onInvalid), async (c) => {
|
|
40
|
+
const body = c.req.valid("json");
|
|
41
|
+
if (deps.audition === undefined)
|
|
42
|
+
return problem(c, {
|
|
43
|
+
status: 500,
|
|
44
|
+
title: "Internal Server Error",
|
|
45
|
+
detail: "Voice auditions aren't available in this build. Start the run and listen to the narration in the project instead.",
|
|
46
|
+
});
|
|
47
|
+
try {
|
|
48
|
+
const spoken = await deps.audition({ provider: body.provider, model: body.model, voiceId: body.voice, text: body.text }, c.req.raw.signal);
|
|
49
|
+
if (!spoken.ok)
|
|
50
|
+
return problem(c, {
|
|
51
|
+
status: 409,
|
|
52
|
+
title: "Conflict",
|
|
53
|
+
detail: "The audition was held back. Try the Audition button again in a moment.",
|
|
54
|
+
});
|
|
55
|
+
return c.body(spoken.value.bytes.slice().buffer, 200, {
|
|
56
|
+
"content-type": "audio/mpeg",
|
|
57
|
+
"cache-control": "no-store",
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
if (isProviderError(error))
|
|
62
|
+
return problem(c, {
|
|
63
|
+
status: 502,
|
|
64
|
+
title: "Bad Gateway",
|
|
65
|
+
detail: `The audition couldn't be spoken: ${error.message}`,
|
|
66
|
+
});
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { zValidator } from "@hono/zod-validator";
|
|
2
|
+
import { Hono } from "hono";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { AutostartRefusal } from "../autostart/service.js";
|
|
5
|
+
import { onInvalid, problem, titleOf } from "./problem.js";
|
|
6
|
+
const body = z.object({ enabled: z.boolean() });
|
|
7
|
+
// Settings → General's "Start Slopify when I log in", and the first-run screen's offer of it.
|
|
8
|
+
// In Docker the view says whether Docker starts at login, and turning it on or off is refused
|
|
9
|
+
// with where Docker's own setting is.
|
|
10
|
+
export function autostartRoutes(service) {
|
|
11
|
+
const unavailable = "Starting at login can't be set from this Slopify. Start it with npx @gentbajko/slopify and try Settings → General again.";
|
|
12
|
+
return (new Hono()
|
|
13
|
+
.get("/", async (c) => {
|
|
14
|
+
if (service === undefined)
|
|
15
|
+
return problem(c, { status: 404, title: titleOf(404), detail: unavailable });
|
|
16
|
+
c.header("Cache-Control", "no-store");
|
|
17
|
+
return c.json(await service.view());
|
|
18
|
+
})
|
|
19
|
+
.put("/", zValidator("json", body, onInvalid), async (c) => {
|
|
20
|
+
if (service === undefined)
|
|
21
|
+
return problem(c, { status: 404, title: titleOf(404), detail: unavailable });
|
|
22
|
+
try {
|
|
23
|
+
return c.json(await service.set(c.req.valid("json").enabled));
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
if (!(error instanceof AutostartRefusal))
|
|
27
|
+
throw error;
|
|
28
|
+
return problem(c, { status: 409, title: titleOf(409), detail: error.message });
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
// The first-run screen's No thanks: never offer it there again, change nothing.
|
|
32
|
+
.post("/answer", async (c) => {
|
|
33
|
+
if (service === undefined)
|
|
34
|
+
return problem(c, { status: 404, title: titleOf(404), detail: unavailable });
|
|
35
|
+
return c.json(await service.answer());
|
|
36
|
+
}));
|
|
37
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { createReadStream } from "node:fs";
|
|
2
|
+
import { Readable } from "node:stream";
|
|
3
|
+
// One `bytes=` range, as browsers ask for media; anything else is answered with the whole file.
|
|
4
|
+
export function byteRange(header, size) {
|
|
5
|
+
const matched = /^bytes=(\d*)-(\d*)$/.exec(header?.trim() ?? "");
|
|
6
|
+
if (matched === null)
|
|
7
|
+
return undefined;
|
|
8
|
+
const [, from = "", to = ""] = matched;
|
|
9
|
+
if (from === "" && to === "")
|
|
10
|
+
return undefined;
|
|
11
|
+
if (from === "") {
|
|
12
|
+
const suffix = Number(to);
|
|
13
|
+
if (suffix === 0)
|
|
14
|
+
return "unsatisfiable";
|
|
15
|
+
return { start: Math.max(0, size - suffix), end: size - 1 };
|
|
16
|
+
}
|
|
17
|
+
const start = Number(from);
|
|
18
|
+
const end = to === "" ? size - 1 : Math.min(Number(to), size - 1);
|
|
19
|
+
if (start >= size || end < start)
|
|
20
|
+
return "unsatisfiable";
|
|
21
|
+
return { start, end };
|
|
22
|
+
}
|
|
23
|
+
// A file on disk as a response that honours one byte range, so an audio or video element
|
|
24
|
+
// can seek: it asks for the bytes at the new time and gets 206 with just that slice. Built
|
|
25
|
+
// with `c.body` so the app's own headers (the version, among others) are still added.
|
|
26
|
+
export function fileResponse(c, path, size, headers) {
|
|
27
|
+
const base = { ...headers, "accept-ranges": "bytes" };
|
|
28
|
+
const range = byteRange(c.req.header("range"), size);
|
|
29
|
+
if (range === "unsatisfiable")
|
|
30
|
+
return c.body(null, 416, { ...base, "content-range": `bytes */${String(size)}` });
|
|
31
|
+
if (size === 0)
|
|
32
|
+
return c.body(null, 200, { ...base, "content-length": "0" });
|
|
33
|
+
const { start, end } = range ?? { start: 0, end: size - 1 };
|
|
34
|
+
const body = Readable.toWeb(createReadStream(path, { start, end }));
|
|
35
|
+
return c.body(body, range === undefined ? 200 : 206, {
|
|
36
|
+
...base,
|
|
37
|
+
"content-length": String(end - start + 1),
|
|
38
|
+
...(range === undefined
|
|
39
|
+
? {}
|
|
40
|
+
: { "content-range": `bytes ${String(start)}-${String(end)}/${String(size)}` }),
|
|
41
|
+
});
|
|
42
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { zValidator } from "@hono/zod-validator";
|
|
3
|
+
import { Hono } from "hono";
|
|
4
|
+
import { bodyLimit } from "hono/body-limit";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { clearChannelVideos, deleteChannelVideo, importChannelVideos, listChannelVideos, previewChannelVideos, } from "../../slices/channels/videos.js";
|
|
7
|
+
import { channelEpisodes, deleteMemory, editMemory, setEpisodeMemory, } from "../../slices/episodes/service.js";
|
|
8
|
+
import { onInvalid, problem, titleOf } from "./problem.js";
|
|
9
|
+
const idParam = z.object({ id: z.uuid() });
|
|
10
|
+
const memoryParam = idParam.extend({ memoryId: z.string().min(1).max(100) });
|
|
11
|
+
const videoParam = idParam.extend({ videoId: z.uuid() });
|
|
12
|
+
// A YouTube Studio export of a large channel is a few megabytes.
|
|
13
|
+
const importMaxBytes = 20 * 1024 * 1024;
|
|
14
|
+
// Channel page → Episodes (episode memory: the setting and the summaries) and Existing videos
|
|
15
|
+
// (titles made before Slopify; a CSV is previewed, then its ticked titles are saved). Mounted
|
|
16
|
+
// beside `channelRoutes` under /api/channels.
|
|
17
|
+
export function channelMemoryRoutes(deps) {
|
|
18
|
+
const service = { db: deps.db, clock: deps.clock, uuid: randomUUID };
|
|
19
|
+
return new Hono()
|
|
20
|
+
.get("/:id/episodes", zValidator("param", idParam, onInvalid), (c) => {
|
|
21
|
+
const result = channelEpisodes(service, c.req.valid("param").id);
|
|
22
|
+
return result.ok ? c.json(result.value) : refused(c, result, "episode");
|
|
23
|
+
})
|
|
24
|
+
.put("/:id/episodes/setting", zValidator("param", idParam, onInvalid), async (c) => {
|
|
25
|
+
const result = setEpisodeMemory(service, c.req.valid("param").id, await body(c));
|
|
26
|
+
return result.ok ? c.json(result.value) : refused(c, result, "episode");
|
|
27
|
+
})
|
|
28
|
+
.put("/:id/episodes/:memoryId", zValidator("param", memoryParam, onInvalid), async (c) => {
|
|
29
|
+
const { id, memoryId } = c.req.valid("param");
|
|
30
|
+
const result = editMemory(service, id, memoryId, await body(c));
|
|
31
|
+
return result.ok ? c.json(result.value) : refused(c, result, "episode");
|
|
32
|
+
})
|
|
33
|
+
.delete("/:id/episodes/:memoryId", zValidator("param", memoryParam, onInvalid), (c) => {
|
|
34
|
+
const { id, memoryId } = c.req.valid("param");
|
|
35
|
+
const result = deleteMemory(service, id, memoryId);
|
|
36
|
+
return result.ok ? c.body(null, 204) : refused(c, result, "episode");
|
|
37
|
+
})
|
|
38
|
+
.get("/:id/videos", zValidator("param", idParam, onInvalid), (c) => {
|
|
39
|
+
const result = listChannelVideos(service, c.req.valid("param").id);
|
|
40
|
+
return result.ok ? c.json({ videos: result.value }) : refused(c, result, "video");
|
|
41
|
+
})
|
|
42
|
+
.post("/:id/videos/preview", zValidator("param", idParam, onInvalid), importLimit(), async (c) => {
|
|
43
|
+
const result = previewChannelVideos(service, c.req.valid("param").id, await body(c));
|
|
44
|
+
return result.ok ? c.json(result.value) : refused(c, result, "video");
|
|
45
|
+
})
|
|
46
|
+
.post("/:id/videos", zValidator("param", idParam, onInvalid), importLimit(), async (c) => {
|
|
47
|
+
const result = importChannelVideos(service, c.req.valid("param").id, await body(c));
|
|
48
|
+
return result.ok ? c.json(result.value, 201) : refused(c, result, "video");
|
|
49
|
+
})
|
|
50
|
+
.delete("/:id/videos", zValidator("param", idParam, onInvalid), (c) => {
|
|
51
|
+
const result = clearChannelVideos(service, c.req.valid("param").id);
|
|
52
|
+
return result.ok ? c.json(result.value) : refused(c, result, "video");
|
|
53
|
+
})
|
|
54
|
+
.delete("/:id/videos/:videoId", zValidator("param", videoParam, onInvalid), (c) => {
|
|
55
|
+
const { id, videoId } = c.req.valid("param");
|
|
56
|
+
const result = deleteChannelVideo(service, id, videoId);
|
|
57
|
+
return result.ok ? c.body(null, 204) : refused(c, result, "video");
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function importLimit() {
|
|
61
|
+
return bodyLimit({
|
|
62
|
+
maxSize: importMaxBytes,
|
|
63
|
+
onError: (c) => refused(c, {
|
|
64
|
+
ok: false,
|
|
65
|
+
reason: "invalid-input",
|
|
66
|
+
message: "This file is larger than 20 MB. Export fewer videos from YouTube Studio, or paste the titles instead.",
|
|
67
|
+
}, "video"),
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
async function body(c) {
|
|
71
|
+
try {
|
|
72
|
+
return await c.req.json();
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function refused(c, result, about) {
|
|
79
|
+
const status = result.reason === "not-found" ? 404 : 400;
|
|
80
|
+
const detail = result.message ??
|
|
81
|
+
(result.reason === "not-found"
|
|
82
|
+
? about === "episode"
|
|
83
|
+
? "This channel or episode summary no longer exists; it may have been deleted in another tab. Reload the channel's Episodes tab."
|
|
84
|
+
: "This channel or video title no longer exists; it may have been deleted in another tab. Reload the channel's Existing videos tab."
|
|
85
|
+
: "This request isn't valid. Reload the page and try again.");
|
|
86
|
+
return problem(c, {
|
|
87
|
+
status,
|
|
88
|
+
title: titleOf(status),
|
|
89
|
+
detail,
|
|
90
|
+
extensions: { reason: result.reason },
|
|
91
|
+
});
|
|
92
|
+
}
|