@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
|
@@ -5,10 +5,12 @@ import { crc32 } from "node:zlib";
|
|
|
5
5
|
import { strFromU8, strToU8, Unzip, UnzipInflate, zipSync } from "fflate";
|
|
6
6
|
import { z } from "zod";
|
|
7
7
|
import { transact } from "../../kernel/db/tx.js";
|
|
8
|
+
import { isUnreachablePath } from "../../kernel/errors.js";
|
|
8
9
|
import { liveProject } from "../admission/repo.js";
|
|
9
10
|
import { silenceGapSecondsMax } from "../admission/rules.js";
|
|
10
11
|
import { detectSlots } from "../admission/substitute.js";
|
|
11
12
|
import { agentConnectionKey } from "../agents/connection.js";
|
|
13
|
+
import { dailySampleBudgetKey, dailySampleBudgetSchema } from "../designs/budget.js";
|
|
12
14
|
import { fontMaxBytes } from "../fonts/model.js";
|
|
13
15
|
import { readFontMetadata } from "../fonts/sfnt.js";
|
|
14
16
|
import { lintEntry, lintPrompt } from "../library/lint.js";
|
|
@@ -29,6 +31,8 @@ import { postingPlanKey, postingPlanSchema } from "../studio/plan.js";
|
|
|
29
31
|
import { leadHoursMax } from "../studio/plan-model.js";
|
|
30
32
|
import { leadHoursKey } from "../studio/releases.js";
|
|
31
33
|
import { autoCommentKey, storedPlaylistsSchema, storedProjectPlaylistsSchema, studioChannelPlaylistPrefix, studioFillQueuePrefix, studioPairingKey, studioPlaylistKey, studioProjectPlaylistsKey, studioRealFootageKey, } from "../studio/settings.js";
|
|
34
|
+
import { dailyBudgetKey, dailyBudgetMax, samplesUsePlansKey } from "../template-studio/budget.js";
|
|
35
|
+
import { notTemplateSample } from "../template-studio/repo.js";
|
|
32
36
|
import { channelLinksKey } from "../youtube/edits-repo.js";
|
|
33
37
|
import { defaultBackupsDir, projectDir, stagingPath } from "./layout.js";
|
|
34
38
|
import { stageKinds } from "./model.js";
|
|
@@ -299,7 +303,7 @@ function planStagedExport(deps) {
|
|
|
299
303
|
stat = lstatSync(path);
|
|
300
304
|
}
|
|
301
305
|
catch (error) {
|
|
302
|
-
if (
|
|
306
|
+
if (isUnreachablePath(error))
|
|
303
307
|
continue;
|
|
304
308
|
throw error;
|
|
305
309
|
}
|
|
@@ -358,7 +362,7 @@ const storedAppearance = z.enum(appearances);
|
|
|
358
362
|
const storedLoudness = z
|
|
359
363
|
.object({ enabled: z.boolean(), videoLufs: z.number(), audioFilesLufs: z.number() })
|
|
360
364
|
.strict();
|
|
361
|
-
// Settings →
|
|
365
|
+
// Settings → YouTube Studio and posting, and the providers a fresh Play form starts with.
|
|
362
366
|
// The AI use marks: ticked voices, Image prompts and projects, by id.
|
|
363
367
|
const storedIds = z.array(z.string().max(200)).max(100_000);
|
|
364
368
|
// The channel page's "Keep only titles containing…" text, one per channel.
|
|
@@ -410,6 +414,12 @@ function portableSettings(settings) {
|
|
|
410
414
|
storedProjectPlaylistsSchema.parse(parsed);
|
|
411
415
|
else if (key === channelLinksKey)
|
|
412
416
|
storedChannelLinks.parse(parsed);
|
|
417
|
+
else if (key === dailySampleBudgetKey)
|
|
418
|
+
dailySampleBudgetSchema.parse(parsed);
|
|
419
|
+
else if (key === dailyBudgetKey)
|
|
420
|
+
z.number().finite().min(0).max(dailyBudgetMax).parse(parsed);
|
|
421
|
+
else if (key === samplesUsePlansKey)
|
|
422
|
+
z.boolean().parse(parsed);
|
|
413
423
|
else if (key === realPersonVoicesKey ||
|
|
414
424
|
key === photorealisticPromptsKey ||
|
|
415
425
|
key === studioRealFootageKey)
|
|
@@ -462,7 +472,7 @@ function validateLibraryRows(manifest) {
|
|
|
462
472
|
const promptIds = new Set();
|
|
463
473
|
const promptNames = new Set();
|
|
464
474
|
for (const row of manifest.prompts) {
|
|
465
|
-
if (row.name !== row.name.trim() || lintPrompt(row).length > 0)
|
|
475
|
+
if (row.name !== row.name.trim() || lintPrompt(row, detectSlots(row.body).names).length > 0)
|
|
466
476
|
throw new Error("The backup contains an invalid prompt.");
|
|
467
477
|
uniquePortableRow(promptIds, row.id, "prompt id");
|
|
468
478
|
uniquePortableRow(promptNames, `${row.kind}\0${row.name.toLowerCase()}`, "prompt name");
|
|
@@ -471,7 +481,7 @@ function validateLibraryRows(manifest) {
|
|
|
471
481
|
const entryIds = new Set();
|
|
472
482
|
const entryNames = new Set();
|
|
473
483
|
for (const row of manifest.entries) {
|
|
474
|
-
if (row.name !== row.name.trim() || lintEntry(row).length > 0)
|
|
484
|
+
if (row.name !== row.name.trim() || lintEntry(row, detectSlots(row.body).names).length > 0)
|
|
475
485
|
throw new Error("The backup contains an invalid entry.");
|
|
476
486
|
uniquePortableRow(entryIds, row.id, "entry id");
|
|
477
487
|
uniquePortableRow(entryNames, `${row.category}\0${row.name.toLowerCase()}`, "entry name");
|
|
@@ -825,7 +835,7 @@ function installedUploadedFonts(paths) {
|
|
|
825
835
|
names = readdirSync(root);
|
|
826
836
|
}
|
|
827
837
|
catch (error) {
|
|
828
|
-
if (
|
|
838
|
+
if (isUnreachablePath(error))
|
|
829
839
|
return [];
|
|
830
840
|
throw error;
|
|
831
841
|
}
|
|
@@ -884,7 +894,7 @@ function readStoredFont(path) {
|
|
|
884
894
|
return readFileSync(descriptor);
|
|
885
895
|
}
|
|
886
896
|
catch (error) {
|
|
887
|
-
if (
|
|
897
|
+
if (isUnreachablePath(error))
|
|
888
898
|
return undefined;
|
|
889
899
|
throw error;
|
|
890
900
|
}
|
|
@@ -903,11 +913,6 @@ function writeImportedFile(deps, path, content, created) {
|
|
|
903
913
|
closeSync(descriptor);
|
|
904
914
|
}
|
|
905
915
|
}
|
|
906
|
-
function unavailable(error) {
|
|
907
|
-
return (error instanceof Error &&
|
|
908
|
-
"code" in error &&
|
|
909
|
-
["ENOENT", "ENOTDIR", "EACCES", "EPERM", "ELOOP"].includes(String(error.code)));
|
|
910
|
-
}
|
|
911
916
|
function planStagedImport(deps, archive, files) {
|
|
912
917
|
const sourceIds = new Set();
|
|
913
918
|
const archivePaths = new Set();
|
|
@@ -963,7 +968,7 @@ export function storageBytes(paths) {
|
|
|
963
968
|
export function storageUsage(deps) {
|
|
964
969
|
const totals = storageBytes(deps.paths);
|
|
965
970
|
const byProject = deps.db
|
|
966
|
-
.prepare(`SELECT id,title FROM projects WHERE ${liveProject()} ORDER BY created_at DESC, id DESC`)
|
|
971
|
+
.prepare(`SELECT id,title FROM projects WHERE ${liveProject()} AND ${notTemplateSample()} ORDER BY created_at DESC, id DESC`)
|
|
967
972
|
.all()
|
|
968
973
|
.flatMap((row) => {
|
|
969
974
|
if (typeof row.id !== "string" || typeof row.title !== "string")
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { socialPlatforms } from "../social/model.js";
|
|
2
3
|
import { outputRoles, stagedFileStates, stageKinds } from "./model.js";
|
|
3
4
|
export const metaSchema = z.object({
|
|
4
5
|
segment: z.enum(["body", "intro", "outro"]).optional(),
|
|
@@ -26,6 +27,7 @@ export const metaSchema = z.object({
|
|
|
26
27
|
})
|
|
27
28
|
.optional(),
|
|
28
29
|
master: z.object({ target: z.number(), integrated: z.number(), truePeak: z.number() }).optional(),
|
|
30
|
+
platform: z.enum(socialPlatforms).optional(),
|
|
29
31
|
});
|
|
30
32
|
export const outputSchema = z.object({
|
|
31
33
|
id: z.string(),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { rmSync } from "node:fs";
|
|
2
|
+
import { errorText } from "../../kernel/errors.js";
|
|
2
3
|
import { stagingPath } from "./layout.js";
|
|
3
4
|
import { deleteStagedFile, stagedFileById } from "./repo.js";
|
|
4
5
|
export function stagedFileReferenced(db, id) {
|
|
@@ -18,7 +19,7 @@ export function releaseStagedFile(deps, id) {
|
|
|
18
19
|
}
|
|
19
20
|
catch (error) {
|
|
20
21
|
deps.log.write("warn", "staging.release", {
|
|
21
|
-
detail: `${id}: ${
|
|
22
|
+
detail: `${id}: ${errorText(error)}`,
|
|
22
23
|
});
|
|
23
24
|
return;
|
|
24
25
|
}
|
|
@@ -3,10 +3,10 @@ import { basename, dirname } from "node:path";
|
|
|
3
3
|
import { pipeline } from "node:stream/promises";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { transact } from "../../kernel/db/tx.js";
|
|
6
|
+
import { errorText } from "../../kernel/errors.js";
|
|
6
7
|
import { extensionOf, outputFileName, outputPath, stagingPath } from "./layout.js";
|
|
7
8
|
import { deleteStagedFile, insertOutput, insertStagedFile, markStagedFileCopied, stagedFileById, } from "./repo.js";
|
|
8
9
|
import { releaseStagedFile, stagedFileReferenced } from "./staging-refs.js";
|
|
9
|
-
export { prepareStagedFile, prepareText } from "./prepare.js";
|
|
10
10
|
// The upload's own name is data, never a path component: the file is stored under a
|
|
11
11
|
// generated id, and anything with a separator, a traversal, or a control character is
|
|
12
12
|
// refused rather than sanitised, so nothing silently lands under a name nobody chose.
|
|
@@ -53,7 +53,7 @@ export async function stageUpload(deps, input) {
|
|
|
53
53
|
deps.emit(event);
|
|
54
54
|
}
|
|
55
55
|
catch (error) {
|
|
56
|
-
deps.log.write("warn", "staging.emit", { detail:
|
|
56
|
+
deps.log.write("warn", "staging.emit", { detail: errorText(error) });
|
|
57
57
|
}
|
|
58
58
|
};
|
|
59
59
|
const progress = (state) => {
|
|
@@ -92,10 +92,10 @@ export async function stageUpload(deps, input) {
|
|
|
92
92
|
stagedFileId: id,
|
|
93
93
|
stageKind: input.stageKind,
|
|
94
94
|
originalFilename,
|
|
95
|
-
detail:
|
|
95
|
+
detail: errorText(error),
|
|
96
96
|
});
|
|
97
97
|
deps.log.write("error", "staging.failed", {
|
|
98
|
-
detail: `${id} ${originalFilename}: ${
|
|
98
|
+
detail: `${id} ${originalFilename}: ${errorText(error)}`,
|
|
99
99
|
});
|
|
100
100
|
throw error;
|
|
101
101
|
}
|
|
@@ -152,7 +152,7 @@ export function attachStagedFile(deps, input) {
|
|
|
152
152
|
catch (error) {
|
|
153
153
|
deps.log.write("error", "storage.attach", {
|
|
154
154
|
projectId: input.projectId,
|
|
155
|
-
detail: `${staged.id} → ${name}: ${
|
|
155
|
+
detail: `${staged.id} → ${name}: ${errorText(error)}`,
|
|
156
156
|
});
|
|
157
157
|
throw error;
|
|
158
158
|
}
|
|
@@ -210,7 +210,7 @@ function discard(deps, id, target, event) {
|
|
|
210
210
|
}
|
|
211
211
|
catch (error) {
|
|
212
212
|
// The row stays `copying`, so the next boot's reconcile removes the file instead.
|
|
213
|
-
deps.log.write("warn", event, { detail: `${id}: ${
|
|
213
|
+
deps.log.write("warn", event, { detail: `${id}: ${errorText(error)}` });
|
|
214
214
|
return;
|
|
215
215
|
}
|
|
216
216
|
deleteStagedFile(deps.db, id);
|
|
@@ -222,6 +222,3 @@ function closed(sink) {
|
|
|
222
222
|
sink.once("close", resolve);
|
|
223
223
|
});
|
|
224
224
|
}
|
|
225
|
-
function messageOf(error) {
|
|
226
|
-
return error instanceof Error ? error.message : String(error);
|
|
227
|
-
}
|
|
@@ -2,6 +2,8 @@ import { existsSync, readdirSync, rmdirSync, statSync, unlinkSync } from "node:f
|
|
|
2
2
|
import { join, relative, sep } from "node:path";
|
|
3
3
|
import { derive } from "../../kernel/runner/graph.js";
|
|
4
4
|
import { liveProject, projectExists, projectPaused, stagesOf } from "../admission/repo.js";
|
|
5
|
+
import { bundledSampleProjects } from "../onboarding/state.js";
|
|
6
|
+
import { notTemplateSample } from "../template-studio/repo.js";
|
|
5
7
|
import { projectSettled } from "./arrange.js";
|
|
6
8
|
import { backupsFolderName, outputPath, projectDir } from "./layout.js";
|
|
7
9
|
import { pieceFile } from "./reconcile.js";
|
|
@@ -10,7 +12,9 @@ import { pieceFile } from "./reconcile.js";
|
|
|
10
12
|
// video and shorts show included: making them again costs money and never gives the same ones. Two
|
|
11
13
|
// rules keep it safe. A file the user supplied is never removed, because nothing could make
|
|
12
14
|
// it again. And only files a project record names are removed: anything else in the folder
|
|
13
|
-
// is reconcile's business (`reconcile.ts`), which leaves the Backups folder alone.
|
|
15
|
+
// is reconcile's business (`reconcile.ts`), which leaves the Backups folder alone. A bundled
|
|
16
|
+
// sample is read-only and keeps every file: nothing on it counts as removable, and no clean-up
|
|
17
|
+
// touches it.
|
|
14
18
|
// What a finished project is for: the files that get uploaded or read, from every revision,
|
|
15
19
|
// so an older video in History stays too.
|
|
16
20
|
export const outputRoles = [
|
|
@@ -32,17 +36,20 @@ export const outputRoles = [
|
|
|
32
36
|
"image",
|
|
33
37
|
"animated_image",
|
|
34
38
|
"short_image",
|
|
39
|
+
"social_post",
|
|
35
40
|
];
|
|
36
41
|
export function projectStorage(deps, projectId) {
|
|
37
42
|
const files = filesOf(deps.paths, projectId);
|
|
38
|
-
const kept = keptPaths(deps.db, projectId);
|
|
39
43
|
const outputs = outputPaths(deps.db, projectId);
|
|
40
|
-
const
|
|
44
|
+
const sample = bundledSampleProjects(deps.db).has(projectId);
|
|
45
|
+
const removable = sample
|
|
46
|
+
? new Set()
|
|
47
|
+
: removablePaths(deps.db, projectId, keptPaths(deps.db, projectId));
|
|
41
48
|
let outputsBytes = 0;
|
|
42
49
|
let workingBytes = 0;
|
|
43
50
|
let removableFiles = 0;
|
|
44
51
|
let removableBytes = 0;
|
|
45
|
-
const history = historyPaths(deps, projectId);
|
|
52
|
+
const history = sample ? new Set() : historyPaths(deps, projectId);
|
|
46
53
|
let historyFiles = 0;
|
|
47
54
|
let historyBytes = 0;
|
|
48
55
|
for (const [path, bytes] of files) {
|
|
@@ -105,15 +112,16 @@ export function deleteOldVersions(deps, projectId) {
|
|
|
105
112
|
removeEmptyUnder(join(projectDir(deps.paths, projectId), "History"));
|
|
106
113
|
return { ok: true, files: count, bytesFreed };
|
|
107
114
|
}
|
|
108
|
-
// Every project's old versions, skipping any that runs or waits to run.
|
|
115
|
+
// Every project's old versions, skipping any that runs or waits to run. A template studio
|
|
116
|
+
// sample keeps its files (its bake-offs and resamples reuse them), and is no project to count;
|
|
117
|
+
// nor is a bundled sample, which is read-only.
|
|
109
118
|
export function deleteAllOldVersions(deps) {
|
|
110
119
|
const busyIds = [];
|
|
111
120
|
let projects = 0;
|
|
112
121
|
let files = 0;
|
|
113
122
|
let bytesFreed = 0;
|
|
114
123
|
let busy = 0;
|
|
115
|
-
for (const
|
|
116
|
-
const id = String(row.id);
|
|
124
|
+
for (const id of cleanable(deps.db)) {
|
|
117
125
|
if (placedFiles(deps.paths, id).size === 0)
|
|
118
126
|
continue;
|
|
119
127
|
const result = deleteOldVersions(deps, id);
|
|
@@ -138,8 +146,7 @@ export function cleanUpAll(deps) {
|
|
|
138
146
|
let files = old.files;
|
|
139
147
|
let bytesFreed = old.bytesFreed;
|
|
140
148
|
let skipped = old.busy;
|
|
141
|
-
for (const
|
|
142
|
-
const id = String(row.id);
|
|
149
|
+
for (const id of cleanable(deps.db)) {
|
|
143
150
|
const result = keepOutputsOnly(deps, id);
|
|
144
151
|
if (result.ok) {
|
|
145
152
|
files += result.files;
|
|
@@ -150,6 +157,16 @@ export function cleanUpAll(deps) {
|
|
|
150
157
|
}
|
|
151
158
|
return { files, bytesFreed, skipped };
|
|
152
159
|
}
|
|
160
|
+
// The projects the clean-ups for every project go through: neither a template studio sample nor
|
|
161
|
+
// a bundled one.
|
|
162
|
+
function cleanable(db) {
|
|
163
|
+
const samples = bundledSampleProjects(db);
|
|
164
|
+
return db
|
|
165
|
+
.prepare(`SELECT id FROM projects WHERE ${liveProject()} AND ${notTemplateSample()}`)
|
|
166
|
+
.all()
|
|
167
|
+
.map((row) => String(row.id))
|
|
168
|
+
.filter((id) => !samples.has(id));
|
|
169
|
+
}
|
|
153
170
|
// The folders under `root` left empty, deepest first, and `root` itself if it is empty then.
|
|
154
171
|
function removeEmptyUnder(root) {
|
|
155
172
|
if (!existsSync(root))
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { listProjects } from "../admission/repo.js";
|
|
2
|
+
import { uploadedProjects } from "../uploads/repo.js";
|
|
2
3
|
import { uploadPack } from "./pack.js";
|
|
3
4
|
import { readPlan } from "./plan.js";
|
|
4
5
|
import { seriesOf } from "./plan-model.js";
|
|
5
|
-
import { allReleases, lineSlots, readLeadHours, releasesOf } from "./releases.js";
|
|
6
|
+
import { allReleases, lineShortTimes, lineSlots, lineZone, readLeadHours, releasesOf, } from "./releases.js";
|
|
6
7
|
import { videoOf } from "./videos.js";
|
|
7
8
|
const hourMs = 60 * 60 * 1000;
|
|
8
9
|
export function releaseCalendar(deps, options) {
|
|
@@ -16,6 +17,7 @@ export function releaseCalendar(deps, options) {
|
|
|
16
17
|
return at >= from.getTime() && at < until.getTime();
|
|
17
18
|
};
|
|
18
19
|
const projects = new Map(listProjects(deps.db).map((project) => [project.id, project]));
|
|
20
|
+
const uploaded = uploadedProjects(deps.db);
|
|
19
21
|
const longs = allReleases(deps.db).filter((release) => release.short === 0 && projects.has(release.projectId) && inWindow(release.at));
|
|
20
22
|
// A schedule's line shows the schedule's name; a stored line its series.
|
|
21
23
|
const seriesOfLine = (line) => {
|
|
@@ -30,6 +32,7 @@ export function releaseCalendar(deps, options) {
|
|
|
30
32
|
return {
|
|
31
33
|
at: long.at,
|
|
32
34
|
line: long.line,
|
|
35
|
+
timeZone: lineZone(plan, long.line),
|
|
33
36
|
series: project === undefined ? "" : seriesOf(project.config),
|
|
34
37
|
project: { id: long.projectId, title: project?.title ?? "" },
|
|
35
38
|
items: items.map((item) => {
|
|
@@ -38,7 +41,7 @@ export function releaseCalendar(deps, options) {
|
|
|
38
41
|
const release = at === undefined || at === "" ? null : at;
|
|
39
42
|
const uploadBy = release === null ? null : new Date(Date.parse(release) - leadMs);
|
|
40
43
|
const video = videoOf(deps.db, long.projectId, item.short ?? null);
|
|
41
|
-
const state = video?.uploadState === "done"
|
|
44
|
+
const state = video?.uploadState === "done" || (short === 0 && uploaded.has(long.projectId))
|
|
42
45
|
? "scheduled"
|
|
43
46
|
: video?.uploadState === "filled"
|
|
44
47
|
? "filled"
|
|
@@ -66,6 +69,8 @@ export function releaseCalendar(deps, options) {
|
|
|
66
69
|
entries.push({
|
|
67
70
|
at: slot.longAt,
|
|
68
71
|
line: slot.row,
|
|
72
|
+
timeZone: lineZone(plan, slot.row),
|
|
73
|
+
shortsAt: lineShortTimes(plan, slot.row, slot.longAt),
|
|
69
74
|
series: seriesOfLine(slot.row),
|
|
70
75
|
project: null,
|
|
71
76
|
items: [],
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// So the automatic answer is Yes only when one of the three cases applies, and No otherwise:
|
|
17
17
|
// 1. a saved voice marked "Imitates a real person" (Settings → Voices) narrates it: an AI
|
|
18
18
|
// voice cloned from, or made to sound like, a real person, so that person seems to say it;
|
|
19
|
-
// 2. uploaded clips the person marked as real footage (the project's
|
|
19
|
+
// 2. uploaded clips the person marked as real footage (the project's Copy for YouTube Studio) get the
|
|
20
20
|
// Look's atmosphere overlay (embers, dust or fog), which adds to a real scene what wasn't
|
|
21
21
|
// there. A colour grade, vignette or grain alone is one of YouTube's minor edits;
|
|
22
22
|
// 3. an AI image model draws its pictures from an Image prompt marked "Draws photorealistic
|
|
@@ -49,18 +49,18 @@ export function aiDisclosureOf(input) {
|
|
|
49
49
|
if (input.realFootage && input.footageOverlay !== undefined)
|
|
50
50
|
reasons.push(`the Look's ${input.footageOverlay} overlay is laid over uploaded clips marked as real footage (YouTube's case 2: "${aiUseCases[1]}")`);
|
|
51
51
|
if (input.photorealistic)
|
|
52
|
-
reasons.push(`an AI image model draws its pictures from an Image prompt marked photorealistic in
|
|
52
|
+
reasons.push(`an AI image model draws its pictures from an Image prompt marked photorealistic in Prompts (YouTube's case 3: "${aiUseCases[2]}")`);
|
|
53
53
|
if (reasons.length > 0)
|
|
54
54
|
return {
|
|
55
55
|
altered: true,
|
|
56
56
|
why: `Yes because ${list(reasons)}. Selecting Yes adds YouTube's AI label.`,
|
|
57
57
|
};
|
|
58
58
|
const footage = input.realFootage && input.kind === "video"
|
|
59
|
-
? " The uploaded real footage only gets
|
|
59
|
+
? " The uploaded real footage only gets color, vignette or grain, which YouTube counts as minor edits."
|
|
60
60
|
: "";
|
|
61
61
|
return {
|
|
62
62
|
altered: false,
|
|
63
|
-
why: `No because none of YouTube's three AI use cases applies: no narrating voice is marked as imitating a real person (Settings → Voices), no real footage is altered, and no AI pictures come from an Image prompt marked photorealistic (
|
|
63
|
+
why: `No because none of YouTube's three AI use cases applies: no narrating voice is marked as imitating a real person (Settings → Voices), no real footage is altered, and no AI pictures come from an Image prompt marked photorealistic (Prompts). Stylized AI pictures and an AI narrator that doesn't pose as a real person don't need the label.${footage}`,
|
|
64
64
|
};
|
|
65
65
|
}
|
|
66
66
|
function quotedList(names) {
|
|
@@ -13,7 +13,7 @@ export const studioAudience = "not_made_for_kids";
|
|
|
13
13
|
export const studioUploadUrl = "https://www.youtube.com/upload";
|
|
14
14
|
// What a playlist takes: every upload, only long videos, or only shorts.
|
|
15
15
|
export const playlistUses = ["all", "long", "shorts"];
|
|
16
|
-
// The steps in Studio's order, as
|
|
16
|
+
// The steps in Studio's order, as Copy for YouTube Studio lists them.
|
|
17
17
|
export const studioSteps = [
|
|
18
18
|
"video",
|
|
19
19
|
"title",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readFileSync, statSync } from "node:fs";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import { editSettings } from "../../kernel/ports/settings-places.js";
|
|
3
4
|
import { thumbnailCountOf } from "../admission/model.js";
|
|
4
5
|
import { projectById } from "../admission/repo.js";
|
|
5
6
|
import { channelById, projectChannelId } from "../channels/repo.js";
|
|
@@ -55,7 +56,7 @@ export function uploadPack(deps, projectId) {
|
|
|
55
56
|
? undefined
|
|
56
57
|
: readFileSync(outputPath(deps.paths, projectId, output.path), "utf8").trim();
|
|
57
58
|
const channelId = projectChannelId(deps.db, projectId);
|
|
58
|
-
// The channel's playlists (Settings → YouTube Studio), ticked as this project chose.
|
|
59
|
+
// The channel's playlists (Settings → YouTube Studio and posting), ticked as this project chose.
|
|
59
60
|
const playlistChoices = projectPlaylists(deps.db, projectId, channelId);
|
|
60
61
|
// The long video goes into the chosen playlists that take long videos, each short into those
|
|
61
62
|
// that take shorts.
|
|
@@ -88,7 +89,7 @@ export function uploadPack(deps, projectId) {
|
|
|
88
89
|
});
|
|
89
90
|
const video = outputs.find((output) => output.role === "video");
|
|
90
91
|
if (video === undefined)
|
|
91
|
-
missing.push("The video isn't made yet. Let the Video stage finish (or use Continue the run on the project page), then open
|
|
92
|
+
missing.push("The video isn't made yet. Let the Video stage finish (or use Continue the run on the project page), then open Copy for YouTube Studio again.");
|
|
92
93
|
const written = text(outputs.find((output) => output.role === "youtube_description"));
|
|
93
94
|
const writtenTags = text(outputs.find((output) => output.role === "youtube_tags"));
|
|
94
95
|
// As the project page shows it: the user's edits, and channel links filled in.
|
|
@@ -109,7 +110,7 @@ export function uploadPack(deps, projectId) {
|
|
|
109
110
|
if (description === undefined)
|
|
110
111
|
missing.push(config.youtubeDescription === true
|
|
111
112
|
? "The YouTube description isn't written yet. It is written after the video's subtitle timing; wait for the Video stage to finish."
|
|
112
|
-
:
|
|
113
|
+
: `No YouTube description is written for this project. Turn on YouTube description in ${editSettings.extras} to have one written with chapters and tags.`);
|
|
113
114
|
const count = thumbnailCountOf(config);
|
|
114
115
|
// The first thumbnail, then the second and third, and never one left from when the project
|
|
115
116
|
// made three and now makes one.
|
|
@@ -118,12 +119,12 @@ export function uploadPack(deps, projectId) {
|
|
|
118
119
|
.sort((a, b) => (a.meta.index ?? 1) - (b.meta.index ?? 1));
|
|
119
120
|
if (thumbnails.length === 0)
|
|
120
121
|
missing.push(config.sources.thumbnail === "off"
|
|
121
|
-
?
|
|
122
|
-
: "The thumbnail isn't made yet. Let the Thumbnail stage finish, then open
|
|
122
|
+
? `This project makes no thumbnail, so YouTube will pick a frame. Set Thumbnail source in ${editSettings.extras} to make or upload one.`
|
|
123
|
+
: "The thumbnail isn't made yet. Let the Thumbnail stage finish, then open Copy for YouTube Studio again.");
|
|
123
124
|
else if (thumbnails.length < count)
|
|
124
|
-
missing.push(`Only ${String(thumbnails.length)} of the ${String(count)} thumbnails are made.
|
|
125
|
+
missing.push(`Only ${String(thumbnails.length)} of the ${String(count)} thumbnails are made. Make the missing ones in the project's Images section.`);
|
|
125
126
|
if (playlistChoices.length === 0)
|
|
126
|
-
missing.push("No playlist is set for this project's channel, so the playlist step is left to you. Set one in Settings → YouTube Studio → Playlists.");
|
|
127
|
+
missing.push("No playlist is set for this project's channel, so the playlist step is left to you. Set one in Settings → YouTube Studio and posting → Playlists.");
|
|
127
128
|
// The video's titles and thumbnails in the project's order, and the ones the person picked
|
|
128
129
|
// for the upload first (`pick.ts`); the A/B test later tries the rest beside them.
|
|
129
130
|
const ownTitle = project.title.slice(0, studioTitleMax);
|
|
@@ -189,7 +190,7 @@ export function uploadPack(deps, projectId) {
|
|
|
189
190
|
output.meta.sentences?.[1] === clip.last);
|
|
190
191
|
const render = exact ?? mine.at(-1);
|
|
191
192
|
if (render === undefined)
|
|
192
|
-
missing.push(`Short ${String(clip.number)} isn't rendered yet. Let the Video stage finish, then open
|
|
193
|
+
missing.push(`Short ${String(clip.number)} isn't rendered yet. Let the Video stage finish, then open Copy for YouTube Studio again.`);
|
|
193
194
|
items.push({
|
|
194
195
|
kind: "short",
|
|
195
196
|
short: clip.number,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { readJsonSetting, writeSetting } from "../settings/repo.js";
|
|
3
3
|
export const uploadPickSchema = z.object({
|
|
4
4
|
title: z.number().int().min(0).max(9),
|
|
5
5
|
thumbnail: z.number().int().min(0).max(9),
|
|
@@ -7,16 +7,7 @@ export const uploadPickSchema = z.object({
|
|
|
7
7
|
export const uploadPickPrefix = "studio.uploadPick.";
|
|
8
8
|
const keyOf = (projectId) => `${uploadPickPrefix}${projectId}`;
|
|
9
9
|
export function readUploadPick(db, projectId) {
|
|
10
|
-
|
|
11
|
-
if (stored === undefined)
|
|
12
|
-
return { title: 0, thumbnail: 0 };
|
|
13
|
-
try {
|
|
14
|
-
const parsed = uploadPickSchema.safeParse(JSON.parse(stored));
|
|
15
|
-
return parsed.success ? parsed.data : { title: 0, thumbnail: 0 };
|
|
16
|
-
}
|
|
17
|
-
catch {
|
|
18
|
-
return { title: 0, thumbnail: 0 };
|
|
19
|
-
}
|
|
10
|
+
return readJsonSetting(db, keyOf(projectId), uploadPickSchema, { title: 0, thumbnail: 0 });
|
|
20
11
|
}
|
|
21
12
|
export function writeUploadPick(db, projectId, pick) {
|
|
22
13
|
writeSetting(db, keyOf(projectId), JSON.stringify(uploadPickSchema.parse(pick)));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { templateById } from "../project-templates/repo.js";
|
|
2
2
|
import { scheduleRows } from "../schedules/repo.js";
|
|
3
|
-
import {
|
|
3
|
+
import { readJsonSetting, writeSetting } from "../settings/repo.js";
|
|
4
4
|
// The posting plan: a week of long-video slots, each with its shorts. Each schedule sets its
|
|
5
5
|
// own (its release times, one line per day it runs, in its time zone); lines saved in Settings
|
|
6
6
|
// before that are kept in the plan's time zone. A finished project takes the next free slot when its
|
|
@@ -11,7 +11,7 @@ export { postingPlanSchema, weekdays, } from "./plan-model.js";
|
|
|
11
11
|
import { postingPlanSchema, seriesOf } from "./plan-model.js";
|
|
12
12
|
export const postingPlanKey = "studio.postingPlan";
|
|
13
13
|
const key = postingPlanKey;
|
|
14
|
-
// No plan until one is made: nothing is scheduled, and
|
|
14
|
+
// No plan until one is made: nothing is scheduled, and Copy for YouTube Studio says so.
|
|
15
15
|
export function emptyPlan(timeZone) {
|
|
16
16
|
return { timeZone, rows: [] };
|
|
17
17
|
}
|
|
@@ -20,20 +20,11 @@ export function localTimeZone() {
|
|
|
20
20
|
}
|
|
21
21
|
// The plan as stored in Settings: the lines made before schedules had release times.
|
|
22
22
|
export function readStoredPlan(db) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return {
|
|
29
|
-
...parsed.data,
|
|
30
|
-
rows: parsed.data.rows.filter((row) => row.schedule === undefined),
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
catch {
|
|
34
|
-
// A broken setting reads as no plan.
|
|
35
|
-
}
|
|
36
|
-
return emptyPlan(localTimeZone());
|
|
23
|
+
// A broken setting reads as no plan.
|
|
24
|
+
const stored = readJsonSetting(db, key, postingPlanSchema, undefined);
|
|
25
|
+
if (stored === undefined)
|
|
26
|
+
return emptyPlan(localTimeZone());
|
|
27
|
+
return { ...stored, rows: stored.rows.filter((row) => row.schedule === undefined) };
|
|
37
28
|
}
|
|
38
29
|
// The series of a schedule's template, from its project title ("{{Topic}} | Stories" →
|
|
39
30
|
// "Stories"); "" when the template is gone or its title names none.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import {
|
|
3
|
+
import { deleteSetting, readJsonSetting, writeSetting } from "../settings/repo.js";
|
|
4
4
|
import { studioFillQueuePrefix, studioPairing } from "./settings.js";
|
|
5
5
|
// What Fill in YouTube Studio chose, waiting for the extension: one entry per video or short,
|
|
6
6
|
// oldest first. Each new upload dialog in Studio is filled from the first entry, which then
|
|
@@ -22,24 +22,13 @@ function queueKey(db) {
|
|
|
22
22
|
}
|
|
23
23
|
function write(db, key, entries) {
|
|
24
24
|
if (entries.length === 0)
|
|
25
|
-
db
|
|
25
|
+
deleteSetting(db, key);
|
|
26
26
|
else
|
|
27
27
|
writeSetting(db, key, JSON.stringify(entries));
|
|
28
28
|
}
|
|
29
29
|
// The waiting entries, oldest first, without the stale ones.
|
|
30
30
|
export function readFillQueue(db, now) {
|
|
31
|
-
const
|
|
32
|
-
if (stored === undefined)
|
|
33
|
-
return [];
|
|
34
|
-
let raw;
|
|
35
|
-
try {
|
|
36
|
-
raw = JSON.parse(stored);
|
|
37
|
-
}
|
|
38
|
-
catch {
|
|
39
|
-
return [];
|
|
40
|
-
}
|
|
41
|
-
if (!Array.isArray(raw))
|
|
42
|
-
return [];
|
|
31
|
+
const raw = readJsonSetting(db, queueKey(db), z.array(z.unknown()), []);
|
|
43
32
|
return raw.flatMap((one) => {
|
|
44
33
|
const parsed = entrySchema.safeParse(one);
|
|
45
34
|
if (!parsed.success)
|
|
@@ -63,6 +63,32 @@ export function lineSlots(plan, from, until) {
|
|
|
63
63
|
})))
|
|
64
64
|
.toSorted((left, right) => Date.parse(left.longAt) - Date.parse(right.longAt));
|
|
65
65
|
}
|
|
66
|
+
// The short times a line gives the video released at `longAt`: each short the line's first
|
|
67
|
+
// time after the one before it, the way a project's shorts are planned (other releases aside).
|
|
68
|
+
export function lineShortTimes(plan, row, longAt, shorts) {
|
|
69
|
+
const line = plan.rows.find((one) => one.name === row);
|
|
70
|
+
if (line === undefined)
|
|
71
|
+
return [];
|
|
72
|
+
const start = Date.parse(longAt);
|
|
73
|
+
const times = line.shorts
|
|
74
|
+
.flatMap((slot) => zonedTimes(zoneOf(plan, line), slot, new Date(start), new Date(start + 5 * 7 * 24 * hourMs)))
|
|
75
|
+
.toSorted((left, right) => left.getTime() - right.getTime());
|
|
76
|
+
const out = [];
|
|
77
|
+
let after = start;
|
|
78
|
+
for (let short = 1; short <= (shorts ?? line.shorts.length); short += 1) {
|
|
79
|
+
const at = times.find((time) => time.getTime() > after);
|
|
80
|
+
if (at === undefined)
|
|
81
|
+
break;
|
|
82
|
+
after = at.getTime();
|
|
83
|
+
out.push(at.toISOString());
|
|
84
|
+
}
|
|
85
|
+
return out;
|
|
86
|
+
}
|
|
87
|
+
// The time zone a line's times are read in: its schedule's, else the plan's.
|
|
88
|
+
export function lineZone(plan, row) {
|
|
89
|
+
const line = plan.rows.find((one) => one.name === row);
|
|
90
|
+
return line === undefined ? plan.timeZone : zoneOf(plan, line);
|
|
91
|
+
}
|
|
66
92
|
// The coming long-video times a project may take: far enough ahead to upload in time, of a
|
|
67
93
|
// line that takes it, in an hour no other release has.
|
|
68
94
|
export function freeSlots(db, plan, who, now, count, exceptProject) {
|