@gentbajko/slopify 3.25.9 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/account-limits.js +30 -0
- package/dist/adapters/alignment/cache.js +3 -5
- package/dist/adapters/alignment/ctc.js +1 -2
- package/dist/adapters/alignment/lock.js +4 -3
- package/dist/adapters/alignment/multilingual.js +0 -3
- package/dist/adapters/alignment/prefetch.js +2 -1
- package/dist/adapters/alignment/worker.js +2 -1
- package/dist/adapters/explain.js +0 -3
- package/dist/adapters/ffmpeg.js +19 -1
- package/dist/adapters/host-cli/index.js +19 -3
- package/dist/adapters/http-failure.js +53 -0
- package/dist/adapters/image/codex.js +36 -4
- package/dist/adapters/image/fal.js +17 -50
- package/dist/adapters/image/google.js +31 -55
- package/dist/adapters/image/openai.js +20 -47
- package/dist/adapters/image/reference.js +36 -0
- package/dist/adapters/image/replicate.js +18 -47
- package/dist/adapters/interest/search-interest.js +113 -0
- package/dist/adapters/key-probes.js +53 -2
- package/dist/adapters/llm/antigravity.js +4 -0
- package/dist/adapters/llm/assistant-cli.js +388 -0
- package/dist/adapters/llm/child-session.js +83 -0
- package/dist/adapters/llm/claude-code-models.js +51 -98
- package/dist/adapters/llm/claude-code.js +13 -2
- package/dist/adapters/llm/codex-limits.js +27 -68
- package/dist/adapters/llm/codex-server-models.js +61 -109
- package/dist/adapters/llm/codex.js +12 -1
- package/dist/adapters/llm/openrouter.js +10 -47
- package/dist/adapters/llm/run-cli.js +6 -22
- package/dist/adapters/llm/sse-lines.js +5 -1
- package/dist/adapters/model-access.js +37 -0
- package/dist/adapters/tts/cartesia.js +15 -46
- package/dist/adapters/tts/elevenlabs.js +20 -52
- package/dist/adapters/tts/gemini.js +19 -40
- package/dist/adapters/tts/inworld-async.js +4 -1
- package/dist/adapters/tts/inworld.js +3 -0
- package/dist/adapters/tts/openai.js +20 -50
- package/dist/assets/models-lifecycle.yaml +6 -0
- package/dist/assistant-wiring.js +60 -0
- package/dist/autopilot-timers.js +59 -0
- package/dist/catalog/lifecycle.js +52 -0
- package/dist/catalog/propose.js +204 -0
- package/dist/catalog/registry.js +3 -1
- package/dist/catalog/schema.js +2 -0
- package/dist/catalog/store.js +39 -0
- package/dist/edge/autostart/native.js +3 -5
- package/dist/edge/autostart/prompt.js +2 -1
- package/dist/edge/autostart/service.js +3 -4
- package/dist/edge/cli-args.js +3 -4
- package/dist/edge/cli.js +2 -1
- package/dist/edge/docker-install/apply.js +11 -11
- package/dist/edge/docker-install/run.js +2 -1
- package/dist/edge/docker-install/state.js +4 -3
- package/dist/edge/docker-install/tree.js +2 -4
- package/dist/edge/docker-install/volume.js +3 -2
- package/dist/edge/docker.js +5 -4
- package/dist/edge/events/hub.js +5 -5
- package/dist/edge/http/actions.js +4 -25
- package/dist/edge/http/actor.js +5 -3
- package/dist/edge/http/app.js +85 -10
- package/dist/edge/http/assistant.js +127 -0
- package/dist/edge/http/audio-preview.js +4 -11
- package/dist/edge/http/auditions.js +1 -1
- package/dist/edge/http/autostart.js +5 -5
- package/dist/edge/http/backups.js +1 -3
- package/dist/edge/http/channel-memory.js +7 -20
- package/dist/edge/http/channels.js +13 -26
- package/dist/edge/http/checkpoints.js +33 -17
- package/dist/edge/http/design-templates.js +167 -0
- package/dist/edge/http/designs.js +178 -0
- package/dist/edge/http/diagnostics.js +5 -1
- package/dist/edge/http/document-themes.js +2 -11
- package/dist/edge/http/draft-files.js +1 -3
- package/dist/edge/http/draft-problem.js +4 -9
- package/dist/edge/http/drafts.js +3 -3
- package/dist/edge/http/entries.js +6 -9
- package/dist/edge/http/extension-auth.js +27 -0
- package/dist/edge/http/external-reviews.js +12 -11
- package/dist/edge/http/files.js +5 -11
- package/dist/edge/http/folder-location.js +1 -4
- package/dist/edge/http/fonts.js +0 -7
- package/dist/edge/http/home.js +38 -13
- package/dist/edge/http/host-cli.js +18 -8
- package/dist/edge/http/ids.js +8 -0
- package/dist/edge/http/keywords.js +35 -0
- package/dist/edge/http/model-checks.js +132 -0
- package/dist/edge/http/narration-peaks.js +5 -13
- package/dist/edge/http/onboarding.js +1 -14
- package/dist/edge/http/open-folder.js +5 -10
- package/dist/edge/http/outputs.js +4 -12
- package/dist/edge/http/patch-notes.js +4 -10
- package/dist/edge/http/planning.js +2 -6
- package/dist/edge/http/problem.js +2 -4
- package/dist/edge/http/project-create.js +1 -0
- package/dist/edge/http/project-templates.js +29 -17
- package/dist/edge/http/projects.js +2 -12
- package/dist/edge/http/prompts.js +7 -14
- package/dist/edge/http/pronunciations.js +1 -2
- package/dist/edge/http/providers.js +39 -24
- package/dist/edge/http/refusal.js +29 -0
- package/dist/edge/http/remote-access.js +40 -0
- package/dist/edge/http/remote-guard.js +71 -0
- package/dist/edge/http/request-guard.js +91 -0
- package/dist/edge/http/reviews.js +11 -24
- package/dist/edge/http/revision-files.js +5 -10
- package/dist/edge/http/revisions.js +10 -15
- package/dist/edge/http/run-cost.js +1 -2
- package/dist/edge/http/same-page.js +80 -0
- package/dist/edge/http/schedules.js +59 -32
- package/dist/edge/http/settings.js +22 -13
- package/dist/edge/http/social.js +282 -0
- package/dist/edge/http/staging.js +2 -4
- package/dist/edge/http/storage-files.js +5 -7
- package/dist/edge/http/storage.js +23 -23
- package/dist/edge/http/studio.js +23 -50
- package/dist/edge/http/style-preview.js +1 -4
- package/dist/edge/http/subtitles.js +3 -17
- package/dist/edge/http/template-studio.js +433 -0
- package/dist/edge/http/trash.js +10 -11
- package/dist/edge/http/tutorial.js +1 -2
- package/dist/edge/http/tutorials.js +5 -11
- package/dist/edge/http/update.js +7 -13
- package/dist/edge/http/waveform.js +3 -6
- package/dist/edge/http/youtube-edits.js +2 -3
- package/dist/edge/mcp/assistant.js +279 -0
- package/dist/edge/mcp/attribution.js +8 -2
- package/dist/edge/mcp/call-route.js +11 -2
- package/dist/edge/mcp/excluded-routes.js +25 -0
- package/dist/edge/mcp/http.js +11 -2
- package/dist/edge/mcp/instructions.js +5 -3
- package/dist/edge/mcp/library-tools.js +1 -0
- package/dist/edge/mcp/route-tool.js +35 -15
- package/dist/edge/mcp/server.js +13 -8
- package/dist/edge/mcp/tools-channels.js +2 -0
- package/dist/edge/mcp/tools-designs.js +165 -0
- package/dist/edge/mcp/tools-library.js +14 -2
- package/dist/edge/mcp/tools-planning.js +23 -4
- package/dist/edge/mcp/tools-projects.js +28 -1
- package/dist/edge/mcp/tools-runs.js +14 -1
- package/dist/edge/mcp/tools-schedules.js +28 -4
- package/dist/edge/mcp/tools-setup.js +61 -3
- package/dist/edge/mcp/tools-social.js +46 -0
- package/dist/edge/mcp/tools-storage.js +8 -0
- package/dist/edge/mcp/tools-template-studio.js +166 -0
- package/dist/edge/mcp/tools.js +6 -0
- package/dist/edge/remote-access/cli.js +71 -0
- package/dist/edge/remote-access/model.js +32 -0
- package/dist/edge/remote-access/rules.js +207 -0
- package/dist/edge/remote-access/service.js +203 -0
- package/dist/edge/remote-access/store.js +44 -0
- package/dist/edge/remote-access/views.js +43 -0
- package/dist/extension/slopify-studio-chrome.zip +0 -0
- package/dist/extension/slopify-studio-firefox.zip +0 -0
- package/dist/host-cli/install.js +2 -2
- package/dist/host-cli/open-folder.js +2 -2
- package/dist/host-cli/paths.js +2 -4
- package/dist/host-cli/server.js +4 -4
- package/dist/host-cli/service.js +3 -3
- package/dist/host-cli/status.js +7 -7
- package/dist/kernel/db/migrations/0063-model-checks.sql +20 -0
- package/dist/kernel/db/migrations/0064-template-studio.sql +46 -0
- package/dist/kernel/db/migrations/0065-promise-and-assistant-reviews.sql +58 -0
- package/dist/kernel/db/migrations/0066-assistant.sql +44 -0
- package/dist/kernel/db/migrations/0067-autopilot.sql +6 -0
- package/dist/kernel/db/migrations/0068-topic-scores.sql +17 -0
- package/dist/kernel/db/migrations/0070-designs.sql +53 -0
- package/dist/kernel/db/migrations/0071-design-usage.sql +33 -0
- package/dist/kernel/db/migrations/0072-design-templates.sql +33 -0
- package/dist/kernel/db/migrations/0073-schedule-design-kind.sql +5 -0
- package/dist/kernel/db/migrations/0074-template-bakeoffs.sql +44 -0
- package/dist/kernel/db/migrations/0075-social-posts.sql +25 -0
- package/dist/kernel/db/migrations/0076-channel-social.sql +5 -0
- package/dist/kernel/db/migrations/0077-post-prompts.sql +17 -0
- package/dist/kernel/db/tx.js +2 -4
- package/dist/kernel/errors.js +27 -1
- package/dist/kernel/json-answer.js +15 -0
- package/dist/kernel/lock.js +4 -9
- package/dist/kernel/paths.js +8 -0
- package/dist/kernel/ports/assistant-cli.js +4 -0
- package/dist/kernel/ports/host-cli.js +3 -0
- package/dist/kernel/ports/interest.js +4 -0
- package/dist/kernel/ports/languages.js +0 -3
- package/dist/kernel/ports/model.js +2 -0
- package/dist/kernel/runner/attempt.js +3 -1
- package/dist/kernel/runner/audition.js +3 -1
- package/dist/kernel/runner/checkpoint-authority.js +2 -1
- package/dist/kernel/runner/index.js +8 -10
- package/dist/kernel/runner/model-access.js +40 -0
- package/dist/kernel/runner/model-check.js +153 -0
- package/dist/kernel/runner/providers.js +15 -10
- package/dist/kernel/runner/standalone.js +9 -5
- package/dist/main.js +334 -19
- package/dist/model-checks.js +166 -0
- package/dist/patch-notes/4.0.0.md +51 -0
- package/dist/patch-notes/index.json +6 -0
- package/dist/slices/admission/heads.js +2 -1
- package/dist/slices/admission/repo.js +2 -1
- package/dist/slices/admission/rules.js +43 -8
- package/dist/slices/admission/schema.js +8 -0
- package/dist/slices/admission/stage-writes.js +0 -3
- package/dist/slices/admission/start.js +48 -31
- package/dist/slices/admission/substitute.js +7 -4
- package/dist/slices/agents/attention-posts.js +31 -0
- package/dist/slices/agents/attention-templates.js +33 -0
- package/dist/slices/agents/attention.js +10 -1
- package/dist/slices/agents/connection.js +2 -6
- package/dist/slices/agents/model.js +6 -3
- package/dist/slices/agents/repo.js +15 -7
- package/dist/slices/article/continuation.js +0 -63
- package/dist/slices/article/plain.js +3 -2
- package/dist/slices/article/segments.js +2 -62
- package/dist/slices/assistant/access.js +1 -0
- package/dist/slices/assistant/asks.js +80 -0
- package/dist/slices/assistant/cost.js +54 -0
- package/dist/slices/assistant/gate.js +222 -0
- package/dist/slices/assistant/model.js +79 -0
- package/dist/slices/assistant/names.js +23 -0
- package/dist/slices/assistant/prompt.js +74 -0
- package/dist/slices/assistant/repo.js +74 -0
- package/dist/slices/assistant/tokens.js +18 -0
- package/dist/slices/assistant/turn.js +342 -0
- package/dist/slices/autopilot/buffer.js +76 -0
- package/dist/slices/autopilot/model.js +14 -0
- package/dist/slices/autopilot/notify.js +27 -0
- package/dist/slices/autopilot/repo.js +34 -0
- package/dist/slices/autopilot/signals.js +194 -0
- package/dist/slices/autopilot/warnings.js +85 -0
- package/dist/slices/autopilot/weekly.js +100 -0
- package/dist/slices/backups/files.js +2 -6
- package/dist/slices/backups/repo.js +3 -15
- package/dist/slices/backups/service.js +4 -3
- package/dist/slices/channels/cast-images.js +2 -1
- package/dist/slices/channels/model.js +0 -1
- package/dist/slices/channels/rebrand.js +0 -3
- package/dist/slices/channels/repo.js +4 -0
- package/dist/slices/channels/schema.js +6 -0
- package/dist/slices/channels/service.js +10 -0
- package/dist/slices/channels/videos.js +2 -14
- package/dist/slices/checkpoints/index.js +0 -1
- package/dist/slices/control/providers.js +0 -26
- package/dist/slices/designs/artwork.js +66 -0
- package/dist/slices/designs/budget.js +23 -0
- package/dist/slices/designs/calls.js +124 -0
- package/dist/slices/designs/check-context.js +38 -0
- package/dist/slices/designs/check-text.js +43 -0
- package/dist/slices/designs/fields.js +88 -0
- package/dist/slices/designs/frames.js +34 -0
- package/dist/slices/designs/gate.js +128 -0
- package/dist/slices/designs/layout.js +293 -0
- package/dist/slices/designs/model.js +35 -0
- package/dist/slices/designs/pdf-job.js +23 -0
- package/dist/slices/designs/pdf-pages.js +30 -0
- package/dist/slices/designs/pdf-runner.js +68 -0
- package/dist/slices/designs/pdf-worker.js +24 -0
- package/dist/slices/designs/picture-size.js +79 -0
- package/dist/slices/designs/pictures.js +55 -0
- package/dist/slices/designs/quote.js +24 -0
- package/dist/slices/designs/render-args.js +36 -0
- package/dist/slices/designs/render-input.js +59 -0
- package/dist/slices/designs/render.js +215 -0
- package/dist/slices/designs/repo.js +101 -0
- package/dist/slices/designs/requests.js +80 -0
- package/dist/slices/designs/runner.js +239 -0
- package/dist/slices/designs/sample-repo.js +91 -0
- package/dist/slices/designs/sample.js +222 -0
- package/dist/slices/designs/schema.js +87 -0
- package/dist/slices/designs/sections.js +44 -0
- package/dist/slices/designs/service.js +233 -0
- package/dist/slices/designs/steps-repo.js +102 -0
- package/dist/slices/designs/studio-kind.js +39 -0
- package/dist/slices/designs/template-checks.js +44 -0
- package/dist/slices/designs/template-make.js +118 -0
- package/dist/slices/designs/templates.js +218 -0
- package/dist/slices/document/fonts.js +2 -1
- package/dist/slices/episodes/summarize.js +5 -1
- package/dist/slices/estimate/index.js +45 -7
- package/dist/slices/fonts/catalog.js +3 -2
- package/dist/slices/fonts/discovery.js +3 -2
- package/dist/slices/fonts/files.js +2 -6
- package/dist/slices/fonts/index.js +0 -1
- package/dist/slices/fonts/sfnt.js +45 -10
- package/dist/slices/images/appearance.js +6 -15
- package/dist/slices/images/scenes.js +100 -27
- package/dist/slices/keywords/digest.js +84 -0
- package/dist/slices/keywords/facts.js +92 -0
- package/dist/slices/keywords/fill.js +80 -0
- package/dist/slices/keywords/model.js +75 -0
- package/dist/slices/keywords/rules.js +187 -0
- package/dist/slices/keywords/schema.js +28 -0
- package/dist/slices/keywords/specs.js +260 -0
- package/dist/slices/keywords/values.js +74 -0
- package/dist/slices/library/lint.js +29 -12
- package/dist/slices/library/model.js +2 -0
- package/dist/slices/library/photorealistic.js +4 -12
- package/dist/slices/library/rename.js +9 -7
- package/dist/slices/library/save.js +14 -10
- package/dist/slices/library/slots.js +37 -11
- package/dist/slices/library/transfer.js +2 -1
- package/dist/slices/library/used-by.js +25 -10
- package/dist/slices/loudness/loudnorm.js +51 -11
- package/dist/slices/model-checks/cost.js +61 -0
- package/dist/slices/model-checks/listing.js +124 -0
- package/dist/slices/model-checks/model.js +20 -0
- package/dist/slices/model-checks/record.js +32 -0
- package/dist/slices/model-checks/repo.js +120 -0
- package/dist/slices/model-checks/rules.js +112 -0
- package/dist/slices/model-checks/run.js +143 -0
- package/dist/slices/model-checks/service.js +198 -0
- package/dist/slices/model-upkeep/switch.js +70 -25
- package/dist/slices/model-upkeep/usage.js +26 -11
- package/dist/slices/narration/breaths.js +2 -1
- package/dist/slices/narration/clean-pieces.js +2 -1
- package/dist/slices/narration/concat.js +2 -1
- package/dist/slices/narration/peaks.js +3 -4
- package/dist/slices/narration/pronunciation-chunks.js +4 -3
- package/dist/slices/narration/pronunciation.js +3 -2
- package/dist/slices/narration/steering.js +4 -3
- package/dist/slices/notifications/notifier.js +4 -3
- package/dist/slices/notifications/rules.js +5 -2
- package/dist/slices/notifications/settings.js +4 -12
- package/dist/slices/notifications/subject.js +2 -1
- package/dist/slices/onboarding/copy.js +2 -1
- package/dist/slices/onboarding/first-run.js +3 -2
- package/dist/slices/onboarding/full-video.js +3 -2
- package/dist/slices/onboarding/install.js +4 -3
- package/dist/slices/onboarding/model.js +0 -16
- package/dist/slices/onboarding/packs.js +22 -22
- package/dist/slices/onboarding/quick-short.js +2 -1
- package/dist/slices/onboarding/sample.js +0 -1
- package/dist/slices/onboarding/state.js +2 -11
- package/dist/slices/onboarding/template.js +2 -1
- package/dist/slices/patch-notes/library.js +3 -2
- package/dist/slices/play-drafts/convert.js +4 -0
- package/dist/slices/play-drafts/review-inputs.js +8 -2
- package/dist/slices/play-drafts/review.js +5 -4
- package/dist/slices/play-drafts/schema.js +4 -0
- package/dist/slices/project-templates/from-project.js +9 -2
- package/dist/slices/project-templates/repo.js +6 -1
- package/dist/slices/project-templates/setup.js +8 -0
- package/dist/slices/project-templates/stand-ins.js +22 -0
- package/dist/slices/rebuild/activity.js +2 -0
- package/dist/slices/rebuild/admission-repo.js +3 -2
- package/dist/slices/rebuild/narration-history.js +4 -5
- package/dist/slices/rebuild/narration-retry.js +2 -1
- package/dist/slices/rebuild/preview-details.js +27 -9
- package/dist/slices/rebuild/preview-plan.js +3 -8
- package/dist/slices/rebuild/recipe-build.js +8 -3
- package/dist/slices/rebuild/recipe-describe.js +3 -3
- package/dist/slices/rebuild/recipe-digest.js +58 -0
- package/dist/slices/rebuild/recipe-input-schema.js +10 -0
- package/dist/slices/rebuild/recipe-keywords.js +71 -0
- package/dist/slices/rebuild/recipe-legacy.js +5 -1
- package/dist/slices/rebuild/recipe-model.js +4 -0
- package/dist/slices/rebuild/recipe-narration-text.js +2 -1
- package/dist/slices/rebuild/recipe-preparation.js +3 -3
- package/dist/slices/rebuild/recipe-reference.js +8 -4
- package/dist/slices/rebuild/recipe-reviews.js +27 -7
- package/dist/slices/rebuild/recipe-save.js +6 -3
- package/dist/slices/rebuild/recipe-scenes.js +40 -11
- package/dist/slices/rebuild/recipe-shorts.js +8 -8
- package/dist/slices/rebuild/recipe-social.js +46 -0
- package/dist/slices/rebuild/recipe-text.js +64 -20
- package/dist/slices/rebuild/recipe-validation.js +98 -28
- package/dist/slices/rebuild/recipe-visual.js +55 -16
- package/dist/slices/rebuild/recipe-work.js +15 -3
- package/dist/slices/rebuild/recipe-youtube.js +4 -3
- package/dist/slices/rebuild/recovery-selection.js +3 -1
- package/dist/slices/rebuild/recovery.js +3 -2
- package/dist/slices/rebuild/repo.js +4 -3
- package/dist/slices/rebuild/review-promise.js +25 -0
- package/dist/slices/rebuild/review-redo.js +2 -1
- package/dist/slices/rebuild/runtime-admission.js +4 -3
- package/dist/slices/rebuild/runtime-agent-review.js +11 -5
- package/dist/slices/rebuild/runtime-animate.js +9 -8
- package/dist/slices/rebuild/runtime-article.js +3 -2
- package/dist/slices/rebuild/runtime-checkpoints.js +5 -4
- package/dist/slices/rebuild/runtime-document.js +8 -11
- package/dist/slices/rebuild/runtime-export-bed.js +6 -11
- package/dist/slices/rebuild/runtime-export-edit.js +6 -15
- package/dist/slices/rebuild/runtime-export-inputs.js +7 -7
- package/dist/slices/rebuild/runtime-export-short.js +8 -4
- package/dist/slices/rebuild/runtime-export.js +34 -12
- package/dist/slices/rebuild/runtime-figure-card.js +2 -1
- package/dist/slices/rebuild/runtime-image.js +4 -9
- package/dist/slices/rebuild/runtime-instructions.js +2 -0
- package/dist/slices/rebuild/runtime-keywords.js +59 -0
- package/dist/slices/rebuild/runtime-lines.js +2 -11
- package/dist/slices/rebuild/runtime-local.js +14 -20
- package/dist/slices/rebuild/runtime-narration-publication.js +2 -5
- package/dist/slices/rebuild/runtime-narration-reuse.js +9 -13
- package/dist/slices/rebuild/runtime-narration-text.js +10 -15
- package/dist/slices/rebuild/runtime-pictures.js +2 -4
- package/dist/slices/rebuild/runtime-piece-label.js +10 -6
- package/dist/slices/rebuild/runtime-plan.js +31 -5
- package/dist/slices/rebuild/runtime-provider.js +60 -19
- package/dist/slices/rebuild/runtime-publication.js +10 -9
- package/dist/slices/rebuild/runtime-review.js +33 -28
- package/dist/slices/rebuild/runtime-run.js +4 -1
- package/dist/slices/rebuild/runtime-shorts.js +25 -45
- package/dist/slices/rebuild/runtime-store.js +3 -8
- package/dist/slices/rebuild/runtime-subtitles.js +16 -14
- package/dist/slices/rebuild/runtime-voices.js +8 -15
- package/dist/slices/rebuild/runtime-youtube.js +11 -29
- package/dist/slices/rebuild/transition-repo.js +6 -5
- package/dist/slices/rebuild/work-records.js +6 -0
- package/dist/slices/reruns/cascade.js +1 -2
- package/dist/slices/reruns/index.js +6 -9
- package/dist/slices/reviews/external-model.js +3 -0
- package/dist/slices/reviews/external-repo.js +2 -1
- package/dist/slices/reviews/external-rows.js +2 -3
- package/dist/slices/reviews/model.js +26 -8
- package/dist/slices/reviews/repo.js +2 -1
- package/dist/slices/reviews/rules.js +1 -0
- package/dist/slices/reviews/view.js +5 -2
- package/dist/slices/revisions/downloads.js +4 -5
- package/dist/slices/revisions/mutation-assets.js +7 -4
- package/dist/slices/revisions/mutation-cues.js +2 -5
- package/dist/slices/revisions/mutations.js +0 -1
- package/dist/slices/revisions/repo.js +3 -1
- package/dist/slices/revisions/schema.js +1 -0
- package/dist/slices/revisions/view.js +20 -0
- package/dist/slices/run-cost/limits.js +5 -2
- package/dist/slices/run-cost/meter.js +12 -5
- package/dist/slices/run-cost/panel.js +8 -74
- package/dist/slices/run-cost/plan-readings.js +127 -0
- package/dist/slices/run-cost/plan-use.js +169 -0
- package/dist/slices/run-cost/pricing.js +3 -3
- package/dist/slices/run-cost/week.js +32 -4
- package/dist/slices/schedules/agenda.js +11 -2
- package/dist/slices/schedules/by-date.js +42 -0
- package/dist/slices/schedules/calendar.js +5 -0
- package/dist/slices/schedules/design-agenda.js +36 -0
- package/dist/slices/schedules/design-check.js +41 -0
- package/dist/slices/schedules/design-run.js +126 -0
- package/dist/slices/schedules/next-run.js +62 -0
- package/dist/slices/schedules/prepare.js +18 -2
- package/dist/slices/schedules/repo.js +24 -16
- package/dist/slices/schedules/rescore.js +35 -0
- package/dist/slices/schedules/scheduler.js +13 -3
- package/dist/slices/schedules/schema.js +54 -3
- package/dist/slices/schedules/score.js +107 -0
- package/dist/slices/schedules/service.js +57 -24
- package/dist/slices/schedules/topic-list.js +15 -0
- package/dist/slices/schedules/topic-scores.js +104 -0
- package/dist/slices/schedules/topics.js +48 -36
- package/dist/slices/settings/cli-paths.js +4 -6
- package/dist/slices/settings/first-run.js +2 -11
- package/dist/slices/settings/model-reach.js +3 -17
- package/dist/slices/settings/model.js +10 -0
- package/dist/slices/settings/playback.js +33 -22
- package/dist/slices/settings/probe-read.js +16 -0
- package/dist/slices/settings/repo.js +33 -0
- package/dist/slices/settings/tutorial.js +2 -2
- package/dist/slices/settings/voices.js +4 -12
- package/dist/slices/shorts/pick.js +2 -15
- package/dist/slices/shorts/prompts.js +2 -13
- package/dist/slices/shorts/render.js +3 -0
- package/dist/slices/social/channel.js +44 -0
- package/dist/slices/social/defaults.js +17 -0
- package/dist/slices/social/due.js +36 -0
- package/dist/slices/social/extension.js +18 -0
- package/dist/slices/social/fit.js +115 -0
- package/dist/slices/social/limits.js +186 -0
- package/dist/slices/social/links.js +21 -0
- package/dist/slices/social/media.js +131 -0
- package/dist/slices/social/model.js +75 -0
- package/dist/slices/social/notice.js +19 -0
- package/dist/slices/social/pack.js +176 -0
- package/dist/slices/social/post-prompt.js +55 -0
- package/dist/slices/social/queue.js +31 -0
- package/dist/slices/social/repo.js +133 -0
- package/dist/slices/social/reshape.js +97 -0
- package/dist/slices/social/rules.js +5 -0
- package/dist/slices/social/schema.js +70 -0
- package/dist/slices/social/serve.js +51 -0
- package/dist/slices/social/service.js +128 -0
- package/dist/slices/social/settings.js +21 -0
- package/dist/slices/social/sources.js +114 -0
- package/dist/slices/social/state.js +110 -0
- package/dist/slices/social/text.js +149 -0
- package/dist/slices/storage/arrange.js +3 -1
- package/dist/slices/storage/assets.js +8 -10
- package/dist/slices/storage/backup-designs.js +74 -0
- package/dist/slices/storage/backup-export.js +8 -3
- package/dist/slices/storage/backup-format.js +10 -0
- package/dist/slices/storage/backup-import.js +20 -2
- package/dist/slices/storage/delete-project.js +6 -5
- package/dist/slices/storage/files-location.js +6 -8
- package/dist/slices/storage/layout.js +13 -2
- package/dist/slices/storage/model.js +3 -0
- package/dist/slices/storage/portable.js +16 -11
- package/dist/slices/storage/schema.js +2 -0
- package/dist/slices/storage/staging-refs.js +2 -1
- package/dist/slices/storage/staging.js +6 -9
- package/dist/slices/storage/trim.js +10 -3
- package/dist/slices/studio/calendar.js +4 -1
- package/dist/slices/studio/pick.js +2 -11
- package/dist/slices/studio/plan.js +6 -15
- package/dist/slices/studio/queue.js +3 -14
- package/dist/slices/studio/releases.js +26 -0
- package/dist/slices/studio/settings.js +14 -51
- package/dist/slices/studio/videos.js +12 -0
- package/dist/slices/style-preview/render.js +34 -13
- package/dist/slices/style-preview/schema.js +13 -0
- package/dist/slices/style-preview/service.js +2 -1
- package/dist/slices/style-preview/settings.js +33 -5
- package/dist/slices/telemetry/collector-client.js +2 -4
- package/dist/slices/telemetry/flush.js +2 -4
- package/dist/slices/telemetry/model.js +0 -7
- package/dist/slices/telemetry/record.js +10 -3
- package/dist/slices/template-studio/anatomy-view.js +118 -0
- package/dist/slices/template-studio/anatomy.js +73 -0
- package/dist/slices/template-studio/bakeoff-make.js +225 -0
- package/dist/slices/template-studio/bakeoff-repo.js +83 -0
- package/dist/slices/template-studio/bakeoff.js +333 -0
- package/dist/slices/template-studio/budget.js +103 -0
- package/dist/slices/template-studio/checks.js +273 -0
- package/dist/slices/template-studio/document-sample.js +56 -0
- package/dist/slices/template-studio/edit.js +15 -0
- package/dist/slices/template-studio/for-all.js +161 -0
- package/dist/slices/template-studio/free-video.js +31 -0
- package/dist/slices/template-studio/free.js +176 -0
- package/dist/slices/template-studio/gate.js +40 -0
- package/dist/slices/template-studio/hidden.js +20 -0
- package/dist/slices/template-studio/jobs.js +79 -0
- package/dist/slices/template-studio/loudness.js +111 -0
- package/dist/slices/template-studio/make.js +40 -0
- package/dist/slices/template-studio/model.js +54 -0
- package/dist/slices/template-studio/pack-import.js +253 -0
- package/dist/slices/template-studio/pack-samples.js +60 -0
- package/dist/slices/template-studio/pack.js +205 -0
- package/dist/slices/template-studio/paid-view.js +66 -0
- package/dist/slices/template-studio/paid.js +348 -0
- package/dist/slices/template-studio/quote.js +148 -0
- package/dist/slices/template-studio/ready.js +104 -0
- package/dist/slices/template-studio/repo.js +134 -0
- package/dist/slices/template-studio/reviews-path.js +58 -0
- package/dist/slices/template-studio/runner.js +84 -0
- package/dist/slices/template-studio/save.js +236 -0
- package/dist/slices/template-studio/say-it-as.js +248 -0
- package/dist/slices/template-studio/schedule-gate.js +34 -0
- package/dist/slices/template-studio/schedule-price.js +30 -0
- package/dist/slices/template-studio/schedule-sample.js +87 -0
- package/dist/slices/template-studio/schema.js +218 -0
- package/dist/slices/template-studio/scope.js +124 -0
- package/dist/slices/template-studio/sections.js +176 -0
- package/dist/slices/template-studio/service.js +173 -0
- package/dist/slices/template-studio/status.js +174 -0
- package/dist/slices/template-studio/topic.js +223 -0
- package/dist/slices/trash/designs.js +76 -0
- package/dist/slices/trash/model.js +13 -4
- package/dist/slices/trash/service.js +52 -2
- package/dist/slices/tutorials/library.js +3 -2
- package/dist/slices/video/ass-text.js +17 -0
- package/dist/slices/video/audio-export.js +2 -64
- package/dist/slices/video/encoding.js +160 -0
- package/dist/slices/video/ffmpeg.js +23 -21
- package/dist/slices/video/figure-card.js +1 -15
- package/dist/slices/video/slideshow.js +43 -18
- package/dist/slices/voices/palette.js +0 -3
- package/dist/slices/youtube/answer.js +2 -15
- package/dist/slices/youtube/edits-repo.js +2 -11
- package/dist/tutorials/Agents-MCP.md +2 -1
- package/dist/tutorials/Assistant.md +42 -0
- package/dist/tutorials/Channels.md +2 -0
- package/dist/tutorials/Home-and-Projects.md +19 -0
- package/dist/tutorials/Install.md +1 -0
- package/dist/tutorials/Keywords-Slopify-Fills.md +87 -0
- package/dist/tutorials/Models.md +33 -1
- package/dist/tutorials/Notifications.md +6 -0
- package/dist/tutorials/Play-Images.md +4 -0
- package/dist/tutorials/Posting-to-Social-Sites.md +121 -0
- package/dist/tutorials/Prompts.md +10 -6
- package/dist/tutorials/Remote-Access.md +63 -0
- package/dist/tutorials/Reviews-and-Checkpoints.md +13 -2
- package/dist/tutorials/Schedules.md +21 -1
- package/dist/tutorials/Settings-Reference.md +50 -8
- package/dist/tutorials/Template-Studio.md +125 -0
- package/dist/tutorials/Templates.md +3 -0
- package/dist/tutorials/_Sidebar.md +5 -0
- package/dist/updater/work-in-progress.js +42 -12
- package/dist/web/assets/index-CkAI_2E7.css +1 -0
- package/dist/web/assets/index-DcD0cdfo.js +334 -0
- package/dist/web/assets/{pdf-T7MCwjIv.js → pdf-TODRwF31.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +5 -1
- package/dist/slices/article/provided-entries.js +0 -66
- package/dist/slices/article/run.js +0 -145
- package/dist/slices/images/run.js +0 -234
- package/dist/slices/narration/legacy-plan.js +0 -58
- package/dist/slices/narration/run.js +0 -270
- package/dist/slices/rebuild/runtime-actions.js +0 -90
- package/dist/slices/research/run.js +0 -224
- package/dist/slices/subtitles/prepare.js +0 -194
- package/dist/slices/subtitles/transcript.js +0 -34
- package/dist/slices/thumbnail/run.js +0 -219
- package/dist/slices/video/audio-inputs.js +0 -33
- package/dist/slices/video/reuse-audio.js +0 -62
- package/dist/slices/video/run.js +0 -78
- package/dist/slices/video/write-export.js +0 -158
- package/dist/slices/video/write-subtitles.js +0 -59
- package/dist/web/assets/index-BadsoORk.css +0 -1
- package/dist/web/assets/index-D8LqNsCl.js +0 -326
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
import { statSync } from "node:fs";
|
|
2
|
+
import { zValidator } from "@hono/zod-validator";
|
|
3
|
+
import { Hono } from "hono";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { designBudgetRoom } from "../../slices/designs/budget.js";
|
|
6
|
+
import { playDraftDocumentSchema } from "../../slices/play-drafts/schema.js";
|
|
7
|
+
import { templateById } from "../../slices/project-templates/repo.js";
|
|
8
|
+
import { readSettings } from "../../slices/settings/playback.js";
|
|
9
|
+
import { providerStatuses } from "../../slices/settings/readiness.js";
|
|
10
|
+
import { getAnatomy } from "../../slices/template-studio/anatomy-view.js";
|
|
11
|
+
import { bakeoffQuoteSchema, bakeoffStartSchema, bakeoffView, discardBakeoff, quoteBakeoff, startBakeoff, } from "../../slices/template-studio/bakeoff.js";
|
|
12
|
+
import { budgetRoom, readStudioSettings, studioSettingsSchema, systemTimeZone, writeStudioSettings, } from "../../slices/template-studio/budget.js";
|
|
13
|
+
import { makeSamplesForAll, quoteSamplesForAll, samplesForAllSchema, } from "../../slices/template-studio/for-all.js";
|
|
14
|
+
import { createFreeSamples } from "../../slices/template-studio/free.js";
|
|
15
|
+
import { enqueueJob } from "../../slices/template-studio/jobs.js";
|
|
16
|
+
import { templateSections, } from "../../slices/template-studio/model.js";
|
|
17
|
+
import { exportPack, packLimits } from "../../slices/template-studio/pack.js";
|
|
18
|
+
import { importPack } from "../../slices/template-studio/pack-import.js";
|
|
19
|
+
import { packSampleFileOf, packSamples } from "../../slices/template-studio/pack-samples.js";
|
|
20
|
+
import { quoteSample, quoteSave } from "../../slices/template-studio/quote.js";
|
|
21
|
+
import { markReady, markReadySchema } from "../../slices/template-studio/ready.js";
|
|
22
|
+
import { createUnknownAcks, makeSampleSchema, requestSample, saveStudio, saveStudioSchema, } from "../../slices/template-studio/save.js";
|
|
23
|
+
import { pronunciationsOf, quoteSayItAs, sayItAs, sayItAsSchema, } from "../../slices/template-studio/say-it-as.js";
|
|
24
|
+
import { withDependents } from "../../slices/template-studio/sections.js";
|
|
25
|
+
import { createPriceCache, createReadinessCache, getStudio, getStudioChecks, } from "../../slices/template-studio/service.js";
|
|
26
|
+
import { fullScriptSchema, setFullScript, setSampleTopic, topicOf, topicSchema, } from "../../slices/template-studio/topic.js";
|
|
27
|
+
import { fileResponse } from "./byte-range.js";
|
|
28
|
+
import { readMultipart } from "./multipart.js";
|
|
29
|
+
import { onInvalid, problem } from "./problem.js";
|
|
30
|
+
// Library → Templates → Open studio. Not `/api/studio`, which is the YouTube Studio pack.
|
|
31
|
+
const param = z.object({ id: z.uuid() });
|
|
32
|
+
export function refusedStudio(c, result) {
|
|
33
|
+
const status = result.reason === "not-found" ? 404 : result.reason === "invalid-input" ? 400 : 409;
|
|
34
|
+
return problem(c, {
|
|
35
|
+
status,
|
|
36
|
+
detail: result.message,
|
|
37
|
+
extensions: { reason: result.reason },
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
// What to price: a Save's document, a new sample topic, or the full script switched.
|
|
41
|
+
const quoteBody = z
|
|
42
|
+
.object({
|
|
43
|
+
document: playDraftDocumentSchema.optional(),
|
|
44
|
+
topic: z.string().trim().min(1).max(300).optional(),
|
|
45
|
+
fullScript: z.boolean().optional(),
|
|
46
|
+
})
|
|
47
|
+
.strict()
|
|
48
|
+
.refine((body) => [body.document, body.topic, body.fullScript].filter((one) => one !== undefined).length === 1, { message: "Send one of document, topic or fullScript." });
|
|
49
|
+
const unpriced = "This Slopify can't price or make paid samples because Play's review isn't available here. Restart Slopify; if it happens again, use Download diagnostics in Settings and report it.";
|
|
50
|
+
// A refusal that carries the new quote (a higher price, unknown prices, past the budget), so the
|
|
51
|
+
// page can show it on the button at once.
|
|
52
|
+
function refusedWithQuote(c, result) {
|
|
53
|
+
const status = result.reason === "not-found" ? 404 : result.reason === "invalid-input" ? 400 : 409;
|
|
54
|
+
return problem(c, {
|
|
55
|
+
status,
|
|
56
|
+
detail: result.message,
|
|
57
|
+
extensions: {
|
|
58
|
+
reason: result.reason,
|
|
59
|
+
...(result.quote === undefined ? {} : { quote: result.quote }),
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
const missingTemplate = "This template no longer exists; it may have been deleted. Go to Library → Templates to see your templates.";
|
|
64
|
+
function service(deps) {
|
|
65
|
+
return (deps.templateStudio ?? {
|
|
66
|
+
free: createFreeSamples({
|
|
67
|
+
loudnessDefault: () => readSettings({ db: deps.db, log: deps.log }).loudness,
|
|
68
|
+
}),
|
|
69
|
+
prices: createPriceCache(),
|
|
70
|
+
wake: () => undefined,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
export function studioDeps(deps, studio) {
|
|
74
|
+
return {
|
|
75
|
+
db: deps.db,
|
|
76
|
+
clock: deps.clock,
|
|
77
|
+
paths: deps.paths,
|
|
78
|
+
log: deps.log,
|
|
79
|
+
free: studio.free,
|
|
80
|
+
prices: studio.prices,
|
|
81
|
+
// Probing the CLIs takes seconds: the studio's checks share one lookup for half a minute.
|
|
82
|
+
providerStatuses: createReadinessCache(() => providerStatuses({
|
|
83
|
+
db: deps.db,
|
|
84
|
+
probe: deps.probe,
|
|
85
|
+
hostCliStatus: deps.hostCliStatus,
|
|
86
|
+
host: deps.speechHost,
|
|
87
|
+
})),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
export function templateStudioRoutes(deps) {
|
|
91
|
+
const studio = service(deps);
|
|
92
|
+
const read = studioDeps(deps, studio);
|
|
93
|
+
const review = studio.review ?? deps.drafts;
|
|
94
|
+
const acks = studio.acks ?? createUnknownAcks();
|
|
95
|
+
const sayDeps = { db: deps.db, paths: deps.paths, ids: deps.ids, clock: deps.clock };
|
|
96
|
+
const gone = (c, detail) => problem(c, { status: 404, detail });
|
|
97
|
+
const settingsView = () => {
|
|
98
|
+
const now = deps.clock.now();
|
|
99
|
+
return {
|
|
100
|
+
...readStudioSettings(deps.db),
|
|
101
|
+
today: budgetRoom(deps.db, now),
|
|
102
|
+
designToday: designBudgetRoom(deps.db, now, systemTimeZone()),
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
const media = (c, file, type, detail) => file === undefined
|
|
106
|
+
? gone(c, detail)
|
|
107
|
+
: fileResponse(c, file.path, file.bytes, {
|
|
108
|
+
"content-type": type,
|
|
109
|
+
"cache-control": "no-cache",
|
|
110
|
+
"x-content-type-options": "nosniff",
|
|
111
|
+
});
|
|
112
|
+
return (new Hono()
|
|
113
|
+
// Settings → Template studio: the samples' daily budgets, and what was spent today.
|
|
114
|
+
.get("/settings", (c) => c.json(settingsView()))
|
|
115
|
+
.put("/settings", zValidator("json", studioSettingsSchema, onInvalid), (c) => {
|
|
116
|
+
writeStudioSettings(deps.db, c.req.valid("json"));
|
|
117
|
+
return c.json(settingsView());
|
|
118
|
+
})
|
|
119
|
+
// Settings → Template studio → Make samples for all: the total first, then each sample
|
|
120
|
+
// that fits today's budget queued at its price, the rest waiting on the budget.
|
|
121
|
+
.post("/samples-for-all/quote", async (c) => {
|
|
122
|
+
if (review === undefined)
|
|
123
|
+
return problem(c, { status: 503, detail: unpriced });
|
|
124
|
+
return c.json(await quoteSamplesForAll({ db: deps.db, clock: deps.clock, review, acks }));
|
|
125
|
+
})
|
|
126
|
+
.post("/samples-for-all", zValidator("json", samplesForAllSchema, onInvalid), async (c) => {
|
|
127
|
+
if (review === undefined)
|
|
128
|
+
return problem(c, { status: 503, detail: unpriced });
|
|
129
|
+
const result = await makeSamplesForAll({ db: deps.db, clock: deps.clock, review, acks }, c.req.valid("json"));
|
|
130
|
+
if (!result.ok)
|
|
131
|
+
return refusedWithQuote(c, result);
|
|
132
|
+
studio.wake();
|
|
133
|
+
for (const templateId of [...result.value.queued, ...result.value.held])
|
|
134
|
+
deps.hub.emitGlobal({ type: "template.sample", templateId });
|
|
135
|
+
return c.json(result.value, 202);
|
|
136
|
+
})
|
|
137
|
+
// Import pack: a pack someone exported becomes a new, unchecked template. Read whole
|
|
138
|
+
// (a pack is at most 30 MB), checked, then written in one go.
|
|
139
|
+
.post("/packs/import", async (c) => {
|
|
140
|
+
if (review === undefined)
|
|
141
|
+
return problem(c, { status: 503, detail: unpriced });
|
|
142
|
+
let tooLarge = false;
|
|
143
|
+
const bytes = await readMultipart(c.req.raw, async (content) => {
|
|
144
|
+
const chunks = [];
|
|
145
|
+
let size = 0;
|
|
146
|
+
for await (const chunk of content) {
|
|
147
|
+
size += chunk.byteLength;
|
|
148
|
+
if (size > packLimits.uploadBytes) {
|
|
149
|
+
tooLarge = true;
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
chunks.push(chunk);
|
|
153
|
+
}
|
|
154
|
+
return Buffer.concat(chunks, Math.min(size, packLimits.uploadBytes));
|
|
155
|
+
});
|
|
156
|
+
if (tooLarge)
|
|
157
|
+
return problem(c, {
|
|
158
|
+
status: 413,
|
|
159
|
+
title: "Content Too Large",
|
|
160
|
+
detail: `This pack is larger than ${String(packLimits.uploadBytes / 1024 / 1024)} MB, more than a template pack ever is. Export it again from the template's studio, or ask whoever made it to.`,
|
|
161
|
+
});
|
|
162
|
+
if (bytes === undefined || bytes.byteLength === 0)
|
|
163
|
+
return problem(c, {
|
|
164
|
+
status: 400,
|
|
165
|
+
detail: "No pack arrived with the upload. Choose the pack's .zip file again.",
|
|
166
|
+
});
|
|
167
|
+
const result = importPack(review, new Uint8Array(bytes));
|
|
168
|
+
if (!result.ok)
|
|
169
|
+
return refusedStudio(c, result);
|
|
170
|
+
deps.hub.emitGlobal({ type: "template.sample", templateId: result.value.templateId });
|
|
171
|
+
return c.json(result.value, 201);
|
|
172
|
+
})
|
|
173
|
+
.get("/:id", zValidator("param", param, onInvalid), async (c) => {
|
|
174
|
+
const result = await getStudio(read, c.req.valid("param").id);
|
|
175
|
+
return result.ok ? c.json(result.value) : refusedStudio(c, result);
|
|
176
|
+
})
|
|
177
|
+
// A section's prompts, each cut into your words, your keywords, Slopify's and settings'.
|
|
178
|
+
.get("/:id/anatomy", zValidator("param", param, onInvalid), zValidator("query", z.object({ section: z.enum(templateSections), item: z.string().max(200).optional() }), onInvalid), async (c) => {
|
|
179
|
+
if (review === undefined)
|
|
180
|
+
return problem(c, { status: 503, detail: unpriced });
|
|
181
|
+
const { section, item } = c.req.valid("query");
|
|
182
|
+
const result = await getAnatomy({ db: deps.db, paths: deps.paths, log: deps.log, review }, c.req.valid("param").id, section, item);
|
|
183
|
+
return result.ok ? c.json(result.value) : refusedStudio(c, result);
|
|
184
|
+
})
|
|
185
|
+
// Mark ready: the newest version, once every check passes and every sample is current,
|
|
186
|
+
// becomes the one its schedules run. Refused with each thing still to fix.
|
|
187
|
+
.post("/:id/ready", zValidator("param", param, onInvalid), zValidator("json", markReadySchema, onInvalid), async (c) => {
|
|
188
|
+
const id = c.req.valid("param").id;
|
|
189
|
+
const result = await markReady(read, id, c.req.valid("json"));
|
|
190
|
+
if (!result.ok) {
|
|
191
|
+
const status = result.reason === "not-found" ? 404 : result.reason === "invalid-input" ? 400 : 409;
|
|
192
|
+
return problem(c, {
|
|
193
|
+
status,
|
|
194
|
+
detail: result.message,
|
|
195
|
+
extensions: { reason: result.reason, problems: result.problems },
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
if (!result.value.replayed)
|
|
199
|
+
deps.hub.emitGlobal({ type: "template.sample", templateId: id });
|
|
200
|
+
return c.json(result.value);
|
|
201
|
+
})
|
|
202
|
+
// Voice → Say it as: the names the sample's script says, each with its first line.
|
|
203
|
+
.get("/:id/pronunciations", zValidator("param", param, onInvalid), (c) => {
|
|
204
|
+
const id = c.req.valid("param").id;
|
|
205
|
+
if (templateById(deps.db, id) === undefined)
|
|
206
|
+
return gone(c, missingTemplate);
|
|
207
|
+
return c.json(pronunciationsOf(sayDeps, id));
|
|
208
|
+
})
|
|
209
|
+
// What voicing the sample's lines that say a word again, the new way, costs.
|
|
210
|
+
.post("/:id/say-it-as/quote", zValidator("param", param, onInvalid), zValidator("json", sayItAsSchema.unwrap().pick({ written: true, spoken: true }), onInvalid), (c) => {
|
|
211
|
+
if (review === undefined)
|
|
212
|
+
return problem(c, { status: 503, detail: unpriced });
|
|
213
|
+
const result = quoteSayItAs({ ...sayDeps, review }, c.req.valid("param").id, c.req.valid("json"));
|
|
214
|
+
return result.ok ? c.json(result.value) : refusedStudio(c, result);
|
|
215
|
+
})
|
|
216
|
+
// Save a "Say it as": the Library alias, and the sample's lines that say it voiced again.
|
|
217
|
+
.post("/:id/say-it-as", zValidator("param", param, onInvalid), zValidator("json", sayItAsSchema, onInvalid), async (c) => {
|
|
218
|
+
if (review === undefined)
|
|
219
|
+
return problem(c, { status: 503, detail: unpriced });
|
|
220
|
+
const id = c.req.valid("param").id;
|
|
221
|
+
const result = await sayItAs({ ...sayDeps, review, acks }, id, c.req.valid("json"));
|
|
222
|
+
if (!result.ok)
|
|
223
|
+
return refusedWithQuote(c, result);
|
|
224
|
+
if (result.value.queued)
|
|
225
|
+
studio.wake();
|
|
226
|
+
deps.hub.emitGlobal({ type: "template.sample", templateId: id });
|
|
227
|
+
return c.json(result.value);
|
|
228
|
+
})
|
|
229
|
+
// The free checks, apart from the studio: they wait on the providers' readiness.
|
|
230
|
+
.get("/:id/checks", zValidator("param", param, onInvalid), async (c) => {
|
|
231
|
+
const result = await getStudioChecks(read, c.req.valid("param").id);
|
|
232
|
+
return result.ok ? c.json(result.value) : refusedStudio(c, result);
|
|
233
|
+
})
|
|
234
|
+
// What a Save's sample would cost, before anything is spent. Prices only; nothing changes.
|
|
235
|
+
.post("/:id/quote", zValidator("param", param, onInvalid), zValidator("json", quoteBody, onInvalid), async (c) => {
|
|
236
|
+
if (review === undefined)
|
|
237
|
+
return problem(c, { status: 503, detail: unpriced });
|
|
238
|
+
const id = c.req.valid("param").id;
|
|
239
|
+
const body = c.req.valid("json");
|
|
240
|
+
const quoteDeps = { db: deps.db, clock: deps.clock, review };
|
|
241
|
+
let result;
|
|
242
|
+
if (body.document !== undefined)
|
|
243
|
+
result = await quoteSave(quoteDeps, id, body.document);
|
|
244
|
+
else if (body.topic !== undefined) {
|
|
245
|
+
const named = topicOf(deps, id, body.topic);
|
|
246
|
+
if (!named.ok)
|
|
247
|
+
return problem(c, {
|
|
248
|
+
status: 400,
|
|
249
|
+
detail: named.message,
|
|
250
|
+
extensions: { reason: "invalid-input" },
|
|
251
|
+
});
|
|
252
|
+
result = await quoteSample(quoteDeps, id, { topic: named.topic, sections: "all" });
|
|
253
|
+
}
|
|
254
|
+
else
|
|
255
|
+
result = await quoteSample(quoteDeps, id, {
|
|
256
|
+
fullScript: body.fullScript,
|
|
257
|
+
sections: withDependents(["script"]),
|
|
258
|
+
});
|
|
259
|
+
return result.ok ? c.json(result.value) : refusedStudio(c, result);
|
|
260
|
+
})
|
|
261
|
+
// Save: the template's next version, with its sample queued at the price the person saw,
|
|
262
|
+
// or without a sample. A higher price, an unaccepted unknown one, or a sample past today's
|
|
263
|
+
// budget is refused with the new quote.
|
|
264
|
+
.post("/:id/save", zValidator("param", param, onInvalid), zValidator("json", saveStudioSchema, onInvalid), async (c) => {
|
|
265
|
+
if (review === undefined)
|
|
266
|
+
return problem(c, { status: 503, detail: unpriced });
|
|
267
|
+
const id = c.req.valid("param").id;
|
|
268
|
+
const result = await saveStudio({ db: deps.db, clock: deps.clock, review, acks }, id, c.req.valid("json"));
|
|
269
|
+
if (!result.ok)
|
|
270
|
+
return refusedWithQuote(c, result);
|
|
271
|
+
if (!result.value.replayed) {
|
|
272
|
+
studio.wake();
|
|
273
|
+
deps.hub.emitGlobal({ type: "template.sample", templateId: id });
|
|
274
|
+
}
|
|
275
|
+
return c.json(result.value);
|
|
276
|
+
})
|
|
277
|
+
// The sample's topic: typed, the next one a schedule has queued, or back to the fixed one.
|
|
278
|
+
.put("/:id/topic", zValidator("param", param, onInvalid), zValidator("json", topicSchema, onInvalid), async (c) => {
|
|
279
|
+
if (review === undefined)
|
|
280
|
+
return problem(c, { status: 503, detail: unpriced });
|
|
281
|
+
const id = c.req.valid("param").id;
|
|
282
|
+
const result = await setSampleTopic({ db: deps.db, clock: deps.clock, review, acks }, id, c.req.valid("json"));
|
|
283
|
+
if (!result.ok)
|
|
284
|
+
return refusedWithQuote(c, result);
|
|
285
|
+
studio.wake();
|
|
286
|
+
deps.hub.emitGlobal({ type: "template.sample", templateId: id });
|
|
287
|
+
return c.json(result.value);
|
|
288
|
+
})
|
|
289
|
+
// The sample's whole script, or back to the outline and first chapter.
|
|
290
|
+
.post("/:id/full-script", zValidator("param", param, onInvalid), zValidator("json", fullScriptSchema, onInvalid), async (c) => {
|
|
291
|
+
if (review === undefined)
|
|
292
|
+
return problem(c, { status: 503, detail: unpriced });
|
|
293
|
+
const id = c.req.valid("param").id;
|
|
294
|
+
const result = await setFullScript({ db: deps.db, clock: deps.clock, review, acks }, id, c.req.valid("json"));
|
|
295
|
+
if (!result.ok)
|
|
296
|
+
return refusedWithQuote(c, result);
|
|
297
|
+
studio.wake();
|
|
298
|
+
deps.hub.emitGlobal({ type: "template.sample", templateId: id });
|
|
299
|
+
return c.json(result.value);
|
|
300
|
+
})
|
|
301
|
+
// Make sample: the current version's paid sample again, at the price the button showed.
|
|
302
|
+
.post("/:id/samples/make", zValidator("param", param, onInvalid), zValidator("json", makeSampleSchema, onInvalid), (c) => {
|
|
303
|
+
const id = c.req.valid("param").id;
|
|
304
|
+
const result = requestSample({ db: deps.db, clock: deps.clock, acks }, id, c.req.valid("json"));
|
|
305
|
+
if (!result.ok)
|
|
306
|
+
return refusedStudio(c, result);
|
|
307
|
+
studio.wake();
|
|
308
|
+
deps.hub.emitGlobal({ type: "template.sample", templateId: id });
|
|
309
|
+
return c.json({ queued: true, ...result.value }, 202);
|
|
310
|
+
})
|
|
311
|
+
// Make samples: the free ones (video, loudness, PDF, the schedule's cost) for the current
|
|
312
|
+
// version, queued behind any other template's and made in the background.
|
|
313
|
+
.post("/:id/free-samples", zValidator("param", param, onInvalid), (c) => {
|
|
314
|
+
const template = templateById(deps.db, c.req.valid("param").id);
|
|
315
|
+
if (template === undefined)
|
|
316
|
+
return gone(c, missingTemplate);
|
|
317
|
+
enqueueJob(deps.db, {
|
|
318
|
+
templateId: template.id,
|
|
319
|
+
templateVersion: template.version,
|
|
320
|
+
ackHigh: 0,
|
|
321
|
+
kind: "free",
|
|
322
|
+
requestedAt: deps.clock.now().toISOString(),
|
|
323
|
+
});
|
|
324
|
+
studio.wake();
|
|
325
|
+
deps.hub.emitGlobal({ type: "template.sample", templateId: template.id });
|
|
326
|
+
return c.json({ queued: true, version: template.version }, 202);
|
|
327
|
+
})
|
|
328
|
+
.get("/:id/loudness/:file", zValidator("param", param.extend({ file: z.enum(["before.mp3", "after.mp3"]) }), onInvalid), (c) => {
|
|
329
|
+
const { id, file } = c.req.valid("param");
|
|
330
|
+
const template = templateById(deps.db, id);
|
|
331
|
+
if (template === undefined)
|
|
332
|
+
return gone(c, missingTemplate);
|
|
333
|
+
const notMade = "This loudness sample isn't made yet. Open the template's studio and press Make samples.";
|
|
334
|
+
if (file === "before.mp3") {
|
|
335
|
+
const source = studio.loudnessSource;
|
|
336
|
+
const size = source === undefined ? undefined : statSync(source, { throwIfNoEntry: false });
|
|
337
|
+
return media(c, source === undefined || size === undefined
|
|
338
|
+
? undefined
|
|
339
|
+
: { path: source, bytes: size.size }, "audio/mpeg", notMade);
|
|
340
|
+
}
|
|
341
|
+
const hash = studio.free.loudnessHashOf(template.document);
|
|
342
|
+
return media(c, hash === undefined ? undefined : studio.loudnessFile?.(hash), "audio/mpeg", hash === undefined
|
|
343
|
+
? "Level the volume is off for this template, so there is no levelled narration. Turn it on in Voice, then press Save."
|
|
344
|
+
: notMade);
|
|
345
|
+
})
|
|
346
|
+
// Compare models: one section's sample on one or two other models, priced first.
|
|
347
|
+
.post("/:id/bakeoff/quote", zValidator("param", param, onInvalid), zValidator("json", bakeoffQuoteSchema, onInvalid), async (c) => {
|
|
348
|
+
if (review === undefined)
|
|
349
|
+
return problem(c, { status: 503, detail: unpriced });
|
|
350
|
+
const result = await quoteBakeoff({ db: deps.db, clock: deps.clock, review, paths: deps.paths, log: deps.log }, c.req.valid("param").id, c.req.valid("json"));
|
|
351
|
+
return result.ok ? c.json(result.value) : refusedStudio(c, result);
|
|
352
|
+
})
|
|
353
|
+
.post("/:id/bakeoff", zValidator("param", param, onInvalid), zValidator("json", bakeoffStartSchema, onInvalid), async (c) => {
|
|
354
|
+
if (review === undefined)
|
|
355
|
+
return problem(c, { status: 503, detail: unpriced });
|
|
356
|
+
const id = c.req.valid("param").id;
|
|
357
|
+
const result = await startBakeoff({
|
|
358
|
+
db: deps.db,
|
|
359
|
+
clock: deps.clock,
|
|
360
|
+
review,
|
|
361
|
+
paths: deps.paths,
|
|
362
|
+
log: deps.log,
|
|
363
|
+
uuid: review.uuid,
|
|
364
|
+
}, id, c.req.valid("json"));
|
|
365
|
+
if (!result.ok)
|
|
366
|
+
return refusedWithQuote(c, result);
|
|
367
|
+
studio.wake();
|
|
368
|
+
deps.hub.emitGlobal({ type: "template.sample", templateId: id });
|
|
369
|
+
return c.json(result.value, 202);
|
|
370
|
+
})
|
|
371
|
+
.get("/:id/bakeoff", zValidator("param", param, onInvalid), (c) => {
|
|
372
|
+
const id = c.req.valid("param").id;
|
|
373
|
+
if (templateById(deps.db, id) === undefined)
|
|
374
|
+
return gone(c, missingTemplate);
|
|
375
|
+
return c.json({ bakeoff: bakeoffView(deps, id) });
|
|
376
|
+
})
|
|
377
|
+
// Discard: the copies and what they made go; today's spend on them still counts.
|
|
378
|
+
.delete("/:id/bakeoff", zValidator("param", param, onInvalid), (c) => {
|
|
379
|
+
const id = c.req.valid("param").id;
|
|
380
|
+
if (templateById(deps.db, id) === undefined)
|
|
381
|
+
return gone(c, missingTemplate);
|
|
382
|
+
const result = discardBakeoff(deps, id);
|
|
383
|
+
if (!result.ok)
|
|
384
|
+
return refusedStudio(c, result);
|
|
385
|
+
deps.hub.emitGlobal({ type: "template.sample", templateId: id });
|
|
386
|
+
return c.json(result.value);
|
|
387
|
+
})
|
|
388
|
+
// Export pack: the version marked ready as a zip to share. A file download, like the
|
|
389
|
+
// project's files: agents read templates with get_template_studio instead.
|
|
390
|
+
.get("/:id/pack", zValidator("param", param, onInvalid), async (c) => {
|
|
391
|
+
const result = await exportPack({
|
|
392
|
+
db: deps.db,
|
|
393
|
+
paths: deps.paths,
|
|
394
|
+
log: deps.log,
|
|
395
|
+
clock: deps.clock,
|
|
396
|
+
version: deps.version,
|
|
397
|
+
}, c.req.valid("param").id);
|
|
398
|
+
if (!result.ok)
|
|
399
|
+
return refusedStudio(c, result);
|
|
400
|
+
return c.body(new Uint8Array(result.value.bytes), 200, {
|
|
401
|
+
"content-type": "application/zip",
|
|
402
|
+
"content-length": String(result.value.bytes.byteLength),
|
|
403
|
+
"content-disposition": `attachment; filename="${result.value.filename}"`,
|
|
404
|
+
"x-content-type-options": "nosniff",
|
|
405
|
+
});
|
|
406
|
+
})
|
|
407
|
+
// The samples an imported pack came with, shown in the studio until it makes its own.
|
|
408
|
+
.get("/:id/pack-samples", zValidator("param", param, onInvalid), (c) => {
|
|
409
|
+
const id = c.req.valid("param").id;
|
|
410
|
+
if (templateById(deps.db, id) === undefined)
|
|
411
|
+
return gone(c, missingTemplate);
|
|
412
|
+
return c.json({
|
|
413
|
+
samples: packSamples(deps.paths, id).map((sample) => ({
|
|
414
|
+
role: sample.role,
|
|
415
|
+
name: sample.name,
|
|
416
|
+
url: `/api/template-studio/${id}/pack-samples/${sample.file}`,
|
|
417
|
+
})),
|
|
418
|
+
});
|
|
419
|
+
})
|
|
420
|
+
.get("/:id/pack-samples/:file", zValidator("param", param.extend({
|
|
421
|
+
file: z.string().regex(/^[a-z0-9-]{1,40}\.(png|jpg|jpeg|webp|mp3|m4a|wav)$/),
|
|
422
|
+
}), onInvalid), (c) => {
|
|
423
|
+
const { id, file } = c.req.valid("param");
|
|
424
|
+
const found = packSampleFileOf(deps.paths, id, file);
|
|
425
|
+
return media(c, found, found?.type ?? "application/octet-stream", "This template has no such sample from a pack. Open its studio to see the samples it has.");
|
|
426
|
+
})
|
|
427
|
+
.get("/:id/document.pdf", zValidator("param", param, onInvalid), (c) => {
|
|
428
|
+
const template = templateById(deps.db, c.req.valid("param").id);
|
|
429
|
+
if (template === undefined)
|
|
430
|
+
return gone(c, missingTemplate);
|
|
431
|
+
return media(c, studio.documentFile?.(studio.free.documentHashOf(template.document)), "application/pdf", "This PDF sample isn't made yet. Open the template's studio and press Make samples.");
|
|
432
|
+
}));
|
|
433
|
+
}
|
package/dist/edge/http/trash.js
CHANGED
|
@@ -4,21 +4,21 @@ import { z } from "zod";
|
|
|
4
4
|
import { withProjectControl } from "../../slices/control/lock.js";
|
|
5
5
|
import { trashKinds, } from "../../slices/trash/model.js";
|
|
6
6
|
import { deleteNow, listTrash, restoreItem } from "../../slices/trash/service.js";
|
|
7
|
-
import {
|
|
7
|
+
import { idString } from "./ids.js";
|
|
8
|
+
import { onInvalid } from "./problem.js";
|
|
9
|
+
import { refusal } from "./refusal.js";
|
|
8
10
|
const itemParam = z.object({
|
|
9
11
|
kind: z.enum(trashKinds),
|
|
10
|
-
id:
|
|
11
|
-
.string()
|
|
12
|
-
.min(1)
|
|
13
|
-
.max(64)
|
|
14
|
-
.regex(/^[0-9A-Za-z_-]+$/),
|
|
12
|
+
id: idString,
|
|
15
13
|
});
|
|
16
14
|
// Settings → Trash's Restore selected / Restore all and Delete selected / Empty trash: the
|
|
17
15
|
// items named, each done on its own so one refusal leaves the rest done.
|
|
18
|
-
const manySchema = z.object({ items: z.array(itemParam).min(1).max(5000) });
|
|
16
|
+
export const manySchema = z.object({ items: z.array(itemParam).min(1).max(5000) });
|
|
19
17
|
// A template comes back before the schedules that run it, so restoring both at once works.
|
|
20
18
|
const restoreOrder = {
|
|
21
19
|
template: 0,
|
|
20
|
+
"design-template": 0,
|
|
21
|
+
design: 1,
|
|
22
22
|
prompt: 1,
|
|
23
23
|
entry: 1,
|
|
24
24
|
project: 1,
|
|
@@ -47,11 +47,10 @@ function failedItem(kind, id, reason, detail) {
|
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
49
|
function refused(c, reason, detail) {
|
|
50
|
-
return
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
return refusal(c, reason, {
|
|
51
|
+
statuses: status,
|
|
52
|
+
otherwise: 500,
|
|
53
53
|
detail: detail === undefined ? details[reason] : `${details[reason]} (${detail})`,
|
|
54
|
-
extensions: { reason },
|
|
55
54
|
});
|
|
56
55
|
}
|
|
57
56
|
// Settings → Trash: the list, Restore and Delete now, one item or many. The daily purge runs from main.ts.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { zValidator } from "@hono/zod-validator";
|
|
2
2
|
import { Hono } from "hono";
|
|
3
3
|
import { readTutorial, resetTutorial, saveTutorial, tutorialWriteSchema, } from "../../slices/settings/tutorial.js";
|
|
4
|
-
import { onInvalid, problem
|
|
4
|
+
import { onInvalid, problem } from "./problem.js";
|
|
5
5
|
// Preserve Hono's inferred route types for the web client.
|
|
6
6
|
export function tutorialRoutes(deps) {
|
|
7
7
|
return new Hono()
|
|
@@ -11,7 +11,6 @@ export function tutorialRoutes(deps) {
|
|
|
11
11
|
if (!result.ok)
|
|
12
12
|
return problem(c, {
|
|
13
13
|
status: 409,
|
|
14
|
-
title: titleOf(409),
|
|
15
14
|
detail: result.reason === "unreadable"
|
|
16
15
|
? "Your saved tutorial progress could not be read. Choose Restart tutorial to start it again."
|
|
17
16
|
: "Your tutorial progress changed in another tab or window. Reload the page, then try again.",
|
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
2
|
import { tutorialPageIdPattern } from "../../slices/tutorials/anchors.js";
|
|
3
3
|
import { bundledTutorialsDir, loadTutorials, searchTutorials, TutorialsMissing, } from "../../slices/tutorials/library.js";
|
|
4
|
-
import { problem
|
|
5
|
-
|
|
6
|
-
function missing(c, error) {
|
|
7
|
-
if (!(error instanceof TutorialsMissing))
|
|
8
|
-
throw error;
|
|
9
|
-
return problem(c, { status: 500, title: titleOf(500), detail: error.message });
|
|
10
|
-
}
|
|
4
|
+
import { problem } from "./problem.js";
|
|
5
|
+
import { bundleMissing } from "./refusal.js";
|
|
11
6
|
// Help → Tutorials: the sidebar and page list, a search across every page, and one page's
|
|
12
7
|
// Markdown. The pages ship with the app, so they are read once and kept.
|
|
13
8
|
// Preserve Hono's inferred route types for the web client.
|
|
@@ -27,7 +22,7 @@ export function tutorialPagesRoutes(deps) {
|
|
|
27
22
|
return c.json((await read()).index);
|
|
28
23
|
}
|
|
29
24
|
catch (error) {
|
|
30
|
-
return
|
|
25
|
+
return bundleMissing(c, error, TutorialsMissing);
|
|
31
26
|
}
|
|
32
27
|
})
|
|
33
28
|
.get("/search", async (c) => {
|
|
@@ -36,7 +31,7 @@ export function tutorialPagesRoutes(deps) {
|
|
|
36
31
|
return c.json({ hits: searchTutorials(await read(), query) });
|
|
37
32
|
}
|
|
38
33
|
catch (error) {
|
|
39
|
-
return
|
|
34
|
+
return bundleMissing(c, error, TutorialsMissing);
|
|
40
35
|
}
|
|
41
36
|
})
|
|
42
37
|
.get("/:page", async (c) => {
|
|
@@ -46,12 +41,11 @@ export function tutorialPagesRoutes(deps) {
|
|
|
46
41
|
markdown = tutorialPageIdPattern.test(id) ? (await read()).pages.get(id) : undefined;
|
|
47
42
|
}
|
|
48
43
|
catch (error) {
|
|
49
|
-
return
|
|
44
|
+
return bundleMissing(c, error, TutorialsMissing);
|
|
50
45
|
}
|
|
51
46
|
if (markdown === undefined)
|
|
52
47
|
return problem(c, {
|
|
53
48
|
status: 404,
|
|
54
|
-
title: titleOf(404),
|
|
55
49
|
detail: `There is no tutorial called ${id} in this version of Slopify. Pick one from the list on Help → Tutorials, or search there.`,
|
|
56
50
|
});
|
|
57
51
|
return c.body(markdown, 200, { "content-type": "text/markdown; charset=utf-8" });
|
package/dist/edge/http/update.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
|
-
import { problem
|
|
2
|
+
import { problem } from "./problem.js";
|
|
3
|
+
import { fromSlopifyPage } from "./same-page.js";
|
|
3
4
|
export function updateRoutes(deps) {
|
|
4
5
|
const unavailable = {
|
|
5
6
|
currentVersion: deps.version,
|
|
@@ -14,16 +15,16 @@ export function updateRoutes(deps) {
|
|
|
14
15
|
.get("/ready", (c) => {
|
|
15
16
|
c.header("Cache-Control", "no-store");
|
|
16
17
|
if (!deps.updater?.ready(c.req.header("X-Slopify-Update-Token") ?? ""))
|
|
17
|
-
return problem(c, { status: 403
|
|
18
|
+
return problem(c, { status: 403 });
|
|
18
19
|
return c.json({ status: "ok", version: deps.version });
|
|
19
20
|
})
|
|
20
21
|
.post("/activate", async (c) => {
|
|
21
22
|
c.header("Cache-Control", "no-store");
|
|
22
23
|
const token = c.req.header("X-Slopify-Update-Token") ?? "";
|
|
23
24
|
if (!deps.updater?.ready(token))
|
|
24
|
-
return problem(c, { status: 403
|
|
25
|
+
return problem(c, { status: 403 });
|
|
25
26
|
if (!(await deps.updater.activate(token)))
|
|
26
|
-
return problem(c, { status: 409
|
|
27
|
+
return problem(c, { status: 409 });
|
|
27
28
|
return c.json({ status: "ok", version: deps.version });
|
|
28
29
|
})
|
|
29
30
|
.get("/", async (c) => {
|
|
@@ -34,33 +35,27 @@ export function updateRoutes(deps) {
|
|
|
34
35
|
})
|
|
35
36
|
// Drops an update that is waiting for running work to finish.
|
|
36
37
|
.delete("/", async (c) => {
|
|
37
|
-
|
|
38
|
-
if (origin !== undefined && origin !== new URL(c.req.url).origin)
|
|
38
|
+
if (!fromSlopifyPage(c, deps.remoteAccess?.remoteHost))
|
|
39
39
|
return problem(c, {
|
|
40
40
|
status: 403,
|
|
41
|
-
title: titleOf(403),
|
|
42
41
|
detail: "For your safety, updates can only be changed from the Slopify page itself. Open Slopify and try again there.",
|
|
43
42
|
});
|
|
44
43
|
if (deps.updater?.cancelWaiting() !== true)
|
|
45
44
|
return problem(c, {
|
|
46
45
|
status: 409,
|
|
47
|
-
title: titleOf(409),
|
|
48
46
|
detail: "No update is waiting to install. Reload the page to see the current update status.",
|
|
49
47
|
});
|
|
50
48
|
return c.json(await deps.updater.check());
|
|
51
49
|
})
|
|
52
50
|
.post("/", async (c) => {
|
|
53
|
-
|
|
54
|
-
if (origin !== undefined && origin !== new URL(c.req.url).origin)
|
|
51
|
+
if (!fromSlopifyPage(c, deps.remoteAccess?.remoteHost))
|
|
55
52
|
return problem(c, {
|
|
56
53
|
status: 403,
|
|
57
|
-
title: titleOf(403),
|
|
58
54
|
detail: "For your safety, updates can only be started from the Slopify page itself. Open Slopify and try again there.",
|
|
59
55
|
});
|
|
60
56
|
if (deps.updater === undefined)
|
|
61
57
|
return problem(c, {
|
|
62
58
|
status: 409,
|
|
63
|
-
title: titleOf(409),
|
|
64
59
|
detail: unavailable.blockedReason,
|
|
65
60
|
});
|
|
66
61
|
const result = await deps.updater.start();
|
|
@@ -69,7 +64,6 @@ export function updateRoutes(deps) {
|
|
|
69
64
|
const status = result.code ?? 409;
|
|
70
65
|
return problem(c, {
|
|
71
66
|
status,
|
|
72
|
-
title: titleOf(status),
|
|
73
67
|
detail: result.info.blockedReason ??
|
|
74
68
|
result.info.error ??
|
|
75
69
|
"You already have the latest version of Slopify.",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { errorText } from "../../kernel/errors.js";
|
|
2
|
+
import { problem } from "./problem.js";
|
|
2
3
|
// The audio player's waveform: `?waveform=N` on a file's own URL answers the file's loudness
|
|
3
4
|
// as N bars from 0 to 1, instead of the file. The peaks come from the same ffmpeg decode as the
|
|
4
5
|
// live view's waveform (`slices/narration/peaks.ts`), ten a second, folded to N.
|
|
@@ -34,19 +35,16 @@ export function waveformAnswer(decode) {
|
|
|
34
35
|
if (!Number.isInteger(bars) || bars < 1 || bars > waveformBarsMax)
|
|
35
36
|
return problem(c, {
|
|
36
37
|
status: 400,
|
|
37
|
-
title: titleOf(400),
|
|
38
38
|
detail: `Ask for a waveform of 1 to ${String(waveformBarsMax)} bars.`,
|
|
39
39
|
});
|
|
40
40
|
if (!file.contentType.startsWith("audio/"))
|
|
41
41
|
return problem(c, {
|
|
42
42
|
status: 400,
|
|
43
|
-
title: titleOf(400),
|
|
44
43
|
detail: "Only an audio file has a waveform. This file plays without one.",
|
|
45
44
|
});
|
|
46
45
|
if (decode === undefined)
|
|
47
46
|
return problem(c, {
|
|
48
47
|
status: 503,
|
|
49
|
-
title: titleOf(503),
|
|
50
48
|
detail: "Slopify can't draw the waveform because ffmpeg isn't available. The audio still plays. Restart Slopify; if it keeps happening, use Download diagnostics in Settings and report it.",
|
|
51
49
|
});
|
|
52
50
|
const key = `${file.path}\0${String(file.bytes)}`;
|
|
@@ -58,8 +56,7 @@ export function waveformAnswer(decode) {
|
|
|
58
56
|
catch (error) {
|
|
59
57
|
return problem(c, {
|
|
60
58
|
status: 500,
|
|
61
|
-
|
|
62
|
-
detail: `Slopify couldn't read this audio to draw its waveform (${error instanceof Error ? error.message : String(error)}). The audio still plays.`,
|
|
59
|
+
detail: `Slopify couldn't read this audio to draw its waveform (${errorText(error)}). The audio still plays.`,
|
|
63
60
|
});
|
|
64
61
|
}
|
|
65
62
|
cache.set(key, waveform);
|