@gentbajko/slopify 3.25.9 → 4.1.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/audio.js +1 -1
- package/dist/adapters/alignment/cache.js +4 -6
- package/dist/adapters/alignment/ctc.js +1 -2
- package/dist/adapters/alignment/lock.js +4 -3
- package/dist/adapters/alignment/multilingual.js +2 -4
- package/dist/adapters/alignment/prefetch.js +2 -1
- package/dist/adapters/alignment/runner.js +1 -1
- package/dist/adapters/alignment/worker.js +3 -2
- package/dist/adapters/explain.js +10 -12
- package/dist/adapters/ffmpeg.js +19 -1
- package/dist/adapters/host-cli/index.js +21 -4
- package/dist/adapters/host-cli/transport.js +2 -1
- package/dist/adapters/http-failure.js +53 -0
- package/dist/adapters/image/bytes.js +2 -1
- package/dist/adapters/image/codex.js +42 -9
- package/dist/adapters/image/fal.js +21 -53
- package/dist/adapters/image/google.js +33 -56
- package/dist/adapters/image/models.js +2 -2
- package/dist/adapters/image/openai.js +22 -48
- package/dist/adapters/image/reference.js +36 -0
- package/dist/adapters/image/replicate.js +21 -49
- package/dist/adapters/image/video.js +2 -1
- package/dist/adapters/interest/search-interest.js +113 -0
- package/dist/adapters/key-probes.js +54 -3
- package/dist/adapters/llm/antigravity-models.js +1 -1
- package/dist/adapters/llm/antigravity.js +11 -6
- 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 +52 -99
- package/dist/adapters/llm/claude-code.js +16 -4
- package/dist/adapters/llm/codex-limits.js +27 -68
- package/dist/adapters/llm/codex-models.js +1 -1
- package/dist/adapters/llm/codex-server-models.js +61 -109
- package/dist/adapters/llm/codex.js +12 -1
- package/dist/adapters/llm/document-workspace.js +3 -2
- package/dist/adapters/llm/image-workspace.js +3 -2
- package/dist/adapters/llm/openrouter.js +12 -48
- package/dist/adapters/llm/run-cli.js +9 -24
- 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 +21 -41
- package/dist/adapters/tts/inworld-async.js +6 -2
- package/dist/adapters/tts/inworld.js +6 -2
- package/dist/adapters/tts/openai.js +20 -50
- package/dist/adapters/tts/system.js +2 -1
- 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 +23 -14
- package/dist/catalog/schema.js +4 -1
- package/dist/catalog/store.js +39 -0
- package/dist/catalog/validate.js +5 -2
- 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 +11 -32
- package/dist/edge/http/actor.js +5 -3
- package/dist/edge/http/app.js +86 -11
- package/dist/edge/http/assistant.js +129 -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 +2 -4
- package/dist/edge/http/channel-memory.js +9 -22
- package/dist/edge/http/channels.js +14 -27
- package/dist/edge/http/checkpoints.js +39 -21
- 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 +5 -14
- package/dist/edge/http/draft-files.js +1 -3
- package/dist/edge/http/draft-problem.js +5 -10
- 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 +47 -14
- 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 +30 -26
- package/dist/edge/http/open-folder.js +5 -10
- package/dist/edge/http/outputs.js +7 -14
- package/dist/edge/http/patch-notes.js +6 -12
- package/dist/edge/http/planning.js +3 -7
- package/dist/edge/http/problem.js +2 -4
- package/dist/edge/http/project-create.js +2 -1
- package/dist/edge/http/project-templates.js +31 -19
- package/dist/edge/http/projects.js +24 -16
- package/dist/edge/http/prompts.js +9 -16
- package/dist/edge/http/pronunciations.js +1 -2
- package/dist/edge/http/providers.js +48 -34
- 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 +12 -25
- package/dist/edge/http/revision-files.js +6 -11
- package/dist/edge/http/revisions.js +14 -19
- package/dist/edge/http/run-cost.js +1 -2
- package/dist/edge/http/same-page.js +80 -0
- package/dist/edge/http/schedules.js +69 -40
- package/dist/edge/http/settings.js +27 -14
- package/dist/edge/http/social.js +283 -0
- package/dist/edge/http/staging.js +3 -5
- package/dist/edge/http/storage-files.js +5 -7
- package/dist/edge/http/storage.js +33 -25
- package/dist/edge/http/studio.js +94 -69
- package/dist/edge/http/style-preview.js +3 -6
- package/dist/edge/http/subtitles.js +3 -17
- package/dist/edge/http/template-studio.js +436 -0
- package/dist/edge/http/trash.js +11 -12
- 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 +3 -4
- 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 +29 -3
- package/dist/edge/mcp/http.js +11 -2
- package/dist/edge/mcp/instructions.js +6 -4
- package/dist/edge/mcp/library-tools.js +9 -5
- package/dist/edge/mcp/remake-tools.js +7 -4
- package/dist/edge/mcp/route-tool.js +35 -15
- package/dist/edge/mcp/server.js +13 -8
- package/dist/edge/mcp/tools-channels.js +11 -9
- package/dist/edge/mcp/tools-designs.js +165 -0
- package/dist/edge/mcp/tools-library.js +19 -7
- package/dist/edge/mcp/tools-planning.js +40 -21
- package/dist/edge/mcp/tools-projects.js +38 -11
- package/dist/edge/mcp/tools-publishing.js +9 -9
- package/dist/edge/mcp/tools-runs.js +23 -10
- package/dist/edge/mcp/tools-schedules.js +32 -8
- package/dist/edge/mcp/tools-setup.js +75 -11
- package/dist/edge/mcp/tools-social.js +47 -0
- package/dist/edge/mcp/tools-storage.js +11 -3
- 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/migrations/0078-uploaded-from-links.sql +7 -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/pipeline.js +12 -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/ports/settings-places.js +31 -0
- package/dist/kernel/ports/system-speech.js +31 -3
- package/dist/kernel/runner/attempt.js +14 -4
- 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 +18 -12
- package/dist/kernel/runner/standalone.js +11 -7
- package/dist/main.js +338 -21
- package/dist/model-catalog.js +20 -0
- package/dist/model-checks.js +166 -0
- package/dist/patch-notes/4.0.0.md +51 -0
- package/dist/patch-notes/4.1.0.md +42 -0
- package/dist/patch-notes/index.json +12 -0
- package/dist/slices/admission/heads.js +2 -1
- package/dist/slices/admission/listing-facts.js +95 -0
- package/dist/slices/admission/repo.js +11 -7
- package/dist/slices/admission/rules.js +50 -15
- 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/state-words.js +34 -5
- 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 +86 -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 +344 -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 +8 -7
- package/dist/slices/channels/cast-images.js +3 -2
- 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 +7 -1
- package/dist/slices/channels/service.js +10 -0
- package/dist/slices/channels/videos.js +3 -15
- package/dist/slices/checkpoints/index.js +0 -1
- package/dist/slices/control/providers.js +2 -28
- 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 +130 -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 +223 -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/document/model.js +7 -4
- package/dist/slices/document/render.js +2 -1
- package/dist/slices/document/theme-schema.js +1 -1
- package/dist/slices/document/theme.js +49 -3
- package/dist/slices/episodes/summarize.js +6 -2
- package/dist/slices/estimate/index.js +72 -28
- package/dist/slices/estimate/requests.js +4 -4
- package/dist/slices/fixes/rules.js +3 -1
- package/dist/slices/fonts/catalog.js +5 -3
- package/dist/slices/fonts/coverage.js +2 -1
- 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 +8 -16
- package/dist/slices/images/scenes.js +102 -28
- package/dist/slices/keywords/digest.js +85 -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 +5 -4
- package/dist/slices/library/used-by.js +25 -10
- package/dist/slices/loudness/loudnorm.js +53 -12
- package/dist/slices/loudness/model.js +3 -2
- package/dist/slices/loudness/sentence-level.js +2 -1
- 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 +125 -0
- package/dist/slices/model-checks/run.js +143 -0
- package/dist/slices/model-checks/service.js +198 -0
- package/dist/slices/model-upkeep/model.js +1 -1
- package/dist/slices/model-upkeep/switch.js +72 -27
- package/dist/slices/model-upkeep/usage.js +39 -13
- package/dist/slices/narration/breaths.js +3 -1
- package/dist/slices/narration/clean-pieces.js +3 -1
- package/dist/slices/narration/concat.js +2 -1
- package/dist/slices/narration/pauses-model.js +3 -2
- package/dist/slices/narration/peaks.js +3 -4
- package/dist/slices/narration/pronunciation-chunks.js +4 -3
- package/dist/slices/narration/pronunciation.js +6 -4
- 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 +4 -3
- package/dist/slices/onboarding/copy.js +21 -3
- package/dist/slices/onboarding/first-run.js +16 -40
- package/dist/slices/onboarding/full-video.js +3 -2
- package/dist/slices/onboarding/install.js +6 -4
- package/dist/slices/onboarding/model.js +26 -37
- package/dist/slices/onboarding/packs.js +22 -22
- package/dist/slices/onboarding/preferred-model.js +14 -0
- package/dist/slices/onboarding/quick-short.js +105 -65
- package/dist/slices/onboarding/sample.js +4 -2
- package/dist/slices/onboarding/state.js +9 -11
- package/dist/slices/onboarding/template.js +8 -6
- package/dist/slices/outputs/add.js +3 -2
- package/dist/slices/outputs/service.js +2 -2
- package/dist/slices/patch-notes/library.js +4 -3
- package/dist/slices/play-drafts/convert.js +4 -0
- package/dist/slices/play-drafts/readiness.js +3 -3
- package/dist/slices/play-drafts/review-inputs.js +9 -3
- package/dist/slices/play-drafts/review.js +5 -4
- package/dist/slices/play-drafts/schema.js +4 -0
- package/dist/slices/play-drafts/service.js +17 -7
- package/dist/slices/project-templates/from-project.js +9 -2
- package/dist/slices/project-templates/next-chapter.js +2 -1
- package/dist/slices/project-templates/provider-defaults.js +22 -0
- package/dist/slices/project-templates/repo.js +6 -1
- package/dist/slices/project-templates/setup-sections.js +26 -0
- package/dist/slices/project-templates/setup.js +14 -1
- 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 +32 -14
- 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 +4 -4
- 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 +8 -5
- 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 +102 -31
- package/dist/slices/rebuild/recipe-visual.js +55 -16
- package/dist/slices/rebuild/recipe-voices.js +3 -2
- package/dist/slices/rebuild/recipe-work.js +19 -7
- package/dist/slices/rebuild/recipe-youtube.js +4 -3
- package/dist/slices/rebuild/recovery-selection.js +3 -1
- package/dist/slices/rebuild/recovery.js +9 -8
- package/dist/slices/rebuild/remake-outdated.js +5 -5
- package/dist/slices/rebuild/repo.js +4 -3
- package/dist/slices/rebuild/review-promise.js +25 -0
- package/dist/slices/rebuild/review-redo.js +3 -2
- 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 +10 -9
- package/dist/slices/rebuild/runtime-article.js +7 -5
- package/dist/slices/rebuild/runtime-checkpoints.js +5 -4
- package/dist/slices/rebuild/runtime-document.js +11 -13
- package/dist/slices/rebuild/runtime-export-bed.js +8 -12
- package/dist/slices/rebuild/runtime-export-edit.js +8 -16
- package/dist/slices/rebuild/runtime-export-inputs.js +12 -11
- package/dist/slices/rebuild/runtime-export-short.js +9 -5
- package/dist/slices/rebuild/runtime-export.js +38 -15
- package/dist/slices/rebuild/runtime-figure-card.js +2 -1
- package/dist/slices/rebuild/runtime-image.js +8 -12
- 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 +17 -22
- 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 +13 -17
- 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 +67 -25
- package/dist/slices/rebuild/runtime-publication.js +10 -9
- package/dist/slices/rebuild/runtime-review.js +39 -33
- package/dist/slices/rebuild/runtime-run.js +4 -1
- package/dist/slices/rebuild/runtime-shorts.js +31 -50
- package/dist/slices/rebuild/runtime-store.js +20 -10
- package/dist/slices/rebuild/runtime-subtitles.js +28 -25
- package/dist/slices/rebuild/runtime-voices.js +10 -16
- package/dist/slices/rebuild/runtime-youtube.js +14 -31
- package/dist/slices/rebuild/service-readiness.js +11 -3
- 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/research/synthesis.js +3 -2
- package/dist/slices/reviews/external-model.js +4 -1
- package/dist/slices/reviews/external-repo.js +2 -1
- package/dist/slices/reviews/external-rows.js +2 -3
- package/dist/slices/reviews/holds.js +5 -4
- package/dist/slices/reviews/model.js +32 -11
- package/dist/slices/reviews/repo.js +2 -1
- package/dist/slices/reviews/rules.js +4 -3
- package/dist/slices/reviews/verdict.js +2 -1
- package/dist/slices/reviews/view.js +5 -2
- package/dist/slices/revisions/downloads.js +4 -5
- package/dist/slices/revisions/image-plan.js +1 -1
- package/dist/slices/revisions/mutation-assets.js +9 -5
- package/dist/slices/revisions/mutation-cues.js +2 -5
- package/dist/slices/revisions/mutations.js +3 -3
- 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 +18 -7
- 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 +30 -21
- package/dist/slices/schedules/rescore.js +35 -0
- package/dist/slices/schedules/scheduler.js +13 -3
- package/dist/slices/schedules/schema.js +57 -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 +53 -41
- package/dist/slices/settings/cli-paths.js +4 -6
- package/dist/slices/settings/first-run.js +8 -43
- package/dist/slices/settings/health.js +30 -15
- package/dist/slices/settings/key-test.js +1 -1
- package/dist/slices/settings/model-reach.js +3 -17
- package/dist/slices/settings/model.js +14 -2
- package/dist/slices/settings/playback.js +34 -23
- 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/keep.js +2 -1
- package/dist/slices/shorts/pick.js +6 -18
- package/dist/slices/shorts/prompts.js +4 -14
- package/dist/slices/shorts/render.js +6 -2
- 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 +132 -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 +56 -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 +129 -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 +22 -4
- 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 +17 -12
- 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 +26 -9
- package/dist/slices/studio/calendar.js +7 -2
- package/dist/slices/studio/disclosure.js +4 -4
- package/dist/slices/studio/model.js +1 -1
- package/dist/slices/studio/pack.js +9 -8
- package/dist/slices/studio/pick.js +2 -11
- package/dist/slices/studio/plan.js +7 -16
- package/dist/slices/studio/queue.js +3 -14
- package/dist/slices/studio/releases.js +26 -0
- package/dist/slices/studio/settings.js +20 -57
- package/dist/slices/studio/videos.js +35 -4
- package/dist/slices/style-preview/render.js +37 -16
- package/dist/slices/style-preview/schema.js +14 -1
- package/dist/slices/style-preview/service.js +2 -1
- package/dist/slices/style-preview/settings.js +33 -5
- package/dist/slices/subtitles/captions.js +1 -1
- 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 +161 -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 +106 -0
- package/dist/slices/template-studio/cards.js +78 -0
- package/dist/slices/template-studio/checks.js +322 -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 +58 -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 +207 -0
- package/dist/slices/template-studio/paid-view.js +66 -0
- package/dist/slices/template-studio/paid.js +355 -0
- package/dist/slices/template-studio/quote.js +155 -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 +32 -0
- package/dist/slices/template-studio/schedule-sample.js +91 -0
- package/dist/slices/template-studio/schema.js +243 -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 +175 -0
- package/dist/slices/template-studio/status.js +191 -0
- package/dist/slices/template-studio/topic.js +223 -0
- package/dist/slices/thumbnail/by-llm.js +1 -1
- 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/uploads/repo.js +37 -1
- package/dist/slices/uploads/rules.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/edit-list.js +2 -2
- package/dist/slices/video/edit-settings.js +1 -1
- package/dist/slices/video/encoding.js +169 -0
- package/dist/slices/video/ffmpeg.js +23 -21
- package/dist/slices/video/figure-card.js +1 -15
- package/dist/slices/video/plan.js +2 -1
- package/dist/slices/video/slideshow.js +43 -18
- package/dist/slices/voices/attribution.js +3 -2
- package/dist/slices/voices/model.js +9 -8
- package/dist/slices/voices/palette.js +0 -3
- package/dist/slices/voices/script.js +2 -1
- package/dist/slices/youtube/answer.js +4 -16
- package/dist/slices/youtube/edits-repo.js +3 -12
- package/dist/tutorials/AI-CLIs.md +25 -25
- package/dist/tutorials/Agents-MCP.md +34 -33
- package/dist/tutorials/Assistant.md +42 -0
- package/dist/tutorials/Backups.md +7 -7
- package/dist/tutorials/Calendar.md +17 -25
- package/dist/tutorials/Cast-Library.md +19 -19
- package/dist/tutorials/Channels.md +42 -35
- package/dist/tutorials/Costs-and-Run-Cost.md +38 -40
- package/dist/tutorials/Create-Article.md +114 -0
- package/dist/tutorials/Create-Extras.md +112 -0
- package/dist/tutorials/Create-Images.md +165 -0
- package/dist/tutorials/Create-Narration.md +178 -0
- package/dist/tutorials/Create-Overview.md +244 -0
- package/dist/tutorials/Create-Video-and-Style.md +144 -0
- package/dist/tutorials/Docker.md +3 -3
- package/dist/tutorials/Document-Themes.md +32 -32
- package/dist/tutorials/Editing-a-Project.md +94 -94
- package/dist/tutorials/FAQ.md +18 -18
- package/dist/tutorials/First-Launch-and-Welcome.md +50 -64
- package/dist/tutorials/Home-and-Projects.md +115 -71
- package/dist/tutorials/Home.md +33 -33
- package/dist/tutorials/Install.md +14 -13
- package/dist/tutorials/Intros-and-Outros.md +33 -34
- package/dist/tutorials/Keyboard-Shortcuts-and-Command-Palette.md +62 -58
- package/dist/tutorials/Keywords-Slopify-Fills.md +87 -0
- package/dist/tutorials/Models.md +53 -21
- package/dist/tutorials/Multiple-Voices.md +41 -40
- package/dist/tutorials/Narration-Aliases-and-Glossary.md +14 -14
- package/dist/tutorials/Notifications.md +27 -17
- package/dist/tutorials/Other-Languages.md +15 -15
- package/dist/tutorials/PDF-Documents.md +38 -29
- package/dist/tutorials/Posting-to-Social-Sites.md +121 -0
- package/dist/tutorials/Project-Page.md +118 -120
- package/dist/tutorials/Prompts-and-Templates.md +84 -0
- package/dist/tutorials/Prompts.md +48 -44
- package/dist/tutorials/Providers-and-Keys.md +105 -98
- package/dist/tutorials/Publishing-to-YouTube.md +31 -31
- package/dist/tutorials/Recovery-and-Retries.md +31 -31
- package/dist/tutorials/Remote-Access.md +63 -0
- package/dist/tutorials/Reviews-and-Checkpoints.md +98 -90
- package/dist/tutorials/Schedules.md +46 -27
- package/dist/tutorials/Settings-Reference.md +194 -174
- package/dist/tutorials/Shorts.md +34 -33
- package/dist/tutorials/Start-at-Login.md +3 -3
- package/dist/tutorials/Studio-Extension.md +22 -24
- package/dist/tutorials/Template-Studio.md +128 -0
- package/dist/tutorials/Templates.md +51 -77
- package/dist/tutorials/Trash.md +4 -4
- package/dist/tutorials/Troubleshooting.md +44 -44
- package/dist/tutorials/Uninstalling-and-Moving.md +5 -5
- package/dist/tutorials/Updating-and-Patch-Notes.md +18 -18
- package/dist/tutorials/Video-Editing.md +34 -34
- package/dist/tutorials/Where-Your-Files-Live.md +10 -10
- package/dist/tutorials/YouTube-Description.md +26 -26
- package/dist/tutorials/Your-First-Short.md +35 -35
- package/dist/tutorials/_Sidebar.md +19 -14
- package/dist/updater/work-in-progress.js +42 -12
- package/dist/web/assets/index-BNftKQrA.css +1 -0
- package/dist/web/assets/index-Cpa58woP.js +459 -0
- package/dist/web/assets/pdf-BKh8f7R6.js +56 -0
- package/dist/web/assets/pdf.worker.min-BmVo14Nb.mjs +28 -0
- package/dist/web/assets/pdf.worker.min-D7i8o3wR.js +1 -0
- package/dist/web/index.html +2 -2
- package/package.json +6 -2
- 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/tutorials/Library-Overview.md +0 -76
- package/dist/tutorials/Play-Images.md +0 -161
- package/dist/tutorials/Play-Narration.md +0 -167
- package/dist/tutorials/Play-Outputs.md +0 -109
- package/dist/tutorials/Play-Overview.md +0 -227
- package/dist/tutorials/Play-Title-and-Article.md +0 -136
- package/dist/tutorials/Play-Video-and-Style.md +0 -143
- package/dist/web/assets/index-BadsoORk.css +0 -1
- package/dist/web/assets/index-D8LqNsCl.js +0 -326
- package/dist/web/assets/pdf-T7MCwjIv.js +0 -54
- package/dist/web/assets/pdf.worker.min-Dswkl-cV.mjs +0 -29
- package/dist/web/assets/pdf.worker.min-zC6nfbxG.js +0 -1
|
@@ -2,18 +2,26 @@ import { zValidator } from "@hono/zod-validator";
|
|
|
2
2
|
import { Hono } from "hono";
|
|
3
3
|
import { HTTPException } from "hono/http-exception";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
+
import { setupSectionNames } from "../../slices/project-templates/setup-sections.js";
|
|
5
6
|
import { calendarRange } from "../../slices/schedules/agenda.js";
|
|
6
7
|
import { rejectHeldTopics, restoreRejectedTopics } from "../../slices/schedules/held-bulk.js";
|
|
7
8
|
import { preparedTopics, prepareTopic } from "../../slices/schedules/prepare.js";
|
|
8
|
-
import {
|
|
9
|
+
import { rescoreTopics } from "../../slices/schedules/rescore.js";
|
|
10
|
+
import { calendarMaxDays, calendarSchema, heldTopicEditSchema, heldTopicSchema, queueMax, scheduleCreateSchema, scheduleRunSchema, scheduleSaveBodySchema, scheduleSummarySchema, scheduleVersionBodySchema, topicMoveSchema, topicQueueSchema, topicTransferSchema, } from "../../slices/schedules/schema.js";
|
|
9
11
|
import { cancelSchedule, createSchedule, deleteSchedule, listScheduleRuns, listSchedules, pauseSchedule, resumeSchedule, updateSchedule, } from "../../slices/schedules/service.js";
|
|
12
|
+
import { scoresFor } from "../../slices/schedules/topic-scores.js";
|
|
10
13
|
import { approveHeldTopics, editHeldTopic, heldTopics, moveTopic, rejectHeldTopic, replaceTopics, transferTopic, } from "../../slices/schedules/topics.js";
|
|
11
|
-
import { onInvalid, problem
|
|
14
|
+
import { onInvalid, problem } from "./problem.js";
|
|
15
|
+
import { refusal } from "./refusal.js";
|
|
12
16
|
const id = z.object({ id: z.uuid() });
|
|
13
17
|
const topicParam = z.object({ id: z.uuid(), topicId: z.string().min(1).max(100) });
|
|
14
18
|
const topicIds = z.array(z.string().min(1).max(100)).min(1).max(queueMax);
|
|
15
|
-
|
|
16
|
-
const
|
|
19
|
+
// Prepare one queued topic: its title as the queue lists it.
|
|
20
|
+
export const prepareTopicBody = z.object({ topic: z.string().min(1).max(500) }).strict();
|
|
21
|
+
// Approve one held topic, optionally under another title.
|
|
22
|
+
export const approveHeldTopicBody = z.object({ title: z.string().optional() }).strict();
|
|
23
|
+
export const heldIds = z.object({ ids: topicIds }).strict();
|
|
24
|
+
export const heldRestore = z
|
|
17
25
|
.object({
|
|
18
26
|
topics: z
|
|
19
27
|
.array(z
|
|
@@ -27,38 +35,36 @@ const heldRestore = z
|
|
|
27
35
|
})
|
|
28
36
|
.strict();
|
|
29
37
|
function refused(c, result) {
|
|
30
|
-
const status = ["not-found", "missing-template", "topic-not-found"].includes(result.reason)
|
|
31
|
-
? 404
|
|
32
|
-
: [
|
|
33
|
-
"conflict",
|
|
34
|
-
"cancel-required",
|
|
35
|
-
"spend-limit",
|
|
36
|
-
"unsupported-media",
|
|
37
|
-
"queue-full",
|
|
38
|
-
"busy",
|
|
39
|
-
].includes(result.reason)
|
|
40
|
-
? 409
|
|
41
|
-
: 400;
|
|
42
38
|
const detail = {
|
|
43
39
|
"invalid-input": "Some schedule settings are not valid. Check each field and save again.",
|
|
44
40
|
"not-found": "This schedule no longer exists; it may have been deleted. Reload the page to see your schedules.",
|
|
45
41
|
conflict: "This schedule changed while you were editing, possibly because a run just started. Reload the page to see the latest, then make your change again.",
|
|
46
42
|
"cancel-required": "This schedule is still active. Cancel it first, then delete it. Completed schedules can be deleted straight away.",
|
|
47
43
|
"missing-template": "The template you picked was deleted or changed. Choose a template again, then save.",
|
|
48
|
-
"unsupported-media":
|
|
44
|
+
"unsupported-media": `This template uses audio, images, a thumbnail, shorts background music or an ambient sound file you supplied, so it cannot run on a schedule: a scheduled run has no one to attach the file again. Pick a template that generates these instead, or open this one in Edit template, remove the music under ${setupSectionNames.shorts} (or pick Rain, Fireplace or Wind as the Ambient sound under ${setupSectionNames.video}) and save it again.`,
|
|
49
45
|
"not-due": "This one-off time has already passed. Choose a time in the future.",
|
|
50
46
|
"spend-limit": "The estimated cost is above this schedule's spend limit, or some prices are unknown. Raise the spend limit or choose models with known prices.",
|
|
51
|
-
readiness: "A provider this template needs is not ready, for example a missing API key, model or voice. Check Settings →
|
|
47
|
+
readiness: "A provider this template needs is not ready, for example a missing API key, model or voice. Check Settings → Keys & services, then try again.",
|
|
52
48
|
"queue-full": `A schedule's topic queue holds at most ${String(queueMax)} topics. Remove some under Schedules → Edit, then try again.`,
|
|
53
49
|
"topic-not-found": "This topic was already approved, turned down or used. Reload the page to see the topics waiting now.",
|
|
54
50
|
busy: "Slopify is already generating topics for this schedule. Wait for it to finish, then try again.",
|
|
51
|
+
"not-ready": "This schedule's template has no version approved for schedules. Open it from Templates with Edit template, then choose More → Approve for schedules.",
|
|
55
52
|
"invalid-topics": "Some topics name keywords this template does not use. Check each topic under Schedules → Edit and save again.",
|
|
56
53
|
};
|
|
57
|
-
return
|
|
58
|
-
|
|
59
|
-
|
|
54
|
+
return refusal(c, result.reason, {
|
|
55
|
+
statuses: {
|
|
56
|
+
"not-found": 404,
|
|
57
|
+
"missing-template": 404,
|
|
58
|
+
"topic-not-found": 404,
|
|
59
|
+
conflict: 409,
|
|
60
|
+
"cancel-required": 409,
|
|
61
|
+
"spend-limit": 409,
|
|
62
|
+
"unsupported-media": 409,
|
|
63
|
+
"queue-full": 409,
|
|
64
|
+
busy: 409,
|
|
65
|
+
},
|
|
66
|
+
otherwise: 400,
|
|
60
67
|
detail: result.message ?? detail[result.reason],
|
|
61
|
-
extensions: { reason: result.reason },
|
|
62
68
|
});
|
|
63
69
|
}
|
|
64
70
|
export function scheduleRoutes(deps) {
|
|
@@ -90,38 +96,44 @@ function routes(deps) {
|
|
|
90
96
|
? c.json({
|
|
91
97
|
schedule: scheduleSummarySchema.parse(schedule),
|
|
92
98
|
runs: runs.value.map((run) => scheduleRunSchema.parse(run)),
|
|
99
|
+
// Each queued and held topic's score and reasons, by `topicKey`, while ranking
|
|
100
|
+
// is on.
|
|
101
|
+
scores: scoresFor(service().db, schedule.id, [
|
|
102
|
+
...schedule.items.map((item) => item.title),
|
|
103
|
+
...heldTopics(service(), schedule.id).map((topic) => topic.title),
|
|
104
|
+
]),
|
|
93
105
|
})
|
|
94
106
|
: refused(c, runs);
|
|
95
107
|
})
|
|
96
|
-
.put("/:id", zValidator("param", id, onInvalid), zValidator("json",
|
|
108
|
+
.put("/:id", zValidator("param", id, onInvalid), zValidator("json", scheduleSaveBodySchema, onInvalid), (c) => {
|
|
97
109
|
const result = updateSchedule(service(), {
|
|
98
110
|
...c.req.valid("json"),
|
|
99
111
|
id: c.req.valid("param").id,
|
|
100
112
|
});
|
|
101
113
|
return result.ok ? c.json(scheduleSummarySchema.parse(result.value)) : refused(c, result);
|
|
102
114
|
})
|
|
103
|
-
.post("/:id/pause", zValidator("param", id, onInvalid), zValidator("json",
|
|
115
|
+
.post("/:id/pause", zValidator("param", id, onInvalid), zValidator("json", scheduleVersionBodySchema, onInvalid), (c) => {
|
|
104
116
|
const result = pauseSchedule(service(), {
|
|
105
117
|
...c.req.valid("json"),
|
|
106
118
|
id: c.req.valid("param").id,
|
|
107
119
|
});
|
|
108
120
|
return result.ok ? c.json(scheduleSummarySchema.parse(result.value)) : refused(c, result);
|
|
109
121
|
})
|
|
110
|
-
.post("/:id/resume", zValidator("param", id, onInvalid), zValidator("json",
|
|
122
|
+
.post("/:id/resume", zValidator("param", id, onInvalid), zValidator("json", scheduleVersionBodySchema, onInvalid), (c) => {
|
|
111
123
|
const result = resumeSchedule(service(), {
|
|
112
124
|
...c.req.valid("json"),
|
|
113
125
|
id: c.req.valid("param").id,
|
|
114
126
|
});
|
|
115
127
|
return result.ok ? c.json(scheduleSummarySchema.parse(result.value)) : refused(c, result);
|
|
116
128
|
})
|
|
117
|
-
.post("/:id/cancel", zValidator("param", id, onInvalid), zValidator("json",
|
|
129
|
+
.post("/:id/cancel", zValidator("param", id, onInvalid), zValidator("json", scheduleVersionBodySchema, onInvalid), (c) => {
|
|
118
130
|
const result = cancelSchedule(service(), {
|
|
119
131
|
...c.req.valid("json"),
|
|
120
132
|
id: c.req.valid("param").id,
|
|
121
133
|
});
|
|
122
134
|
return result.ok ? c.json(scheduleSummarySchema.parse(result.value)) : refused(c, result);
|
|
123
135
|
})
|
|
124
|
-
.delete("/:id", zValidator("param", id, onInvalid), zValidator("json",
|
|
136
|
+
.delete("/:id", zValidator("param", id, onInvalid), zValidator("json", scheduleVersionBodySchema, onInvalid), (c) => {
|
|
125
137
|
const result = deleteSchedule(service(), {
|
|
126
138
|
...c.req.valid("json"),
|
|
127
139
|
id: c.req.valid("param").id,
|
|
@@ -132,14 +144,31 @@ function routes(deps) {
|
|
|
132
144
|
const scheduleId = c.req.valid("param").id;
|
|
133
145
|
if (listSchedules(service()).every((item) => item.id !== scheduleId))
|
|
134
146
|
return refused(c, { ok: false, reason: "not-found" });
|
|
147
|
+
const topics = heldTopics(service(), scheduleId).map((topic) => heldTopicSchema.parse(topic));
|
|
135
148
|
return c.json({
|
|
136
|
-
topics
|
|
149
|
+
topics,
|
|
150
|
+
scores: scoresFor(service().db, scheduleId, topics.map((topic) => topic.title)),
|
|
137
151
|
});
|
|
152
|
+
})
|
|
153
|
+
// Rescore: every queued and held topic scored again, then put in score order. With search
|
|
154
|
+
// ranking it asks Google, DuckDuckGo and Wikipedia, up to 15 seconds.
|
|
155
|
+
.post("/:id/topics/score", zValidator("param", id, onInvalid), async (c) => {
|
|
156
|
+
const result = await rescoreTopics(service(), c.req.valid("param").id);
|
|
157
|
+
if (result.ok)
|
|
158
|
+
return c.json(scheduleSummarySchema.parse(result.value));
|
|
159
|
+
if (result.reason === "scoring-off")
|
|
160
|
+
return problem(c, {
|
|
161
|
+
status: 409,
|
|
162
|
+
detail: result.message,
|
|
163
|
+
extensions: { reason: result.reason },
|
|
164
|
+
});
|
|
165
|
+
return refused(c, result);
|
|
138
166
|
})
|
|
139
167
|
// Which queued topics are prepared ahead, and their projects.
|
|
140
168
|
.get("/:id/prepared", zValidator("param", id, onInvalid), (c) => c.json({ prepared: preparedTopics(service(), c.req.valid("param").id) }))
|
|
141
|
-
//
|
|
142
|
-
|
|
169
|
+
// Make ahead one queued topic: everything but the video now, which its scheduled day puts
|
|
170
|
+
// together.
|
|
171
|
+
.post("/:id/topics/prepare", zValidator("param", id, onInvalid), zValidator("json", prepareTopicBody, onInvalid), async (c) => {
|
|
143
172
|
const result = await prepareTopic(service(), {
|
|
144
173
|
scheduleId: c.req.valid("param").id,
|
|
145
174
|
title: c.req.valid("json").topic,
|
|
@@ -148,17 +177,19 @@ function routes(deps) {
|
|
|
148
177
|
return c.json({ projectId: result.projectId, title: result.title }, 201);
|
|
149
178
|
const detail = {
|
|
150
179
|
"not-found": "This topic is no longer in the schedule's queue. Reload the page to see the queue as it is now.",
|
|
151
|
-
"already-prepared": "This topic is already
|
|
152
|
-
|
|
153
|
-
"
|
|
180
|
+
"already-prepared": "This topic is already made ahead: its project is in Projects under the same title.",
|
|
181
|
+
paused: "Its schedule is paused, so it makes nothing, ahead or on its day. Resume it under Calendar → Schedules, then press Make ahead again.",
|
|
182
|
+
"missing-template": "This schedule's template was deleted, so there is nothing to make it from. Choose another template under Edit.",
|
|
183
|
+
"spend-limit": "Making it ahead would go over this schedule's spend limit. Raise the limit under Edit, or make it from Create instead.",
|
|
184
|
+
design: "A poster schedule makes each poster on its day, so there is nothing to make ahead. To have one now, open its poster template (Templates → Poster templates) and press Make a poster; to have it sooner on a schedule, give its row an earlier date in the Schedule section.",
|
|
154
185
|
};
|
|
155
186
|
return problem(c, {
|
|
156
187
|
status: result.reason === "not-found" ? 404 : 409,
|
|
157
|
-
|
|
158
|
-
|
|
188
|
+
detail: (result.reason === "not-ready" ? result.detail : undefined) ??
|
|
189
|
+
detail[result.reason] ??
|
|
159
190
|
(result.detail === undefined
|
|
160
|
-
? `This topic couldn't be
|
|
161
|
-
: `This topic couldn't be
|
|
191
|
+
? `This topic couldn't be made ahead (${result.reason}). Open Create with the schedule's template to see what the setup needs, then try again.`
|
|
192
|
+
: `This topic couldn't be made ahead: ${result.detail} Fix it in the schedule's template (Templates → Edit template), then try again.`),
|
|
162
193
|
extensions: { reason: result.reason },
|
|
163
194
|
});
|
|
164
195
|
})
|
|
@@ -181,7 +212,6 @@ function routes(deps) {
|
|
|
181
212
|
if (refusal !== undefined)
|
|
182
213
|
return problem(c, {
|
|
183
214
|
status: 409,
|
|
184
|
-
title: titleOf(409),
|
|
185
215
|
detail: refusal,
|
|
186
216
|
extensions: { reason: "busy" },
|
|
187
217
|
});
|
|
@@ -206,7 +236,7 @@ function routes(deps) {
|
|
|
206
236
|
const result = restoreRejectedTopics(service(), c.req.valid("param").id, c.req.valid("json").topics);
|
|
207
237
|
return result.ok ? c.json(scheduleSummarySchema.parse(result.value)) : refused(c, result);
|
|
208
238
|
})
|
|
209
|
-
.post("/:id/topics/held/:topicId/approve", zValidator("param", topicParam, onInvalid), zValidator("json",
|
|
239
|
+
.post("/:id/topics/held/:topicId/approve", zValidator("param", topicParam, onInvalid), zValidator("json", approveHeldTopicBody, onInvalid), (c) => {
|
|
210
240
|
const { id: scheduleId, topicId } = c.req.valid("param");
|
|
211
241
|
const { title } = c.req.valid("json");
|
|
212
242
|
const result = approveHeldTopics(service(), scheduleId, [topicId], title === undefined ? {} : { [topicId]: title });
|
|
@@ -257,7 +287,6 @@ export function calendarRoutes(deps) {
|
|
|
257
287
|
if (!result.ok)
|
|
258
288
|
return problem(c, {
|
|
259
289
|
status: 400,
|
|
260
|
-
title: titleOf(400),
|
|
261
290
|
detail: `Choose a range whose end is after its start and at most ${String(calendarMaxDays)} days long.`,
|
|
262
291
|
extensions: { reason: result.reason },
|
|
263
292
|
});
|
|
@@ -10,9 +10,10 @@ import { readNotificationUrl, saveNotificationUrl } from "../../slices/notificat
|
|
|
10
10
|
import { appearances, providerIds } from "../../slices/settings/model.js";
|
|
11
11
|
import { readSettings, saveSettings } from "../../slices/settings/playback.js";
|
|
12
12
|
import { addVoice, removeVoice, setVoiceImitatesRealPerson, setVoiceLanguages, voiceIdMax, voiceNameMax, voices, } from "../../slices/settings/voices.js";
|
|
13
|
+
import { clipEncodeSpeeds, noNvenc, renderPriorities, videoEncoders, } from "../../slices/video/encoding.js";
|
|
13
14
|
import { readChannelLinks, writeChannelLinks } from "../../slices/youtube/edits-repo.js";
|
|
14
15
|
import { channelLinkNameMax, channelLinksMax, channelLinksProblem, channelLinkUrlMax, } from "../../slices/youtube/placeholders.js";
|
|
15
|
-
import { onInvalid, problem
|
|
16
|
+
import { onInvalid, problem } from "./problem.js";
|
|
16
17
|
const idParam = z.object({ id: z.string().min(1).max(64) });
|
|
17
18
|
// Shape only. The rules - trimming, emptiness, length, the provider speaking at all -
|
|
18
19
|
// are the slice's, so one set of messages reaches the form.
|
|
@@ -26,7 +27,7 @@ const voiceLanguagesBody = z.object({ languages: z.array(z.string().max(20)).max
|
|
|
26
27
|
const voiceRealPersonBody = z.object({ imitatesRealPerson: z.boolean() });
|
|
27
28
|
// Length and scheme are the slice's rules, so their sentences reach the field; this bound only
|
|
28
29
|
// keeps a pasted novel off the parser.
|
|
29
|
-
const notificationBody = z.object({ url: z.string().max(notificationUrlMax * 2) });
|
|
30
|
+
export const notificationBody = z.object({ url: z.string().max(notificationUrlMax * 2) });
|
|
30
31
|
// Shape and a generous bound only; `channelLinksProblem` says what is wrong in Settings' words.
|
|
31
32
|
export const channelLinksBody = z.object({
|
|
32
33
|
links: z
|
|
@@ -45,6 +46,10 @@ const playbackBody = z.object({
|
|
|
45
46
|
.optional(),
|
|
46
47
|
// Absent keeps what is saved, as with loudness.
|
|
47
48
|
subtitleModel: z.enum(englishSubtitleModels).optional(),
|
|
49
|
+
// Absent keeps what is saved, as with loudness.
|
|
50
|
+
videoEncoder: z.enum(videoEncoders).optional(),
|
|
51
|
+
clipEncodeSpeed: z.enum(clipEncodeSpeeds).optional(),
|
|
52
|
+
renderPriority: z.enum(renderPriorities).optional(),
|
|
48
53
|
});
|
|
49
54
|
// The return type is inferred so Hono keeps the route types the SPA's client is
|
|
50
55
|
// generated from; see stagingRoutes.
|
|
@@ -61,16 +66,34 @@ export function settingsRoutes(deps) {
|
|
|
61
66
|
...body,
|
|
62
67
|
loudness: body.loudness ?? saved.loudness,
|
|
63
68
|
subtitleModel: body.subtitleModel ?? saved.subtitleModel,
|
|
69
|
+
videoEncoder: body.videoEncoder ?? saved.videoEncoder,
|
|
70
|
+
clipEncodeSpeed: body.clipEncodeSpeed ?? saved.clipEncodeSpeed,
|
|
71
|
+
renderPriority: body.renderPriority ?? saved.renderPriority,
|
|
64
72
|
});
|
|
65
73
|
if (!result.ok) {
|
|
66
74
|
return problem(c, {
|
|
67
75
|
status: 400,
|
|
68
|
-
title: titleOf(400),
|
|
69
76
|
detail: "These settings cannot be saved yet. Fix the highlighted fields and try again.",
|
|
70
77
|
extensions: { fields: result.fields },
|
|
71
78
|
});
|
|
72
79
|
}
|
|
73
80
|
return c.json(result.settings);
|
|
81
|
+
})
|
|
82
|
+
// Settings → Production defaults → Video encoder offers the NVIDIA card only where its
|
|
83
|
+
// encoder works; the first ask runs a test encode, later ones answer from memory.
|
|
84
|
+
.get("/encoders", async (c) => {
|
|
85
|
+
const status = deps.nvenc === undefined
|
|
86
|
+
? { available: false, reason: noNvenc }
|
|
87
|
+
: await deps.nvenc();
|
|
88
|
+
return c.json({
|
|
89
|
+
nvidia: status.available
|
|
90
|
+
? { available: true }
|
|
91
|
+
: {
|
|
92
|
+
available: false,
|
|
93
|
+
reason: status.reason,
|
|
94
|
+
...(status.detail === undefined ? {} : { detail: status.detail }),
|
|
95
|
+
},
|
|
96
|
+
});
|
|
74
97
|
})
|
|
75
98
|
// The named links `{{Name}}` placeholders in YouTube descriptions fill from
|
|
76
99
|
// (`slices/youtube/placeholders.ts`).
|
|
@@ -81,8 +104,7 @@ export function settingsRoutes(deps) {
|
|
|
81
104
|
if (refused !== undefined)
|
|
82
105
|
return problem(c, {
|
|
83
106
|
status: 400,
|
|
84
|
-
|
|
85
|
-
detail: `The channel links weren't saved: ${refused} Then press Save in Settings → Channel links.`,
|
|
107
|
+
detail: `The channel links weren't saved: ${refused} Then press Save in Settings → YouTube Studio and posting.`,
|
|
86
108
|
});
|
|
87
109
|
writeChannelLinks(deps.db, links);
|
|
88
110
|
return c.json({ links: readChannelLinks(deps.db) });
|
|
@@ -93,7 +115,6 @@ export function settingsRoutes(deps) {
|
|
|
93
115
|
if (!result.ok) {
|
|
94
116
|
return problem(c, {
|
|
95
117
|
status: 400,
|
|
96
|
-
title: titleOf(400),
|
|
97
118
|
detail: `The Notification URL wasn't saved: ${result.message} Fix it in Settings → Notifications → Notification URL, then press Save.`,
|
|
98
119
|
extensions: { fields: [{ field: "url", message: result.message }] },
|
|
99
120
|
});
|
|
@@ -108,7 +129,6 @@ export function settingsRoutes(deps) {
|
|
|
108
129
|
if (invalid !== undefined) {
|
|
109
130
|
return problem(c, {
|
|
110
131
|
status: 400,
|
|
111
|
-
title: titleOf(400),
|
|
112
132
|
detail: `The test notification wasn't sent: ${invalid}`,
|
|
113
133
|
extensions: { fields: [{ field: "url", message: invalid }] },
|
|
114
134
|
});
|
|
@@ -118,7 +138,6 @@ export function settingsRoutes(deps) {
|
|
|
118
138
|
deps.log.write("warn", "notification.test", { detail: sendFailureText(result) });
|
|
119
139
|
return problem(c, {
|
|
120
140
|
status: 502,
|
|
121
|
-
title: titleOf(502),
|
|
122
141
|
detail: `The test notification wasn't delivered: ${sendFailureText(result)}. Check the address in Settings → Notifications → Notification URL (for ntfy, https://ntfy.sh/your-topic), then press Send test notification again.`,
|
|
123
142
|
});
|
|
124
143
|
}
|
|
@@ -147,12 +166,10 @@ export function settingsRoutes(deps) {
|
|
|
147
166
|
return result.reason === "duplicate-voice-id"
|
|
148
167
|
? problem(c, {
|
|
149
168
|
status: 409,
|
|
150
|
-
title: titleOf(409),
|
|
151
169
|
detail: "This voice ID is already saved for this provider. Use the existing voice, or enter a different voice ID.",
|
|
152
170
|
})
|
|
153
171
|
: problem(c, {
|
|
154
172
|
status: 400,
|
|
155
|
-
title: titleOf(400),
|
|
156
173
|
detail: "This voice cannot be added yet. Fix the highlighted field and try again.",
|
|
157
174
|
extensions: { fields: [fieldOf(result.reason)] },
|
|
158
175
|
});
|
|
@@ -166,12 +183,10 @@ export function settingsRoutes(deps) {
|
|
|
166
183
|
return result.reason === "missing"
|
|
167
184
|
? problem(c, {
|
|
168
185
|
status: 404,
|
|
169
|
-
title: titleOf(404),
|
|
170
186
|
detail: "This voice no longer exists; it may have been deleted. Reload Settings → Voices to see your voices.",
|
|
171
187
|
})
|
|
172
188
|
: problem(c, {
|
|
173
189
|
status: 400,
|
|
174
|
-
title: titleOf(400),
|
|
175
190
|
detail: "The languages weren't saved. Fix the highlighted field and try again.",
|
|
176
191
|
extensions: { fields: [fieldOf("unknown-language")] },
|
|
177
192
|
});
|
|
@@ -183,7 +198,6 @@ export function settingsRoutes(deps) {
|
|
|
183
198
|
? c.body(null, 204)
|
|
184
199
|
: problem(c, {
|
|
185
200
|
status: 404,
|
|
186
|
-
title: titleOf(404),
|
|
187
201
|
detail: "This voice no longer exists; it may have been deleted. Reload Settings → Voices to see your voices.",
|
|
188
202
|
});
|
|
189
203
|
})
|
|
@@ -191,7 +205,6 @@ export function settingsRoutes(deps) {
|
|
|
191
205
|
if (!removeVoice(voiceDeps, c.req.valid("param").id).ok) {
|
|
192
206
|
return problem(c, {
|
|
193
207
|
status: 404,
|
|
194
|
-
title: titleOf(404),
|
|
195
208
|
detail: "This voice no longer exists; it may have been deleted already. Reload the page to see your voices.",
|
|
196
209
|
});
|
|
197
210
|
}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import { zValidator } from "@hono/zod-validator";
|
|
2
|
+
import { Hono } from "hono";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { projectById } from "../../slices/admission/repo.js";
|
|
5
|
+
import { webhookBody } from "../../slices/notifications/address.js";
|
|
6
|
+
import { sendFailureText } from "../../slices/notifications/send.js";
|
|
7
|
+
import { readNotificationUrl } from "../../slices/notifications/settings.js";
|
|
8
|
+
import { setupSectionNames } from "../../slices/project-templates/setup-sections.js";
|
|
9
|
+
import { waitsForRelease } from "../../slices/social/due.js";
|
|
10
|
+
import { postingExtension, writeExtensionSites } from "../../slices/social/extension.js";
|
|
11
|
+
import { platformLabels } from "../../slices/social/model.js";
|
|
12
|
+
import { postReadyNotice } from "../../slices/social/notice.js";
|
|
13
|
+
import { packFor, postPacks } from "../../slices/social/pack.js";
|
|
14
|
+
import { readPost, writeChoice, writeReddit, writeTextEdit } from "../../slices/social/repo.js";
|
|
15
|
+
import { choiceBody, linkBody, platformSchema, redditBody, resultBody, textBody, } from "../../slices/social/schema.js";
|
|
16
|
+
import { serveMedia } from "../../slices/social/serve.js";
|
|
17
|
+
import { duePosts, fillPost, linksToFind, nextPost, notPostingSentence, reportPost, setPostLink, } from "../../slices/social/service.js";
|
|
18
|
+
import { fileResponse } from "./byte-range.js";
|
|
19
|
+
import { extensionAuth } from "./extension-auth.js";
|
|
20
|
+
import { idString } from "./ids.js";
|
|
21
|
+
import { onInvalid, problem } from "./problem.js";
|
|
22
|
+
import { fromSlopifyPage } from "./same-page.js";
|
|
23
|
+
const projectParam = z.object({ projectId: idString });
|
|
24
|
+
const postParam = projectParam.extend({ platform: platformSchema });
|
|
25
|
+
const fileParam = projectParam.extend({
|
|
26
|
+
file: z
|
|
27
|
+
.string()
|
|
28
|
+
.min(1)
|
|
29
|
+
.max(200)
|
|
30
|
+
.regex(/^[A-Za-z0-9_-]+$/),
|
|
31
|
+
});
|
|
32
|
+
const nextQuery = z.object({
|
|
33
|
+
platform: platformSchema,
|
|
34
|
+
projectId: z.string().min(1).max(200).optional(),
|
|
35
|
+
});
|
|
36
|
+
const dueQuery = z.object({ hours: z.coerce.number().int().min(1).max(48).optional() });
|
|
37
|
+
const sitesBody = z.object({ sites: z.array(platformSchema).max(6) });
|
|
38
|
+
export function socialRoutes(deps) {
|
|
39
|
+
const { allowOrigin, allowed, refused } = extensionAuth(deps.db);
|
|
40
|
+
const media = {
|
|
41
|
+
db: deps.db,
|
|
42
|
+
paths: deps.paths,
|
|
43
|
+
reshaper: deps.social?.reshaper,
|
|
44
|
+
...(deps.designs === undefined
|
|
45
|
+
? {}
|
|
46
|
+
: {
|
|
47
|
+
renderDesign: async (designId, format, signal) => {
|
|
48
|
+
const drawn = await deps.designs?.renderer.render(designId, format, "png", signal);
|
|
49
|
+
return drawn?.ok === true
|
|
50
|
+
? { ok: true, path: drawn.value.path }
|
|
51
|
+
: {
|
|
52
|
+
ok: false,
|
|
53
|
+
sentence: drawn?.message ??
|
|
54
|
+
"The design couldn't be drawn. Open it in Designs to check it, then close Copy for YouTube Studio and open it again.",
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
}),
|
|
58
|
+
};
|
|
59
|
+
const samePage = (c) => fromSlopifyPage(c, deps.remoteAccess?.remoteHost)
|
|
60
|
+
? undefined
|
|
61
|
+
: problem(c, {
|
|
62
|
+
status: 403,
|
|
63
|
+
detail: "For your safety, a project's posts can only be changed from the Slopify page itself. Open Slopify and try again there.",
|
|
64
|
+
});
|
|
65
|
+
const answer = (c, projectId) => {
|
|
66
|
+
const result = postPacks(deps, projectId, deps.clock.now());
|
|
67
|
+
if (!result.ok)
|
|
68
|
+
return unknownProject(c);
|
|
69
|
+
const body = {
|
|
70
|
+
...result.packs,
|
|
71
|
+
extension: postingExtension(deps.db),
|
|
72
|
+
};
|
|
73
|
+
return c.json(body);
|
|
74
|
+
};
|
|
75
|
+
const refusal = (c, refused) => problem(c, { status: refused.status, detail: refused.sentence });
|
|
76
|
+
const posting = (c, projectId, platform) => packFor(deps, projectId, platform, deps.clock.now()) === undefined
|
|
77
|
+
? problem(c, {
|
|
78
|
+
status: 404,
|
|
79
|
+
detail: notPostingSentence(platform),
|
|
80
|
+
})
|
|
81
|
+
: undefined;
|
|
82
|
+
// A post filled at its release time tells the Notification URL, when one is set. Never
|
|
83
|
+
// awaited: the extension's report doesn't wait on it.
|
|
84
|
+
const notify = (text, projectId) => {
|
|
85
|
+
const url = readNotificationUrl(deps.db);
|
|
86
|
+
const send = deps.sendNotification;
|
|
87
|
+
if (url === null || send === undefined)
|
|
88
|
+
return;
|
|
89
|
+
void send(url, webhookBody(text)).then((sent) => {
|
|
90
|
+
if (!sent.ok)
|
|
91
|
+
deps.log.write("warn", "social.notify", { projectId, detail: sendFailureText(sent) });
|
|
92
|
+
});
|
|
93
|
+
};
|
|
94
|
+
const changed = (projectId) => deps.hub.emit(projectId, { type: "project.updated", projectId });
|
|
95
|
+
return (new Hono()
|
|
96
|
+
.get("/packs/:projectId", zValidator("param", projectParam, onInvalid), (c) => answer(c, c.req.valid("param").projectId))
|
|
97
|
+
// The project's own pick for one site: other settings, not posting there ({off: true}),
|
|
98
|
+
// or back to the template's (null).
|
|
99
|
+
.put("/packs/:projectId/:platform/choice", zValidator("param", postParam, onInvalid), zValidator("json", choiceBody, onInvalid), (c) => {
|
|
100
|
+
const denied = samePage(c);
|
|
101
|
+
if (denied !== undefined)
|
|
102
|
+
return denied;
|
|
103
|
+
const { projectId, platform } = c.req.valid("param");
|
|
104
|
+
if (postPacks(deps, projectId, deps.clock.now()).ok === false)
|
|
105
|
+
return unknownProject(c);
|
|
106
|
+
const { choice } = c.req.valid("json");
|
|
107
|
+
const config = projectById(deps.db, projectId)?.config.social?.[platform];
|
|
108
|
+
if (choice !== null &&
|
|
109
|
+
!("off" in choice) &&
|
|
110
|
+
choice.text === "prompt" &&
|
|
111
|
+
config?.text !== "prompt")
|
|
112
|
+
return problem(c, {
|
|
113
|
+
status: 409,
|
|
114
|
+
detail: `A post written by a post prompt is a paid step, so it is picked in the template, in Edit template → ${setupSectionNames.youtube}, where Save shows its cost. For this project, pick a free text source in Copy for YouTube Studio → ${platformLabels[platform]} → Settings.`,
|
|
115
|
+
});
|
|
116
|
+
writeChoice(deps.db, { projectId, platform }, choice, deps.clock.now());
|
|
117
|
+
changed(projectId);
|
|
118
|
+
return answer(c, projectId);
|
|
119
|
+
})
|
|
120
|
+
.put("/packs/:projectId/:platform/text", zValidator("param", postParam, onInvalid), zValidator("json", textBody, onInvalid), (c) => {
|
|
121
|
+
const denied = samePage(c);
|
|
122
|
+
if (denied !== undefined)
|
|
123
|
+
return denied;
|
|
124
|
+
const { projectId, platform } = c.req.valid("param");
|
|
125
|
+
const missing = posting(c, projectId, platform);
|
|
126
|
+
if (missing !== undefined)
|
|
127
|
+
return missing;
|
|
128
|
+
writeTextEdit(deps.db, { projectId, platform }, c.req.valid("json"), deps.clock.now());
|
|
129
|
+
changed(projectId);
|
|
130
|
+
return answer(c, projectId);
|
|
131
|
+
})
|
|
132
|
+
.delete("/packs/:projectId/:platform/text", zValidator("param", postParam, onInvalid), (c) => {
|
|
133
|
+
const denied = samePage(c);
|
|
134
|
+
if (denied !== undefined)
|
|
135
|
+
return denied;
|
|
136
|
+
const { projectId, platform } = c.req.valid("param");
|
|
137
|
+
const missing = posting(c, projectId, platform);
|
|
138
|
+
if (missing !== undefined)
|
|
139
|
+
return missing;
|
|
140
|
+
writeTextEdit(deps.db, { projectId, platform }, null, deps.clock.now());
|
|
141
|
+
changed(projectId);
|
|
142
|
+
return answer(c, projectId);
|
|
143
|
+
})
|
|
144
|
+
.put("/packs/:projectId/:platform/reddit", zValidator("param", postParam, onInvalid), zValidator("json", redditBody, onInvalid), (c) => {
|
|
145
|
+
const denied = samePage(c);
|
|
146
|
+
if (denied !== undefined)
|
|
147
|
+
return denied;
|
|
148
|
+
const { projectId, platform } = c.req.valid("param");
|
|
149
|
+
if (platform !== "reddit")
|
|
150
|
+
return problem(c, {
|
|
151
|
+
status: 400,
|
|
152
|
+
detail: "Only a Reddit post has a subreddit, title and flair. Open the Reddit post instead.",
|
|
153
|
+
});
|
|
154
|
+
const missing = posting(c, projectId, platform);
|
|
155
|
+
if (missing !== undefined)
|
|
156
|
+
return missing;
|
|
157
|
+
writeReddit(deps.db, { projectId, platform }, c.req.valid("json").reddit, deps.clock.now());
|
|
158
|
+
changed(projectId);
|
|
159
|
+
return answer(c, projectId);
|
|
160
|
+
})
|
|
161
|
+
// The post's link, pasted by the person (posted by hand, or the extension missed it); null
|
|
162
|
+
// removes it, so the post can be filled again.
|
|
163
|
+
.put("/packs/:projectId/:platform/link", zValidator("param", postParam, onInvalid), zValidator("json", linkBody, onInvalid), (c) => {
|
|
164
|
+
const denied = samePage(c);
|
|
165
|
+
if (denied !== undefined)
|
|
166
|
+
return denied;
|
|
167
|
+
const { projectId, platform } = c.req.valid("param");
|
|
168
|
+
const result = setPostLink(deps, projectId, platform, c.req.valid("json").url, deps.clock.now());
|
|
169
|
+
if (!result.ok)
|
|
170
|
+
return refusal(c, result);
|
|
171
|
+
changed(projectId);
|
|
172
|
+
return answer(c, projectId);
|
|
173
|
+
})
|
|
174
|
+
// Fill in <site>: never offered to agents or the assistant (`excluded-routes.ts`).
|
|
175
|
+
.post("/packs/:projectId/:platform/fill", zValidator("param", postParam, onInvalid), (c) => {
|
|
176
|
+
const denied = samePage(c);
|
|
177
|
+
if (denied !== undefined)
|
|
178
|
+
return denied;
|
|
179
|
+
const { projectId, platform } = c.req.valid("param");
|
|
180
|
+
const result = fillPost(deps, projectId, platform, deps.clock.now());
|
|
181
|
+
if (!result.ok)
|
|
182
|
+
return refusal(c, result);
|
|
183
|
+
changed(projectId);
|
|
184
|
+
return c.json({
|
|
185
|
+
pack: result.pack,
|
|
186
|
+
waitsForRelease: result.waitsForRelease,
|
|
187
|
+
url: result.pack.composeUrl,
|
|
188
|
+
});
|
|
189
|
+
})
|
|
190
|
+
.get("/media/:projectId/:file", zValidator("param", fileParam, onInvalid), async (c) => {
|
|
191
|
+
const { projectId, file } = c.req.valid("param");
|
|
192
|
+
return served(c, await serveMedia(media, projectId, file, deps.clock.now(), c.req.raw.signal));
|
|
193
|
+
})
|
|
194
|
+
// The extension's routes.
|
|
195
|
+
.options("/ext/*", (c) => {
|
|
196
|
+
allowOrigin(c, false);
|
|
197
|
+
return c.body(null, 204);
|
|
198
|
+
})
|
|
199
|
+
.get("/ext/next", zValidator("query", nextQuery, onInvalid), (c) => {
|
|
200
|
+
allowOrigin(c, false);
|
|
201
|
+
if (!allowed(c))
|
|
202
|
+
return refused(c);
|
|
203
|
+
const { platform, projectId } = c.req.valid("query");
|
|
204
|
+
const next = nextPost(deps, platform, deps.clock.now(), projectId);
|
|
205
|
+
if (next === undefined)
|
|
206
|
+
return problem(c, {
|
|
207
|
+
status: 404,
|
|
208
|
+
detail: "Nothing is waiting to be filled on this site. In Slopify, open the project, press Copy for YouTube Studio, then Fill in the site's post.",
|
|
209
|
+
});
|
|
210
|
+
changed(next.pack.projectId);
|
|
211
|
+
return c.json(next);
|
|
212
|
+
})
|
|
213
|
+
.get("/ext/packs/:projectId/:platform", zValidator("param", postParam, onInvalid), (c) => {
|
|
214
|
+
allowOrigin(c, false);
|
|
215
|
+
if (!allowed(c))
|
|
216
|
+
return refused(c);
|
|
217
|
+
const { projectId, platform } = c.req.valid("param");
|
|
218
|
+
const pack = packFor(deps, projectId, platform, deps.clock.now());
|
|
219
|
+
return pack === undefined ? unknownPost(c) : c.json(pack);
|
|
220
|
+
})
|
|
221
|
+
.get("/ext/files/:projectId/:file", zValidator("param", fileParam, onInvalid), async (c) => {
|
|
222
|
+
allowOrigin(c, false);
|
|
223
|
+
if (!allowed(c))
|
|
224
|
+
return refused(c);
|
|
225
|
+
const { projectId, file } = c.req.valid("param");
|
|
226
|
+
return served(c, await serveMedia(media, projectId, file, deps.clock.now(), c.req.raw.signal));
|
|
227
|
+
})
|
|
228
|
+
.get("/ext/due", zValidator("query", dueQuery, onInvalid), (c) => {
|
|
229
|
+
allowOrigin(c, false);
|
|
230
|
+
if (!allowed(c))
|
|
231
|
+
return refused(c);
|
|
232
|
+
const hours = c.req.valid("query").hours ?? 24;
|
|
233
|
+
return c.json({ posts: duePosts(deps, deps.clock.now(), hours * 3_600_000) });
|
|
234
|
+
})
|
|
235
|
+
.post("/ext/result", zValidator("json", resultBody, onInvalid), (c) => {
|
|
236
|
+
allowOrigin(c, false);
|
|
237
|
+
if (!allowed(c))
|
|
238
|
+
return refused(c);
|
|
239
|
+
const body = c.req.valid("json");
|
|
240
|
+
const result = reportPost(deps, body, deps.clock.now());
|
|
241
|
+
if (!result.ok)
|
|
242
|
+
return refusal(c, result);
|
|
243
|
+
changed(body.projectId);
|
|
244
|
+
if (body.state === "filled" && waitsForRelease(body.platform) && result.pack !== undefined)
|
|
245
|
+
notify(postReadyNotice(result.pack), body.projectId);
|
|
246
|
+
return c.json({ status: readPost(deps.db, body).state });
|
|
247
|
+
})
|
|
248
|
+
.get("/ext/find-links", (c) => {
|
|
249
|
+
allowOrigin(c, false);
|
|
250
|
+
if (!allowed(c))
|
|
251
|
+
return refused(c);
|
|
252
|
+
return c.json({ posts: linksToFind(deps, deps.clock.now()) });
|
|
253
|
+
})
|
|
254
|
+
// Which sites the extension may post on, as its options last granted them.
|
|
255
|
+
.post("/ext/sites", zValidator("json", sitesBody, onInvalid), (c) => {
|
|
256
|
+
allowOrigin(c, false);
|
|
257
|
+
if (!allowed(c))
|
|
258
|
+
return refused(c);
|
|
259
|
+
writeExtensionSites(deps.db, c.req.valid("json").sites, deps.clock.now());
|
|
260
|
+
return c.json(postingExtension(deps.db));
|
|
261
|
+
}));
|
|
262
|
+
}
|
|
263
|
+
function served(c, result) {
|
|
264
|
+
if (!result.ok)
|
|
265
|
+
return problem(c, { status: result.status, detail: result.sentence });
|
|
266
|
+
return fileResponse(c, result.path, result.bytes, {
|
|
267
|
+
"content-type": result.contentType,
|
|
268
|
+
"content-disposition": `inline; filename="${result.filename.replace(/["\\]/g, "")}"`,
|
|
269
|
+
"x-content-type-options": "nosniff",
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
function unknownProject(c) {
|
|
273
|
+
return problem(c, {
|
|
274
|
+
status: 404,
|
|
275
|
+
detail: "This project no longer exists. Go back to Projects and open it again.",
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
function unknownPost(c) {
|
|
279
|
+
return problem(c, {
|
|
280
|
+
status: 404,
|
|
281
|
+
detail: "That project no longer posts to this site. In Slopify, open the project's Copy for YouTube Studio and press Fill again.",
|
|
282
|
+
});
|
|
283
|
+
}
|