@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.
Files changed (718) hide show
  1. package/dist/adapters/account-limits.js +30 -0
  2. package/dist/adapters/alignment/audio.js +1 -1
  3. package/dist/adapters/alignment/cache.js +4 -6
  4. package/dist/adapters/alignment/ctc.js +1 -2
  5. package/dist/adapters/alignment/lock.js +4 -3
  6. package/dist/adapters/alignment/multilingual.js +2 -4
  7. package/dist/adapters/alignment/prefetch.js +2 -1
  8. package/dist/adapters/alignment/runner.js +1 -1
  9. package/dist/adapters/alignment/worker.js +3 -2
  10. package/dist/adapters/explain.js +10 -12
  11. package/dist/adapters/ffmpeg.js +19 -1
  12. package/dist/adapters/host-cli/index.js +21 -4
  13. package/dist/adapters/host-cli/transport.js +2 -1
  14. package/dist/adapters/http-failure.js +53 -0
  15. package/dist/adapters/image/bytes.js +2 -1
  16. package/dist/adapters/image/codex.js +42 -9
  17. package/dist/adapters/image/fal.js +21 -53
  18. package/dist/adapters/image/google.js +33 -56
  19. package/dist/adapters/image/models.js +2 -2
  20. package/dist/adapters/image/openai.js +22 -48
  21. package/dist/adapters/image/reference.js +36 -0
  22. package/dist/adapters/image/replicate.js +21 -49
  23. package/dist/adapters/image/video.js +2 -1
  24. package/dist/adapters/interest/search-interest.js +113 -0
  25. package/dist/adapters/key-probes.js +54 -3
  26. package/dist/adapters/llm/antigravity-models.js +1 -1
  27. package/dist/adapters/llm/antigravity.js +11 -6
  28. package/dist/adapters/llm/assistant-cli.js +388 -0
  29. package/dist/adapters/llm/child-session.js +83 -0
  30. package/dist/adapters/llm/claude-code-models.js +52 -99
  31. package/dist/adapters/llm/claude-code.js +16 -4
  32. package/dist/adapters/llm/codex-limits.js +27 -68
  33. package/dist/adapters/llm/codex-models.js +1 -1
  34. package/dist/adapters/llm/codex-server-models.js +61 -109
  35. package/dist/adapters/llm/codex.js +12 -1
  36. package/dist/adapters/llm/document-workspace.js +3 -2
  37. package/dist/adapters/llm/image-workspace.js +3 -2
  38. package/dist/adapters/llm/openrouter.js +12 -48
  39. package/dist/adapters/llm/run-cli.js +9 -24
  40. package/dist/adapters/llm/sse-lines.js +5 -1
  41. package/dist/adapters/model-access.js +37 -0
  42. package/dist/adapters/tts/cartesia.js +15 -46
  43. package/dist/adapters/tts/elevenlabs.js +20 -52
  44. package/dist/adapters/tts/gemini.js +21 -41
  45. package/dist/adapters/tts/inworld-async.js +6 -2
  46. package/dist/adapters/tts/inworld.js +6 -2
  47. package/dist/adapters/tts/openai.js +20 -50
  48. package/dist/adapters/tts/system.js +2 -1
  49. package/dist/assets/models-lifecycle.yaml +6 -0
  50. package/dist/assistant-wiring.js +60 -0
  51. package/dist/autopilot-timers.js +59 -0
  52. package/dist/catalog/lifecycle.js +52 -0
  53. package/dist/catalog/propose.js +204 -0
  54. package/dist/catalog/registry.js +23 -14
  55. package/dist/catalog/schema.js +4 -1
  56. package/dist/catalog/store.js +39 -0
  57. package/dist/catalog/validate.js +5 -2
  58. package/dist/edge/autostart/native.js +3 -5
  59. package/dist/edge/autostart/prompt.js +2 -1
  60. package/dist/edge/autostart/service.js +3 -4
  61. package/dist/edge/cli-args.js +3 -4
  62. package/dist/edge/cli.js +2 -1
  63. package/dist/edge/docker-install/apply.js +11 -11
  64. package/dist/edge/docker-install/run.js +2 -1
  65. package/dist/edge/docker-install/state.js +4 -3
  66. package/dist/edge/docker-install/tree.js +2 -4
  67. package/dist/edge/docker-install/volume.js +3 -2
  68. package/dist/edge/docker.js +5 -4
  69. package/dist/edge/events/hub.js +5 -5
  70. package/dist/edge/http/actions.js +11 -32
  71. package/dist/edge/http/actor.js +5 -3
  72. package/dist/edge/http/app.js +86 -11
  73. package/dist/edge/http/assistant.js +129 -0
  74. package/dist/edge/http/audio-preview.js +4 -11
  75. package/dist/edge/http/auditions.js +1 -1
  76. package/dist/edge/http/autostart.js +5 -5
  77. package/dist/edge/http/backups.js +2 -4
  78. package/dist/edge/http/channel-memory.js +9 -22
  79. package/dist/edge/http/channels.js +14 -27
  80. package/dist/edge/http/checkpoints.js +39 -21
  81. package/dist/edge/http/design-templates.js +167 -0
  82. package/dist/edge/http/designs.js +178 -0
  83. package/dist/edge/http/diagnostics.js +5 -1
  84. package/dist/edge/http/document-themes.js +5 -14
  85. package/dist/edge/http/draft-files.js +1 -3
  86. package/dist/edge/http/draft-problem.js +5 -10
  87. package/dist/edge/http/drafts.js +3 -3
  88. package/dist/edge/http/entries.js +6 -9
  89. package/dist/edge/http/extension-auth.js +27 -0
  90. package/dist/edge/http/external-reviews.js +12 -11
  91. package/dist/edge/http/files.js +5 -11
  92. package/dist/edge/http/folder-location.js +1 -4
  93. package/dist/edge/http/fonts.js +0 -7
  94. package/dist/edge/http/home.js +47 -14
  95. package/dist/edge/http/host-cli.js +18 -8
  96. package/dist/edge/http/ids.js +8 -0
  97. package/dist/edge/http/keywords.js +35 -0
  98. package/dist/edge/http/model-checks.js +132 -0
  99. package/dist/edge/http/narration-peaks.js +5 -13
  100. package/dist/edge/http/onboarding.js +30 -26
  101. package/dist/edge/http/open-folder.js +5 -10
  102. package/dist/edge/http/outputs.js +7 -14
  103. package/dist/edge/http/patch-notes.js +6 -12
  104. package/dist/edge/http/planning.js +3 -7
  105. package/dist/edge/http/problem.js +2 -4
  106. package/dist/edge/http/project-create.js +2 -1
  107. package/dist/edge/http/project-templates.js +31 -19
  108. package/dist/edge/http/projects.js +24 -16
  109. package/dist/edge/http/prompts.js +9 -16
  110. package/dist/edge/http/pronunciations.js +1 -2
  111. package/dist/edge/http/providers.js +48 -34
  112. package/dist/edge/http/refusal.js +29 -0
  113. package/dist/edge/http/remote-access.js +40 -0
  114. package/dist/edge/http/remote-guard.js +71 -0
  115. package/dist/edge/http/request-guard.js +91 -0
  116. package/dist/edge/http/reviews.js +12 -25
  117. package/dist/edge/http/revision-files.js +6 -11
  118. package/dist/edge/http/revisions.js +14 -19
  119. package/dist/edge/http/run-cost.js +1 -2
  120. package/dist/edge/http/same-page.js +80 -0
  121. package/dist/edge/http/schedules.js +69 -40
  122. package/dist/edge/http/settings.js +27 -14
  123. package/dist/edge/http/social.js +283 -0
  124. package/dist/edge/http/staging.js +3 -5
  125. package/dist/edge/http/storage-files.js +5 -7
  126. package/dist/edge/http/storage.js +33 -25
  127. package/dist/edge/http/studio.js +94 -69
  128. package/dist/edge/http/style-preview.js +3 -6
  129. package/dist/edge/http/subtitles.js +3 -17
  130. package/dist/edge/http/template-studio.js +436 -0
  131. package/dist/edge/http/trash.js +11 -12
  132. package/dist/edge/http/tutorial.js +1 -2
  133. package/dist/edge/http/tutorials.js +5 -11
  134. package/dist/edge/http/update.js +7 -13
  135. package/dist/edge/http/waveform.js +3 -6
  136. package/dist/edge/http/youtube-edits.js +3 -4
  137. package/dist/edge/mcp/assistant.js +279 -0
  138. package/dist/edge/mcp/attribution.js +8 -2
  139. package/dist/edge/mcp/call-route.js +11 -2
  140. package/dist/edge/mcp/excluded-routes.js +29 -3
  141. package/dist/edge/mcp/http.js +11 -2
  142. package/dist/edge/mcp/instructions.js +6 -4
  143. package/dist/edge/mcp/library-tools.js +9 -5
  144. package/dist/edge/mcp/remake-tools.js +7 -4
  145. package/dist/edge/mcp/route-tool.js +35 -15
  146. package/dist/edge/mcp/server.js +13 -8
  147. package/dist/edge/mcp/tools-channels.js +11 -9
  148. package/dist/edge/mcp/tools-designs.js +165 -0
  149. package/dist/edge/mcp/tools-library.js +19 -7
  150. package/dist/edge/mcp/tools-planning.js +40 -21
  151. package/dist/edge/mcp/tools-projects.js +38 -11
  152. package/dist/edge/mcp/tools-publishing.js +9 -9
  153. package/dist/edge/mcp/tools-runs.js +23 -10
  154. package/dist/edge/mcp/tools-schedules.js +32 -8
  155. package/dist/edge/mcp/tools-setup.js +75 -11
  156. package/dist/edge/mcp/tools-social.js +47 -0
  157. package/dist/edge/mcp/tools-storage.js +11 -3
  158. package/dist/edge/mcp/tools-template-studio.js +166 -0
  159. package/dist/edge/mcp/tools.js +6 -0
  160. package/dist/edge/remote-access/cli.js +71 -0
  161. package/dist/edge/remote-access/model.js +32 -0
  162. package/dist/edge/remote-access/rules.js +207 -0
  163. package/dist/edge/remote-access/service.js +203 -0
  164. package/dist/edge/remote-access/store.js +44 -0
  165. package/dist/edge/remote-access/views.js +43 -0
  166. package/dist/extension/slopify-studio-chrome.zip +0 -0
  167. package/dist/extension/slopify-studio-firefox.zip +0 -0
  168. package/dist/host-cli/install.js +2 -2
  169. package/dist/host-cli/open-folder.js +2 -2
  170. package/dist/host-cli/paths.js +2 -4
  171. package/dist/host-cli/server.js +4 -4
  172. package/dist/host-cli/service.js +3 -3
  173. package/dist/host-cli/status.js +7 -7
  174. package/dist/kernel/db/migrations/0063-model-checks.sql +20 -0
  175. package/dist/kernel/db/migrations/0064-template-studio.sql +46 -0
  176. package/dist/kernel/db/migrations/0065-promise-and-assistant-reviews.sql +58 -0
  177. package/dist/kernel/db/migrations/0066-assistant.sql +44 -0
  178. package/dist/kernel/db/migrations/0067-autopilot.sql +6 -0
  179. package/dist/kernel/db/migrations/0068-topic-scores.sql +17 -0
  180. package/dist/kernel/db/migrations/0070-designs.sql +53 -0
  181. package/dist/kernel/db/migrations/0071-design-usage.sql +33 -0
  182. package/dist/kernel/db/migrations/0072-design-templates.sql +33 -0
  183. package/dist/kernel/db/migrations/0073-schedule-design-kind.sql +5 -0
  184. package/dist/kernel/db/migrations/0074-template-bakeoffs.sql +44 -0
  185. package/dist/kernel/db/migrations/0075-social-posts.sql +25 -0
  186. package/dist/kernel/db/migrations/0076-channel-social.sql +5 -0
  187. package/dist/kernel/db/migrations/0077-post-prompts.sql +17 -0
  188. package/dist/kernel/db/migrations/0078-uploaded-from-links.sql +7 -0
  189. package/dist/kernel/db/tx.js +2 -4
  190. package/dist/kernel/errors.js +27 -1
  191. package/dist/kernel/json-answer.js +15 -0
  192. package/dist/kernel/lock.js +4 -9
  193. package/dist/kernel/paths.js +8 -0
  194. package/dist/kernel/pipeline.js +12 -0
  195. package/dist/kernel/ports/assistant-cli.js +4 -0
  196. package/dist/kernel/ports/host-cli.js +3 -0
  197. package/dist/kernel/ports/interest.js +4 -0
  198. package/dist/kernel/ports/languages.js +0 -3
  199. package/dist/kernel/ports/model.js +2 -0
  200. package/dist/kernel/ports/settings-places.js +31 -0
  201. package/dist/kernel/ports/system-speech.js +31 -3
  202. package/dist/kernel/runner/attempt.js +14 -4
  203. package/dist/kernel/runner/audition.js +3 -1
  204. package/dist/kernel/runner/checkpoint-authority.js +2 -1
  205. package/dist/kernel/runner/index.js +8 -10
  206. package/dist/kernel/runner/model-access.js +40 -0
  207. package/dist/kernel/runner/model-check.js +153 -0
  208. package/dist/kernel/runner/providers.js +18 -12
  209. package/dist/kernel/runner/standalone.js +11 -7
  210. package/dist/main.js +338 -21
  211. package/dist/model-catalog.js +20 -0
  212. package/dist/model-checks.js +166 -0
  213. package/dist/patch-notes/4.0.0.md +51 -0
  214. package/dist/patch-notes/4.1.0.md +42 -0
  215. package/dist/patch-notes/index.json +12 -0
  216. package/dist/slices/admission/heads.js +2 -1
  217. package/dist/slices/admission/listing-facts.js +95 -0
  218. package/dist/slices/admission/repo.js +11 -7
  219. package/dist/slices/admission/rules.js +50 -15
  220. package/dist/slices/admission/schema.js +8 -0
  221. package/dist/slices/admission/stage-writes.js +0 -3
  222. package/dist/slices/admission/start.js +48 -31
  223. package/dist/slices/admission/state-words.js +34 -5
  224. package/dist/slices/admission/substitute.js +7 -4
  225. package/dist/slices/agents/attention-posts.js +31 -0
  226. package/dist/slices/agents/attention-templates.js +33 -0
  227. package/dist/slices/agents/attention.js +10 -1
  228. package/dist/slices/agents/connection.js +2 -6
  229. package/dist/slices/agents/model.js +6 -3
  230. package/dist/slices/agents/repo.js +15 -7
  231. package/dist/slices/article/continuation.js +0 -63
  232. package/dist/slices/article/plain.js +3 -2
  233. package/dist/slices/article/segments.js +2 -62
  234. package/dist/slices/assistant/access.js +1 -0
  235. package/dist/slices/assistant/asks.js +80 -0
  236. package/dist/slices/assistant/cost.js +54 -0
  237. package/dist/slices/assistant/gate.js +222 -0
  238. package/dist/slices/assistant/model.js +86 -0
  239. package/dist/slices/assistant/names.js +23 -0
  240. package/dist/slices/assistant/prompt.js +74 -0
  241. package/dist/slices/assistant/repo.js +74 -0
  242. package/dist/slices/assistant/tokens.js +18 -0
  243. package/dist/slices/assistant/turn.js +344 -0
  244. package/dist/slices/autopilot/buffer.js +76 -0
  245. package/dist/slices/autopilot/model.js +14 -0
  246. package/dist/slices/autopilot/notify.js +27 -0
  247. package/dist/slices/autopilot/repo.js +34 -0
  248. package/dist/slices/autopilot/signals.js +194 -0
  249. package/dist/slices/autopilot/warnings.js +85 -0
  250. package/dist/slices/autopilot/weekly.js +100 -0
  251. package/dist/slices/backups/files.js +2 -6
  252. package/dist/slices/backups/repo.js +3 -15
  253. package/dist/slices/backups/service.js +8 -7
  254. package/dist/slices/channels/cast-images.js +3 -2
  255. package/dist/slices/channels/model.js +0 -1
  256. package/dist/slices/channels/rebrand.js +0 -3
  257. package/dist/slices/channels/repo.js +4 -0
  258. package/dist/slices/channels/schema.js +7 -1
  259. package/dist/slices/channels/service.js +10 -0
  260. package/dist/slices/channels/videos.js +3 -15
  261. package/dist/slices/checkpoints/index.js +0 -1
  262. package/dist/slices/control/providers.js +2 -28
  263. package/dist/slices/designs/artwork.js +66 -0
  264. package/dist/slices/designs/budget.js +23 -0
  265. package/dist/slices/designs/calls.js +124 -0
  266. package/dist/slices/designs/check-context.js +38 -0
  267. package/dist/slices/designs/check-text.js +43 -0
  268. package/dist/slices/designs/fields.js +88 -0
  269. package/dist/slices/designs/frames.js +34 -0
  270. package/dist/slices/designs/gate.js +130 -0
  271. package/dist/slices/designs/layout.js +293 -0
  272. package/dist/slices/designs/model.js +35 -0
  273. package/dist/slices/designs/pdf-job.js +23 -0
  274. package/dist/slices/designs/pdf-pages.js +30 -0
  275. package/dist/slices/designs/pdf-runner.js +68 -0
  276. package/dist/slices/designs/pdf-worker.js +24 -0
  277. package/dist/slices/designs/picture-size.js +79 -0
  278. package/dist/slices/designs/pictures.js +55 -0
  279. package/dist/slices/designs/quote.js +24 -0
  280. package/dist/slices/designs/render-args.js +36 -0
  281. package/dist/slices/designs/render-input.js +59 -0
  282. package/dist/slices/designs/render.js +215 -0
  283. package/dist/slices/designs/repo.js +101 -0
  284. package/dist/slices/designs/requests.js +80 -0
  285. package/dist/slices/designs/runner.js +239 -0
  286. package/dist/slices/designs/sample-repo.js +91 -0
  287. package/dist/slices/designs/sample.js +223 -0
  288. package/dist/slices/designs/schema.js +87 -0
  289. package/dist/slices/designs/sections.js +44 -0
  290. package/dist/slices/designs/service.js +233 -0
  291. package/dist/slices/designs/steps-repo.js +102 -0
  292. package/dist/slices/designs/studio-kind.js +39 -0
  293. package/dist/slices/designs/template-checks.js +44 -0
  294. package/dist/slices/designs/template-make.js +118 -0
  295. package/dist/slices/designs/templates.js +218 -0
  296. package/dist/slices/document/fonts.js +2 -1
  297. package/dist/slices/document/model.js +7 -4
  298. package/dist/slices/document/render.js +2 -1
  299. package/dist/slices/document/theme-schema.js +1 -1
  300. package/dist/slices/document/theme.js +49 -3
  301. package/dist/slices/episodes/summarize.js +6 -2
  302. package/dist/slices/estimate/index.js +72 -28
  303. package/dist/slices/estimate/requests.js +4 -4
  304. package/dist/slices/fixes/rules.js +3 -1
  305. package/dist/slices/fonts/catalog.js +5 -3
  306. package/dist/slices/fonts/coverage.js +2 -1
  307. package/dist/slices/fonts/discovery.js +3 -2
  308. package/dist/slices/fonts/files.js +2 -6
  309. package/dist/slices/fonts/index.js +0 -1
  310. package/dist/slices/fonts/sfnt.js +45 -10
  311. package/dist/slices/images/appearance.js +8 -16
  312. package/dist/slices/images/scenes.js +102 -28
  313. package/dist/slices/keywords/digest.js +85 -0
  314. package/dist/slices/keywords/facts.js +92 -0
  315. package/dist/slices/keywords/fill.js +80 -0
  316. package/dist/slices/keywords/model.js +75 -0
  317. package/dist/slices/keywords/rules.js +187 -0
  318. package/dist/slices/keywords/schema.js +28 -0
  319. package/dist/slices/keywords/specs.js +260 -0
  320. package/dist/slices/keywords/values.js +74 -0
  321. package/dist/slices/library/lint.js +29 -12
  322. package/dist/slices/library/model.js +2 -0
  323. package/dist/slices/library/photorealistic.js +4 -12
  324. package/dist/slices/library/rename.js +9 -7
  325. package/dist/slices/library/save.js +14 -10
  326. package/dist/slices/library/slots.js +37 -11
  327. package/dist/slices/library/transfer.js +5 -4
  328. package/dist/slices/library/used-by.js +25 -10
  329. package/dist/slices/loudness/loudnorm.js +53 -12
  330. package/dist/slices/loudness/model.js +3 -2
  331. package/dist/slices/loudness/sentence-level.js +2 -1
  332. package/dist/slices/model-checks/cost.js +61 -0
  333. package/dist/slices/model-checks/listing.js +124 -0
  334. package/dist/slices/model-checks/model.js +20 -0
  335. package/dist/slices/model-checks/record.js +32 -0
  336. package/dist/slices/model-checks/repo.js +120 -0
  337. package/dist/slices/model-checks/rules.js +125 -0
  338. package/dist/slices/model-checks/run.js +143 -0
  339. package/dist/slices/model-checks/service.js +198 -0
  340. package/dist/slices/model-upkeep/model.js +1 -1
  341. package/dist/slices/model-upkeep/switch.js +72 -27
  342. package/dist/slices/model-upkeep/usage.js +39 -13
  343. package/dist/slices/narration/breaths.js +3 -1
  344. package/dist/slices/narration/clean-pieces.js +3 -1
  345. package/dist/slices/narration/concat.js +2 -1
  346. package/dist/slices/narration/pauses-model.js +3 -2
  347. package/dist/slices/narration/peaks.js +3 -4
  348. package/dist/slices/narration/pronunciation-chunks.js +4 -3
  349. package/dist/slices/narration/pronunciation.js +6 -4
  350. package/dist/slices/narration/steering.js +4 -3
  351. package/dist/slices/notifications/notifier.js +4 -3
  352. package/dist/slices/notifications/rules.js +5 -2
  353. package/dist/slices/notifications/settings.js +4 -12
  354. package/dist/slices/notifications/subject.js +4 -3
  355. package/dist/slices/onboarding/copy.js +21 -3
  356. package/dist/slices/onboarding/first-run.js +16 -40
  357. package/dist/slices/onboarding/full-video.js +3 -2
  358. package/dist/slices/onboarding/install.js +6 -4
  359. package/dist/slices/onboarding/model.js +26 -37
  360. package/dist/slices/onboarding/packs.js +22 -22
  361. package/dist/slices/onboarding/preferred-model.js +14 -0
  362. package/dist/slices/onboarding/quick-short.js +105 -65
  363. package/dist/slices/onboarding/sample.js +4 -2
  364. package/dist/slices/onboarding/state.js +9 -11
  365. package/dist/slices/onboarding/template.js +8 -6
  366. package/dist/slices/outputs/add.js +3 -2
  367. package/dist/slices/outputs/service.js +2 -2
  368. package/dist/slices/patch-notes/library.js +4 -3
  369. package/dist/slices/play-drafts/convert.js +4 -0
  370. package/dist/slices/play-drafts/readiness.js +3 -3
  371. package/dist/slices/play-drafts/review-inputs.js +9 -3
  372. package/dist/slices/play-drafts/review.js +5 -4
  373. package/dist/slices/play-drafts/schema.js +4 -0
  374. package/dist/slices/play-drafts/service.js +17 -7
  375. package/dist/slices/project-templates/from-project.js +9 -2
  376. package/dist/slices/project-templates/next-chapter.js +2 -1
  377. package/dist/slices/project-templates/provider-defaults.js +22 -0
  378. package/dist/slices/project-templates/repo.js +6 -1
  379. package/dist/slices/project-templates/setup-sections.js +26 -0
  380. package/dist/slices/project-templates/setup.js +14 -1
  381. package/dist/slices/project-templates/stand-ins.js +22 -0
  382. package/dist/slices/rebuild/activity.js +2 -0
  383. package/dist/slices/rebuild/admission-repo.js +3 -2
  384. package/dist/slices/rebuild/narration-history.js +4 -5
  385. package/dist/slices/rebuild/narration-retry.js +2 -1
  386. package/dist/slices/rebuild/preview-details.js +32 -14
  387. package/dist/slices/rebuild/preview-plan.js +3 -8
  388. package/dist/slices/rebuild/recipe-build.js +8 -3
  389. package/dist/slices/rebuild/recipe-describe.js +3 -3
  390. package/dist/slices/rebuild/recipe-digest.js +58 -0
  391. package/dist/slices/rebuild/recipe-input-schema.js +10 -0
  392. package/dist/slices/rebuild/recipe-keywords.js +71 -0
  393. package/dist/slices/rebuild/recipe-legacy.js +5 -1
  394. package/dist/slices/rebuild/recipe-model.js +4 -0
  395. package/dist/slices/rebuild/recipe-narration-text.js +2 -1
  396. package/dist/slices/rebuild/recipe-preparation.js +4 -4
  397. package/dist/slices/rebuild/recipe-reference.js +8 -4
  398. package/dist/slices/rebuild/recipe-reviews.js +27 -7
  399. package/dist/slices/rebuild/recipe-save.js +8 -5
  400. package/dist/slices/rebuild/recipe-scenes.js +40 -11
  401. package/dist/slices/rebuild/recipe-shorts.js +8 -8
  402. package/dist/slices/rebuild/recipe-social.js +46 -0
  403. package/dist/slices/rebuild/recipe-text.js +64 -20
  404. package/dist/slices/rebuild/recipe-validation.js +102 -31
  405. package/dist/slices/rebuild/recipe-visual.js +55 -16
  406. package/dist/slices/rebuild/recipe-voices.js +3 -2
  407. package/dist/slices/rebuild/recipe-work.js +19 -7
  408. package/dist/slices/rebuild/recipe-youtube.js +4 -3
  409. package/dist/slices/rebuild/recovery-selection.js +3 -1
  410. package/dist/slices/rebuild/recovery.js +9 -8
  411. package/dist/slices/rebuild/remake-outdated.js +5 -5
  412. package/dist/slices/rebuild/repo.js +4 -3
  413. package/dist/slices/rebuild/review-promise.js +25 -0
  414. package/dist/slices/rebuild/review-redo.js +3 -2
  415. package/dist/slices/rebuild/runtime-admission.js +4 -3
  416. package/dist/slices/rebuild/runtime-agent-review.js +11 -5
  417. package/dist/slices/rebuild/runtime-animate.js +10 -9
  418. package/dist/slices/rebuild/runtime-article.js +7 -5
  419. package/dist/slices/rebuild/runtime-checkpoints.js +5 -4
  420. package/dist/slices/rebuild/runtime-document.js +11 -13
  421. package/dist/slices/rebuild/runtime-export-bed.js +8 -12
  422. package/dist/slices/rebuild/runtime-export-edit.js +8 -16
  423. package/dist/slices/rebuild/runtime-export-inputs.js +12 -11
  424. package/dist/slices/rebuild/runtime-export-short.js +9 -5
  425. package/dist/slices/rebuild/runtime-export.js +38 -15
  426. package/dist/slices/rebuild/runtime-figure-card.js +2 -1
  427. package/dist/slices/rebuild/runtime-image.js +8 -12
  428. package/dist/slices/rebuild/runtime-instructions.js +2 -0
  429. package/dist/slices/rebuild/runtime-keywords.js +59 -0
  430. package/dist/slices/rebuild/runtime-lines.js +2 -11
  431. package/dist/slices/rebuild/runtime-local.js +17 -22
  432. package/dist/slices/rebuild/runtime-narration-publication.js +2 -5
  433. package/dist/slices/rebuild/runtime-narration-reuse.js +9 -13
  434. package/dist/slices/rebuild/runtime-narration-text.js +13 -17
  435. package/dist/slices/rebuild/runtime-pictures.js +2 -4
  436. package/dist/slices/rebuild/runtime-piece-label.js +10 -6
  437. package/dist/slices/rebuild/runtime-plan.js +31 -5
  438. package/dist/slices/rebuild/runtime-provider.js +67 -25
  439. package/dist/slices/rebuild/runtime-publication.js +10 -9
  440. package/dist/slices/rebuild/runtime-review.js +39 -33
  441. package/dist/slices/rebuild/runtime-run.js +4 -1
  442. package/dist/slices/rebuild/runtime-shorts.js +31 -50
  443. package/dist/slices/rebuild/runtime-store.js +20 -10
  444. package/dist/slices/rebuild/runtime-subtitles.js +28 -25
  445. package/dist/slices/rebuild/runtime-voices.js +10 -16
  446. package/dist/slices/rebuild/runtime-youtube.js +14 -31
  447. package/dist/slices/rebuild/service-readiness.js +11 -3
  448. package/dist/slices/rebuild/transition-repo.js +6 -5
  449. package/dist/slices/rebuild/work-records.js +6 -0
  450. package/dist/slices/reruns/cascade.js +1 -2
  451. package/dist/slices/reruns/index.js +6 -9
  452. package/dist/slices/research/synthesis.js +3 -2
  453. package/dist/slices/reviews/external-model.js +4 -1
  454. package/dist/slices/reviews/external-repo.js +2 -1
  455. package/dist/slices/reviews/external-rows.js +2 -3
  456. package/dist/slices/reviews/holds.js +5 -4
  457. package/dist/slices/reviews/model.js +32 -11
  458. package/dist/slices/reviews/repo.js +2 -1
  459. package/dist/slices/reviews/rules.js +4 -3
  460. package/dist/slices/reviews/verdict.js +2 -1
  461. package/dist/slices/reviews/view.js +5 -2
  462. package/dist/slices/revisions/downloads.js +4 -5
  463. package/dist/slices/revisions/image-plan.js +1 -1
  464. package/dist/slices/revisions/mutation-assets.js +9 -5
  465. package/dist/slices/revisions/mutation-cues.js +2 -5
  466. package/dist/slices/revisions/mutations.js +3 -3
  467. package/dist/slices/revisions/repo.js +3 -1
  468. package/dist/slices/revisions/schema.js +1 -0
  469. package/dist/slices/revisions/view.js +20 -0
  470. package/dist/slices/run-cost/limits.js +5 -2
  471. package/dist/slices/run-cost/meter.js +12 -5
  472. package/dist/slices/run-cost/panel.js +8 -74
  473. package/dist/slices/run-cost/plan-readings.js +127 -0
  474. package/dist/slices/run-cost/plan-use.js +169 -0
  475. package/dist/slices/run-cost/pricing.js +3 -3
  476. package/dist/slices/run-cost/week.js +32 -4
  477. package/dist/slices/schedules/agenda.js +18 -7
  478. package/dist/slices/schedules/by-date.js +42 -0
  479. package/dist/slices/schedules/calendar.js +5 -0
  480. package/dist/slices/schedules/design-agenda.js +36 -0
  481. package/dist/slices/schedules/design-check.js +41 -0
  482. package/dist/slices/schedules/design-run.js +126 -0
  483. package/dist/slices/schedules/next-run.js +62 -0
  484. package/dist/slices/schedules/prepare.js +18 -2
  485. package/dist/slices/schedules/repo.js +30 -21
  486. package/dist/slices/schedules/rescore.js +35 -0
  487. package/dist/slices/schedules/scheduler.js +13 -3
  488. package/dist/slices/schedules/schema.js +57 -3
  489. package/dist/slices/schedules/score.js +107 -0
  490. package/dist/slices/schedules/service.js +57 -24
  491. package/dist/slices/schedules/topic-list.js +15 -0
  492. package/dist/slices/schedules/topic-scores.js +104 -0
  493. package/dist/slices/schedules/topics.js +53 -41
  494. package/dist/slices/settings/cli-paths.js +4 -6
  495. package/dist/slices/settings/first-run.js +8 -43
  496. package/dist/slices/settings/health.js +30 -15
  497. package/dist/slices/settings/key-test.js +1 -1
  498. package/dist/slices/settings/model-reach.js +3 -17
  499. package/dist/slices/settings/model.js +14 -2
  500. package/dist/slices/settings/playback.js +34 -23
  501. package/dist/slices/settings/probe-read.js +16 -0
  502. package/dist/slices/settings/repo.js +33 -0
  503. package/dist/slices/settings/tutorial.js +2 -2
  504. package/dist/slices/settings/voices.js +4 -12
  505. package/dist/slices/shorts/keep.js +2 -1
  506. package/dist/slices/shorts/pick.js +6 -18
  507. package/dist/slices/shorts/prompts.js +4 -14
  508. package/dist/slices/shorts/render.js +6 -2
  509. package/dist/slices/social/channel.js +44 -0
  510. package/dist/slices/social/defaults.js +17 -0
  511. package/dist/slices/social/due.js +36 -0
  512. package/dist/slices/social/extension.js +18 -0
  513. package/dist/slices/social/fit.js +115 -0
  514. package/dist/slices/social/limits.js +186 -0
  515. package/dist/slices/social/links.js +21 -0
  516. package/dist/slices/social/media.js +132 -0
  517. package/dist/slices/social/model.js +75 -0
  518. package/dist/slices/social/notice.js +19 -0
  519. package/dist/slices/social/pack.js +176 -0
  520. package/dist/slices/social/post-prompt.js +56 -0
  521. package/dist/slices/social/queue.js +31 -0
  522. package/dist/slices/social/repo.js +133 -0
  523. package/dist/slices/social/reshape.js +97 -0
  524. package/dist/slices/social/rules.js +5 -0
  525. package/dist/slices/social/schema.js +70 -0
  526. package/dist/slices/social/serve.js +51 -0
  527. package/dist/slices/social/service.js +129 -0
  528. package/dist/slices/social/settings.js +21 -0
  529. package/dist/slices/social/sources.js +114 -0
  530. package/dist/slices/social/state.js +110 -0
  531. package/dist/slices/social/text.js +149 -0
  532. package/dist/slices/storage/arrange.js +3 -1
  533. package/dist/slices/storage/assets.js +8 -10
  534. package/dist/slices/storage/backup-designs.js +74 -0
  535. package/dist/slices/storage/backup-export.js +8 -3
  536. package/dist/slices/storage/backup-format.js +10 -0
  537. package/dist/slices/storage/backup-import.js +22 -4
  538. package/dist/slices/storage/delete-project.js +6 -5
  539. package/dist/slices/storage/files-location.js +6 -8
  540. package/dist/slices/storage/layout.js +13 -2
  541. package/dist/slices/storage/model.js +3 -0
  542. package/dist/slices/storage/portable.js +17 -12
  543. package/dist/slices/storage/schema.js +2 -0
  544. package/dist/slices/storage/staging-refs.js +2 -1
  545. package/dist/slices/storage/staging.js +6 -9
  546. package/dist/slices/storage/trim.js +26 -9
  547. package/dist/slices/studio/calendar.js +7 -2
  548. package/dist/slices/studio/disclosure.js +4 -4
  549. package/dist/slices/studio/model.js +1 -1
  550. package/dist/slices/studio/pack.js +9 -8
  551. package/dist/slices/studio/pick.js +2 -11
  552. package/dist/slices/studio/plan.js +7 -16
  553. package/dist/slices/studio/queue.js +3 -14
  554. package/dist/slices/studio/releases.js +26 -0
  555. package/dist/slices/studio/settings.js +20 -57
  556. package/dist/slices/studio/videos.js +35 -4
  557. package/dist/slices/style-preview/render.js +37 -16
  558. package/dist/slices/style-preview/schema.js +14 -1
  559. package/dist/slices/style-preview/service.js +2 -1
  560. package/dist/slices/style-preview/settings.js +33 -5
  561. package/dist/slices/subtitles/captions.js +1 -1
  562. package/dist/slices/telemetry/collector-client.js +2 -4
  563. package/dist/slices/telemetry/flush.js +2 -4
  564. package/dist/slices/telemetry/model.js +0 -7
  565. package/dist/slices/telemetry/record.js +10 -3
  566. package/dist/slices/template-studio/anatomy-view.js +161 -0
  567. package/dist/slices/template-studio/anatomy.js +73 -0
  568. package/dist/slices/template-studio/bakeoff-make.js +225 -0
  569. package/dist/slices/template-studio/bakeoff-repo.js +83 -0
  570. package/dist/slices/template-studio/bakeoff.js +333 -0
  571. package/dist/slices/template-studio/budget.js +106 -0
  572. package/dist/slices/template-studio/cards.js +78 -0
  573. package/dist/slices/template-studio/checks.js +322 -0
  574. package/dist/slices/template-studio/document-sample.js +56 -0
  575. package/dist/slices/template-studio/edit.js +15 -0
  576. package/dist/slices/template-studio/for-all.js +161 -0
  577. package/dist/slices/template-studio/free-video.js +31 -0
  578. package/dist/slices/template-studio/free.js +176 -0
  579. package/dist/slices/template-studio/gate.js +40 -0
  580. package/dist/slices/template-studio/hidden.js +20 -0
  581. package/dist/slices/template-studio/jobs.js +79 -0
  582. package/dist/slices/template-studio/loudness.js +111 -0
  583. package/dist/slices/template-studio/make.js +40 -0
  584. package/dist/slices/template-studio/model.js +58 -0
  585. package/dist/slices/template-studio/pack-import.js +253 -0
  586. package/dist/slices/template-studio/pack-samples.js +60 -0
  587. package/dist/slices/template-studio/pack.js +207 -0
  588. package/dist/slices/template-studio/paid-view.js +66 -0
  589. package/dist/slices/template-studio/paid.js +355 -0
  590. package/dist/slices/template-studio/quote.js +155 -0
  591. package/dist/slices/template-studio/ready.js +104 -0
  592. package/dist/slices/template-studio/repo.js +134 -0
  593. package/dist/slices/template-studio/reviews-path.js +58 -0
  594. package/dist/slices/template-studio/runner.js +84 -0
  595. package/dist/slices/template-studio/save.js +236 -0
  596. package/dist/slices/template-studio/say-it-as.js +248 -0
  597. package/dist/slices/template-studio/schedule-gate.js +34 -0
  598. package/dist/slices/template-studio/schedule-price.js +32 -0
  599. package/dist/slices/template-studio/schedule-sample.js +91 -0
  600. package/dist/slices/template-studio/schema.js +243 -0
  601. package/dist/slices/template-studio/scope.js +124 -0
  602. package/dist/slices/template-studio/sections.js +176 -0
  603. package/dist/slices/template-studio/service.js +175 -0
  604. package/dist/slices/template-studio/status.js +191 -0
  605. package/dist/slices/template-studio/topic.js +223 -0
  606. package/dist/slices/thumbnail/by-llm.js +1 -1
  607. package/dist/slices/trash/designs.js +76 -0
  608. package/dist/slices/trash/model.js +13 -4
  609. package/dist/slices/trash/service.js +52 -2
  610. package/dist/slices/tutorials/library.js +3 -2
  611. package/dist/slices/uploads/repo.js +37 -1
  612. package/dist/slices/uploads/rules.js +3 -2
  613. package/dist/slices/video/ass-text.js +17 -0
  614. package/dist/slices/video/audio-export.js +2 -64
  615. package/dist/slices/video/edit-list.js +2 -2
  616. package/dist/slices/video/edit-settings.js +1 -1
  617. package/dist/slices/video/encoding.js +169 -0
  618. package/dist/slices/video/ffmpeg.js +23 -21
  619. package/dist/slices/video/figure-card.js +1 -15
  620. package/dist/slices/video/plan.js +2 -1
  621. package/dist/slices/video/slideshow.js +43 -18
  622. package/dist/slices/voices/attribution.js +3 -2
  623. package/dist/slices/voices/model.js +9 -8
  624. package/dist/slices/voices/palette.js +0 -3
  625. package/dist/slices/voices/script.js +2 -1
  626. package/dist/slices/youtube/answer.js +4 -16
  627. package/dist/slices/youtube/edits-repo.js +3 -12
  628. package/dist/tutorials/AI-CLIs.md +25 -25
  629. package/dist/tutorials/Agents-MCP.md +34 -33
  630. package/dist/tutorials/Assistant.md +42 -0
  631. package/dist/tutorials/Backups.md +7 -7
  632. package/dist/tutorials/Calendar.md +17 -25
  633. package/dist/tutorials/Cast-Library.md +19 -19
  634. package/dist/tutorials/Channels.md +42 -35
  635. package/dist/tutorials/Costs-and-Run-Cost.md +38 -40
  636. package/dist/tutorials/Create-Article.md +114 -0
  637. package/dist/tutorials/Create-Extras.md +112 -0
  638. package/dist/tutorials/Create-Images.md +165 -0
  639. package/dist/tutorials/Create-Narration.md +178 -0
  640. package/dist/tutorials/Create-Overview.md +244 -0
  641. package/dist/tutorials/Create-Video-and-Style.md +144 -0
  642. package/dist/tutorials/Docker.md +3 -3
  643. package/dist/tutorials/Document-Themes.md +32 -32
  644. package/dist/tutorials/Editing-a-Project.md +94 -94
  645. package/dist/tutorials/FAQ.md +18 -18
  646. package/dist/tutorials/First-Launch-and-Welcome.md +50 -64
  647. package/dist/tutorials/Home-and-Projects.md +115 -71
  648. package/dist/tutorials/Home.md +33 -33
  649. package/dist/tutorials/Install.md +14 -13
  650. package/dist/tutorials/Intros-and-Outros.md +33 -34
  651. package/dist/tutorials/Keyboard-Shortcuts-and-Command-Palette.md +62 -58
  652. package/dist/tutorials/Keywords-Slopify-Fills.md +87 -0
  653. package/dist/tutorials/Models.md +53 -21
  654. package/dist/tutorials/Multiple-Voices.md +41 -40
  655. package/dist/tutorials/Narration-Aliases-and-Glossary.md +14 -14
  656. package/dist/tutorials/Notifications.md +27 -17
  657. package/dist/tutorials/Other-Languages.md +15 -15
  658. package/dist/tutorials/PDF-Documents.md +38 -29
  659. package/dist/tutorials/Posting-to-Social-Sites.md +121 -0
  660. package/dist/tutorials/Project-Page.md +118 -120
  661. package/dist/tutorials/Prompts-and-Templates.md +84 -0
  662. package/dist/tutorials/Prompts.md +48 -44
  663. package/dist/tutorials/Providers-and-Keys.md +105 -98
  664. package/dist/tutorials/Publishing-to-YouTube.md +31 -31
  665. package/dist/tutorials/Recovery-and-Retries.md +31 -31
  666. package/dist/tutorials/Remote-Access.md +63 -0
  667. package/dist/tutorials/Reviews-and-Checkpoints.md +98 -90
  668. package/dist/tutorials/Schedules.md +46 -27
  669. package/dist/tutorials/Settings-Reference.md +194 -174
  670. package/dist/tutorials/Shorts.md +34 -33
  671. package/dist/tutorials/Start-at-Login.md +3 -3
  672. package/dist/tutorials/Studio-Extension.md +22 -24
  673. package/dist/tutorials/Template-Studio.md +128 -0
  674. package/dist/tutorials/Templates.md +51 -77
  675. package/dist/tutorials/Trash.md +4 -4
  676. package/dist/tutorials/Troubleshooting.md +44 -44
  677. package/dist/tutorials/Uninstalling-and-Moving.md +5 -5
  678. package/dist/tutorials/Updating-and-Patch-Notes.md +18 -18
  679. package/dist/tutorials/Video-Editing.md +34 -34
  680. package/dist/tutorials/Where-Your-Files-Live.md +10 -10
  681. package/dist/tutorials/YouTube-Description.md +26 -26
  682. package/dist/tutorials/Your-First-Short.md +35 -35
  683. package/dist/tutorials/_Sidebar.md +19 -14
  684. package/dist/updater/work-in-progress.js +42 -12
  685. package/dist/web/assets/index-BNftKQrA.css +1 -0
  686. package/dist/web/assets/index-Cpa58woP.js +459 -0
  687. package/dist/web/assets/pdf-BKh8f7R6.js +56 -0
  688. package/dist/web/assets/pdf.worker.min-BmVo14Nb.mjs +28 -0
  689. package/dist/web/assets/pdf.worker.min-D7i8o3wR.js +1 -0
  690. package/dist/web/index.html +2 -2
  691. package/package.json +6 -2
  692. package/dist/slices/article/provided-entries.js +0 -66
  693. package/dist/slices/article/run.js +0 -145
  694. package/dist/slices/images/run.js +0 -234
  695. package/dist/slices/narration/legacy-plan.js +0 -58
  696. package/dist/slices/narration/run.js +0 -270
  697. package/dist/slices/rebuild/runtime-actions.js +0 -90
  698. package/dist/slices/research/run.js +0 -224
  699. package/dist/slices/subtitles/prepare.js +0 -194
  700. package/dist/slices/subtitles/transcript.js +0 -34
  701. package/dist/slices/thumbnail/run.js +0 -219
  702. package/dist/slices/video/audio-inputs.js +0 -33
  703. package/dist/slices/video/reuse-audio.js +0 -62
  704. package/dist/slices/video/run.js +0 -78
  705. package/dist/slices/video/write-export.js +0 -158
  706. package/dist/slices/video/write-subtitles.js +0 -59
  707. package/dist/tutorials/Library-Overview.md +0 -76
  708. package/dist/tutorials/Play-Images.md +0 -161
  709. package/dist/tutorials/Play-Narration.md +0 -167
  710. package/dist/tutorials/Play-Outputs.md +0 -109
  711. package/dist/tutorials/Play-Overview.md +0 -227
  712. package/dist/tutorials/Play-Title-and-Article.md +0 -136
  713. package/dist/tutorials/Play-Video-and-Style.md +0 -143
  714. package/dist/web/assets/index-BadsoORk.css +0 -1
  715. package/dist/web/assets/index-D8LqNsCl.js +0 -326
  716. package/dist/web/assets/pdf-T7MCwjIv.js +0 -54
  717. package/dist/web/assets/pdf.worker.min-Dswkl-cV.mjs +0 -29
  718. package/dist/web/assets/pdf.worker.min-zC6nfbxG.js +0 -1
@@ -1,4 +1,6 @@
1
- import { z } from "zod";
1
+ import { errorText } from "../../kernel/errors.js";
2
+ import { editSettings } from "../../kernel/ports/settings-places.js";
3
+ import { assetPathOrUndefined } from "../revisions/mutation-assets.js";
2
4
  import { outputPath } from "../storage/layout.js";
3
5
  import { usesAmbientBed } from "../video/ambient-bed.js";
4
6
  import { probeDurationMs } from "../video/ffmpeg.js";
@@ -17,25 +19,19 @@ export async function exportBed(deps, context, view) {
17
19
  };
18
20
  if (bed.source !== "upload")
19
21
  return { source: { kind: "noise", preset: bed.source }, ...settings };
20
- const fix = "Make the video again from Play with another file under Video and style → Ambient sound, or pick Rain, Fireplace or Wind there instead.";
22
+ const fix = `Choose another file under Ambient sound in ${editSettings.sound}, or pick Rain, Fireplace or Wind there instead.`;
21
23
  const assetId = view.revision.content.ambientBed;
22
- const row = z
23
- .object({ path: z.string() })
24
- .safeParse(assetId === undefined
25
- ? undefined
26
- : deps.db
27
- .prepare("SELECT path FROM project_assets WHERE project_id=? AND id=?")
28
- .get(context.work.projectId, assetId));
29
- if (!row.success)
24
+ const saved = assetId === undefined ? undefined : assetPathOrUndefined(deps, context.work.projectId, assetId);
25
+ if (saved === undefined)
30
26
  throw new Error(`The ambient sound's audio file is no longer in the project. ${fix}`);
31
- const path = outputPath(deps.paths, context.work.projectId, row.data.path);
27
+ const path = outputPath(deps.paths, context.work.projectId, saved);
32
28
  let durationMs;
33
29
  try {
34
30
  durationMs = await probeDurationMs(deps.ffmpeg, path, context.signal, deps.log);
35
31
  }
36
32
  catch (error) {
37
33
  context.signal.throwIfAborted();
38
- throw new Error(`The ambient sound's audio file couldn't be read as audio (${error instanceof Error ? error.message : String(error)}). ${fix}`);
34
+ throw new Error(`The ambient sound's audio file couldn't be read as audio (${errorText(error)}). ${fix}`);
39
35
  }
40
36
  if (durationMs <= 0)
41
37
  throw new Error(`The ambient sound's audio file holds no sound. ${fix}`);
@@ -1,16 +1,17 @@
1
- import { readFileSync } from "node:fs";
2
1
  import { extname } from "node:path";
3
2
  import { z } from "zod";
3
+ import { editSettings } from "../../kernel/ports/settings-places.js";
4
4
  import { usesFigureCards, usesYoutubeDescription } from "../admission/rules.js";
5
5
  import { captionFont, captionFontDeps } from "../fonts/coverage.js";
6
6
  import { resolveFont } from "../fonts/index.js";
7
+ import { isReadyOutput, readyOutput } from "../revisions/view.js";
7
8
  import { outputPath } from "../storage/layout.js";
8
9
  import { headingChapters } from "../video/chapters.js";
9
10
  import { sentenceCutPoints } from "../video/cuts.js";
10
11
  import { usesChapterCards, usesNarrationCuts, videoEditOf, } from "../video/edit-settings.js";
11
12
  import { probeDurationMs } from "../video/ffmpeg.js";
12
13
  import { passageSpans } from "../video/figure-spans.js";
13
- import { wordsSchema } from "./runtime-subtitles.js";
14
+ import { readTimingWords } from "./runtime-subtitles.js";
14
15
  // The containers an uploaded clip may come in; anything else in the images is a still.
15
16
  const clipExtensions = new Set([".mp4", ".mov", ".m4v", ".webm", ".mkv"]);
16
17
  export function isClipPath(path) {
@@ -19,7 +20,7 @@ export function isClipPath(path) {
19
20
  export async function exportEdit(deps, context, view, images) {
20
21
  const config = view.revision.config;
21
22
  const order = view.revision.content.imageOrder;
22
- const ready = (key) => view.outputs.find((row) => row.workKey === key && row.selected && row.available && row.state === "ready");
23
+ const ready = (key) => readyOutput(view, { workKey: key });
23
24
  // Clips: an animated image's clip first, then an uploaded clip in the image's own place.
24
25
  const clips = [];
25
26
  for (const [index, key] of order.entries()) {
@@ -109,15 +110,8 @@ async function titleFont(deps, config) {
109
110
  ...(config.titleStyle?.color === undefined ? {} : { color: config.titleStyle.color }),
110
111
  };
111
112
  }
112
- export function timingWords(deps, context, view) {
113
- const timing = view.outputs.find((row) => row.selected &&
114
- row.available &&
115
- row.state === "ready" &&
116
- row.workKey === "subtitles:timing" &&
117
- row.output.role === "subtitle_words");
118
- if (timing === undefined)
119
- throw new Error("The narration's word timing, which the video's cuts and chapter cards follow, is missing. Use More → Render the video again in the Video section, then Try again.");
120
- return wordsSchema.parse(JSON.parse(readFileSync(outputPath(deps.paths, context.work.projectId, timing.output.path), "utf8"))).words;
113
+ function timingWords(deps, context, view) {
114
+ return readTimingWords(deps, context, view, "the video's cuts and chapter cards follow").words;
121
115
  }
122
116
  // The YouTube description's chapters when that step runs, the article's headings otherwise.
123
117
  function chaptersOf(view, config, words) {
@@ -142,7 +136,7 @@ async function clipSeconds(deps, context, path, place) {
142
136
  ms = 0;
143
137
  }
144
138
  if (!(ms > 0))
145
- throw new Error(`Image ${String(place)} is a video clip Slopify can't read, so the video can't show it. Replace it with another clip or an image in Edit project → Images, then Try again.`);
139
+ throw new Error(`Image ${String(place)} is a video clip Slopify can't read, so the video can't show it. Replace it with another clip or an image in ${editSettings.images}, then Try again.`);
146
140
  return ms / 1000;
147
141
  }
148
142
  // Each card in the video's frame, shown where its description is spoken: the description's
@@ -150,9 +144,7 @@ async function clipSeconds(deps, context, path, place) {
150
144
  // found in the timing is left out rather than shown at the wrong moment.
151
145
  export function figureShots(deps, context, view, format = view.revision.config.format, words = timingWords(deps, context, view)) {
152
146
  const cards = view.outputs
153
- .filter((row) => row.selected &&
154
- row.available &&
155
- row.state === "ready" &&
147
+ .filter((row) => isReadyOutput(row) &&
156
148
  row.output.role === "figure_card" &&
157
149
  row.workKey.startsWith("figure:card:") &&
158
150
  (row.output.meta.format ?? view.revision.config.format) === format)
@@ -1,9 +1,12 @@
1
1
  import { readFileSync } from "node:fs";
2
2
  import { z } from "zod";
3
+ import { internalError } from "../../kernel/errors.js";
4
+ import { editSettings } from "../../kernel/ports/settings-places.js";
3
5
  import { narrationAliasesOf, usesNarrationPreparation, usesPronunciationGlossary, } from "../admission/rules.js";
4
6
  import { plainText } from "../article/plain.js";
5
7
  import { splitEndMatter } from "../article/split.js";
6
8
  import { usesLoudness } from "../loudness/model.js";
9
+ import { donePiece, isReadyOutput, readyOutput } from "../revisions/view.js";
7
10
  import { outputPath } from "../storage/layout.js";
8
11
  import { probeDurationMs } from "../video/ffmpeg.js";
9
12
  import { audioTimeline } from "../video/plan.js";
@@ -17,11 +20,11 @@ export function exportSnapshot(deps, context, piece) {
17
20
  .prepare("SELECT recipe_context FROM revision_work WHERE id=?")
18
21
  .get(context.work.workId);
19
22
  if (view === undefined || row === undefined)
20
- throw new Error("Slopify hit an internal error (the project version being exported is missing). Try again; if it happens again, use Download diagnostics in Settings and report it.");
23
+ throw new Error(internalError("the project version being exported is missing"));
21
24
  const plan = executionPlan(deps, view, savedCatalogue(row.recipe_context));
22
25
  const recipe = plan.recipes.find((one) => one.key === piece.key && one.fingerprint === piece.fingerprint);
23
26
  if (recipe === undefined || recipe.deferred || recipe.unresolved)
24
- throw new Error("Slopify hit an internal error (the export no longer matches the project's saved inputs). Try again; if it happens again, use Download diagnostics in Settings and report it.");
27
+ throw new Error(internalError("the export no longer matches the project's saved inputs"));
25
28
  return { view, plan };
26
29
  }
27
30
  export async function revisionAudio(deps, context, view,
@@ -45,25 +48,23 @@ export async function narrationAudio(deps, view, options = {}) {
45
48
  const levelled = options.levelled === true && usesLoudness(config) && config.sources.audio === "generate";
46
49
  const input = async (role) => {
47
50
  const segment = role === "audio_body" ? "body" : role === "audio_intro" ? "intro" : "outro";
48
- const row = view.outputs.find((one) => one.selected &&
49
- one.available &&
50
- one.state === "ready" &&
51
+ const row = view.outputs.find((one) => isReadyOutput(one) &&
51
52
  (levelled
52
53
  ? one.output.role === "audio_levelled" && one.workKey === levelKey(segment)
53
54
  : one.output.role === role));
54
55
  if (row === undefined && levelled)
55
- throw new Error(`The levelled ${segment === "body" ? "narration" : segment} isn't finished yet, so the sound can't be exported at an even volume. Let the Narration stage finish (Resume, or Try again on it), then retry this stage; or turn off Level the volume in Edit project → Pauses and volume.`);
56
+ throw new Error(`The levelled ${segment === "body" ? "narration" : segment} isn't finished yet, so the sound can't be exported at an even volume. Let the Narration stage finish (Continue the run, or Try again on Narration), then try this stage again; or turn off Level the volume in ${editSettings.sound}.`);
56
57
  if (row === undefined)
57
58
  return undefined;
58
59
  const path = outputPath(deps.paths, view.revision.projectId, row.output.path);
59
60
  const duration = row.output.durationMs ?? (await deps.measure(path));
60
61
  if (!Number.isFinite(duration) || duration <= 0)
61
- throw new Error("Slopify couldn't read the length of the narration audio, so the file may be damaged or empty. Regenerate the narration (or upload your audio file again in Edit project → Narration), then Try again.");
62
+ throw new Error(`Slopify couldn't read the length of the narration audio, so the file may be damaged or empty. Press Make the narration again in the project's Narration section (or upload your audio file again in ${editSettings.narration}), then Try again.`);
62
63
  return { path, seconds: duration / 1000 };
63
64
  };
64
65
  const body = await input("audio_body");
65
66
  if (body === undefined)
66
- throw new Error("The narration audio isn't finished yet. Let the Narration stage finish (Resume, or Try again on it), then retry this stage.");
67
+ throw new Error("The narration audio isn't finished yet. Let the Narration stage finish (Continue the run, or Try again on Narration), then try this stage again.");
67
68
  return narrationTimeline(config, {
68
69
  body,
69
70
  intro: speaksPart(config, "intro") ? await input("audio_intro") : undefined,
@@ -95,7 +96,7 @@ export function revisionTranscript(deps, snapshot, kind) {
95
96
  const concat = plan.recipes.find((one) => one.key === (kind === "body" ? "audio:body:concat" : `audio:${kind}`));
96
97
  if (concat !== undefined) {
97
98
  const texts = concat.dependsOn.map((key) => {
98
- const row = view.pieces.find((one) => one.key === key && one.selected && one.available && one.piece.state === "done");
99
+ const row = donePiece(view, key);
99
100
  const payload = row?.piece.payload === null || row?.piece.payload === undefined
100
101
  ? undefined
101
102
  : z.object({ text: z.string().optional() }).parse(JSON.parse(row.piece.payload));
@@ -108,13 +109,13 @@ export function revisionTranscript(deps, snapshot, kind) {
108
109
  Array.isArray(recipe.input.semantic) &&
109
110
  typeof recipe.input.semantic[0] === "string")
110
111
  return recipe.input.semantic[0];
111
- throw new Error("Some narration chunks aren't finished, so the full narration text isn't available. Let the Narration stage finish (Resume, or Try again on it), or regenerate the missing chunk in Edit project → Narration.");
112
+ throw new Error(`Some narration chunks aren't finished, so the full narration text isn't available. Let the Narration stage finish (Continue the run, or Try again on Narration), or make the missing chunk again in ${editSettings.narration}.`);
112
113
  });
113
114
  if (texts.length > 0)
114
115
  return texts.join("\n");
115
116
  }
116
117
  if (kind === "body") {
117
- const row = view.outputs.find((one) => one.selected && one.available && one.state === "ready" && one.output.role === "article_txt");
118
+ const row = readyOutput(view, { role: "article_txt" });
118
119
  if (row !== undefined)
119
120
  return readFileSync(outputPath(deps.paths, view.revision.projectId, row.output.path), "utf8");
120
121
  if (view.articleMarkdown !== null)
@@ -5,9 +5,9 @@ import { renderShort, shortFrame } from "../shorts/render.js";
5
5
  import { allocateAsset, discardPreparedAssets, sealAsset } from "../storage/assets.js";
6
6
  import { projectDir } from "../storage/layout.js";
7
7
  import { probeDurationMs } from "../video/ffmpeg.js";
8
- import { slideshowImages } from "./runtime-export.js";
8
+ import { exportMachine, slideshowImages, warningsOf, } from "./runtime-export.js";
9
9
  import { preparedResult, preparedText, publishResult } from "./runtime-publication.js";
10
- import { timingWords } from "./runtime-shorts.js";
10
+ import { readTimingWords } from "./runtime-subtitles.js";
11
11
  // A short-mode project's video (`admission/short-mode.ts`): the whole narration timeline as
12
12
  // one clip through the Shorts renderer, so it looks like every short Slopify cuts from a long
13
13
  // video - the images moving as the project says, the title on top and big word-by-word
@@ -16,10 +16,10 @@ export async function executeShortExport(deps, context, piece, snapshot, timelin
16
16
  const { view } = snapshot;
17
17
  const config = view.revision.config;
18
18
  const images = slideshowImages(deps, context, view);
19
- const words = timingWords(deps, context, view);
19
+ const words = readTimingWords(deps, context, view, "the shorts are cut and captioned from").words;
20
20
  const seconds = timeline.reduce((sum, segment) => sum + segment.seconds, 0);
21
21
  if (seconds <= 0)
22
- throw new Error("The short has no narration to play, so there is nothing to render. Let the Narration stage finish (Resume, or Try again on it), then retry this stage.");
22
+ throw new Error("The short has no narration to play, so there is nothing to render. Let the Narration stage finish (Continue the run, or Try again on Narration), then try this stage again.");
23
23
  const font = await resolveBoldFont(deps.paths, config.subtitles?.fontId ?? "default");
24
24
  if (!context.maySubmit(piece.id))
25
25
  return "held";
@@ -28,8 +28,11 @@ export async function executeShortExport(deps, context, piece, snapshot, timelin
28
28
  const directory = projectDir(deps.paths, context.work.projectId);
29
29
  const relativePath = (path) => relative(directory, path).split(sep).join("/");
30
30
  try {
31
+ const machine = await exportMachine(deps, context.work.projectId);
31
32
  const master = await renderShort({
32
33
  bin: deps.ffmpeg,
34
+ machine: machine.machine,
35
+ notice: machine.notice,
33
36
  master: masterGoal(config, "video"),
34
37
  timeline,
35
38
  start: 0,
@@ -58,6 +61,7 @@ export async function executeShortExport(deps, context, piece, snapshot, timelin
58
61
  const asset = sealAsset(deps, pending);
59
62
  prepared.push(preparedResult(deps, context, piece, "video", asset, durationMs, {
60
63
  subtitlesMode: "off",
64
+ ...warningsOf(machine.notices),
61
65
  ...(master === undefined ? {} : { master }),
62
66
  }));
63
67
  prepared.push(preparedText(deps, context, piece, "render_params", "render.json", JSON.stringify({
@@ -76,7 +80,7 @@ export async function executeShortExport(deps, context, piece, snapshot, timelin
76
80
  output: pending.path,
77
81
  })));
78
82
  await publishResult(deps, context, piece, prepared, { totalSeconds: seconds }, asset);
79
- deps.count?.("stage.completed", { stage: "video" });
83
+ deps.count?.("stage.completed", { stage: "video" }, context.work.projectId);
80
84
  return "done";
81
85
  }
82
86
  finally {
@@ -1,8 +1,11 @@
1
1
  import { copyFileSync, mkdirSync, mkdtempSync, rmSync } from "node:fs";
2
2
  import { extname, join, relative, sep } from "node:path";
3
+ import { internalError } from "../../kernel/errors.js";
4
+ import { editSettings } from "../../kernel/ports/settings-places.js";
3
5
  import { usesShortMode } from "../admission/short-mode.js";
4
6
  import { masterFile, masterReport } from "../loudness/loudnorm.js";
5
7
  import { masterGoal } from "../loudness/model.js";
8
+ import { isReadyOutput, readyOutput } from "../revisions/view.js";
6
9
  import { allocateAsset, discardPreparedAssets, sealAsset } from "../storage/assets.js";
7
10
  import { outputPath, projectDir, renderCacheDir } from "../storage/layout.js";
8
11
  import { audioExportArgs } from "../video/audio-export.js";
@@ -18,10 +21,21 @@ import { exportSnapshot, retainedOutput, revisionAudio, } from "./runtime-export
18
21
  import { executeShortExport } from "./runtime-export-short.js";
19
22
  import { lineLevelledAudio } from "./runtime-lines.js";
20
23
  import { preparedResult, preparedText, publishResult } from "./runtime-publication.js";
24
+ import { pieceAlreadyDone } from "./work-records.js";
25
+ // The machine settings for one render, and the notices it gathers for the project page.
26
+ export async function exportMachine(deps, projectId) {
27
+ const chosen = await deps.renderMachine?.(projectId);
28
+ const notices = chosen?.notice === undefined ? [] : [chosen.notice];
29
+ return {
30
+ machine: chosen?.machine,
31
+ notices,
32
+ notice: (sentence) => {
33
+ notices.push(sentence);
34
+ },
35
+ };
36
+ }
21
37
  export async function executeExportRecipe(deps, context, piece) {
22
- if (deps.db
23
- .prepare("SELECT state FROM revision_work_pieces WHERE id=? AND work_id=?")
24
- .get(piece.id, context.work.workId)?.state === "done")
38
+ if (pieceAlreadyDone(deps.db, context.work.workId, piece.id))
25
39
  return "done";
26
40
  if (!context.maySubmit(piece.id))
27
41
  return "held";
@@ -31,9 +45,9 @@ export async function executeExportRecipe(deps, context, piece) {
31
45
  const audio = await revisionAudio(deps, context, view, { levelled: true });
32
46
  const wav = piece.key === "export:wav";
33
47
  if (!wav && piece.key !== "export:video")
34
- throw new Error("Slopify hit an internal error (unknown kind of export). Try again; if it happens again, use Download diagnostics in Settings and report it.");
48
+ throw new Error(internalError("unknown kind of export"));
35
49
  if (wav && audio.length === 0)
36
- throw new Error("The audio export has nothing to export because narration is turned off for this project. Turn narration on in Edit project, then Try again.");
50
+ throw new Error(`The audio export has nothing to export because narration is turned off for this project. Turn narration on (Narration source in ${editSettings.narration}), then Try again.`);
37
51
  if (!wav && usesShortMode(view.revision.config))
38
52
  return executeShortExport(deps, context, piece, snapshot, audio);
39
53
  const filename = wav ? "audio.wav" : "video.mp4";
@@ -49,7 +63,7 @@ export async function executeExportRecipe(deps, context, piece) {
49
63
  const sound = linesDirectory === undefined
50
64
  ? audio
51
65
  : await lineLevelledAudio(deps, context, view, audio, linesDirectory);
52
- const captions = snapshot.view.outputs.filter((row) => row.selected && row.available && row.state === "ready" && row.workKey === "subtitles:files");
66
+ const captions = snapshot.view.outputs.filter((row) => isReadyOutput(row) && row.workKey === "subtitles:files");
53
67
  const burn = !wav && view.revision.config.subtitles?.mode === "burn-in";
54
68
  if (burn)
55
69
  directory = captionDirectory(deps, context, snapshot);
@@ -119,6 +133,8 @@ export async function executeExportRecipe(deps, context, piece) {
119
133
  };
120
134
  if (!context.maySubmit(piece.id))
121
135
  return "held";
136
+ const machine = await exportMachine(deps, context.work.projectId);
137
+ const priority = machine.machine?.priority;
122
138
  const onProgress = (elapsedMs) => context.emit({
123
139
  type: "stage.progress",
124
140
  projectId: context.work.projectId,
@@ -139,10 +155,11 @@ export async function executeExportRecipe(deps, context, piece) {
139
155
  args: audioExportArgs(sound, mixed),
140
156
  signal: context.signal,
141
157
  log: deps.log,
158
+ priority,
142
159
  onProgress,
143
160
  });
144
161
  if (goal !== undefined) {
145
- const run = { bin: deps.ffmpeg, log: deps.log, signal: context.signal };
162
+ const run = { bin: deps.ffmpeg, log: deps.log, signal: context.signal, priority };
146
163
  await masterFile(run, mixed, `${pending.absolutePath}.master.wav`, goal, {
147
164
  sampleRate: 48000,
148
165
  channels: 2,
@@ -167,6 +184,7 @@ export async function executeExportRecipe(deps, context, piece) {
167
184
  ],
168
185
  signal: context.signal,
169
186
  log: deps.log,
187
+ priority,
170
188
  onProgress: () => { },
171
189
  });
172
190
  master = await masterReport(run, pending.absolutePath, goal);
@@ -193,20 +211,21 @@ export async function executeExportRecipe(deps, context, piece) {
193
211
  log: deps.log,
194
212
  onProgress,
195
213
  ...(goal === undefined ? {} : { master: goal }),
214
+ machine: machine.machine,
215
+ notice: machine.notice,
196
216
  });
197
217
  context.signal.throwIfAborted();
198
218
  const asset = sealAsset(deps, pending);
199
219
  prepared.push(preparedResult(deps, context, piece, wav ? "audio_export" : "video", asset, Math.round(totalSeconds * 1000), {
200
220
  subtitlesMode: mode,
201
- ...(edited === undefined || edited.warnings.length === 0
202
- ? {}
203
- : { warnings: edited.warnings }),
221
+ // An audio export encodes no picture, so the encoder's notice is not its to show.
222
+ ...warningsOf([...(edited?.warnings ?? []), ...(wav ? [] : machine.notices)]),
204
223
  ...(master === undefined ? {} : { master }),
205
224
  }));
206
225
  prepared.push(preparedText(deps, context, piece, "render_params", "render.json", JSON.stringify(record)));
207
226
  prepared.push(...captions.map((row) => retainedOutput(deps, view, row)));
208
227
  await publishResult(deps, context, piece, prepared, { totalSeconds }, asset);
209
- deps.count?.("stage.completed", { stage: "video" });
228
+ deps.count?.("stage.completed", { stage: "video" }, context.work.projectId);
210
229
  return "done";
211
230
  }
212
231
  finally {
@@ -220,18 +239,18 @@ export async function executeExportRecipe(deps, context, piece) {
220
239
  // The project's images in slideshow order, as files.
221
240
  export function slideshowImages(deps, context, view) {
222
241
  return view.revision.content.imageOrder.map((key) => {
223
- const row = view.outputs.find((one) => one.workKey === `image:${key}` && one.selected && one.available && one.state === "ready");
242
+ const row = readyOutput(view, { workKey: `image:${key}` });
224
243
  if (row === undefined)
225
- throw new Error("The video export can't find one of the slideshow images. Use More → make it again in its section on Images (or regenerate that image in Edit project → Images), then Try again.");
244
+ throw new Error(`The video export can't find one of the slideshow images. Press Make this image again on it in the project's Images section (or replace it in ${editSettings.images}), then Try again.`);
226
245
  return outputPath(deps.paths, context.work.projectId, row.output.path);
227
246
  });
228
247
  }
229
248
  function captionDirectory(deps, context, snapshot) {
230
- const find = (role) => snapshot.view.outputs.find((row) => row.selected && row.available && row.state === "ready" && row.output.role === role);
249
+ const find = (role) => readyOutput(snapshot.view, { role });
231
250
  const ass = find("subtitle_ass");
232
251
  const font = find("subtitle_font");
233
252
  if (ass === undefined || font === undefined)
234
- throw new Error("Burned-in captions are missing their caption file or font. Use More → Render the video again in the Video section (or choose the caption font again in Edit project → Subtitles), then Try again.");
253
+ throw new Error(`Burned-in captions are missing their caption file or font. Press Make the video again in the project's Video section (or choose the caption font again in ${editSettings.captions}), then Try again.`);
235
254
  const directory = mkdtempSync(join(projectDir(deps.paths, context.work.projectId), "render-"));
236
255
  try {
237
256
  mkdirSync(join(directory, "fonts"), { mode: 0o700 });
@@ -244,3 +263,7 @@ function captionDirectory(deps, context, snapshot) {
244
263
  throw error;
245
264
  }
246
265
  }
266
+ // What the video could not do as asked, for the project page; nothing when it did it all.
267
+ export function warningsOf(warnings) {
268
+ return warnings.length === 0 ? {} : { warnings };
269
+ }
@@ -1,6 +1,7 @@
1
1
  import { copyFileSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { z } from "zod";
4
+ import { internalError } from "../../kernel/errors.js";
4
5
  import { captionFont, captionFontDeps } from "../fonts/coverage.js";
5
6
  import { describedKinds } from "../narration/blocks.js";
6
7
  import { allocateAsset, discardPreparedAssets, sealAsset } from "../storage/assets.js";
@@ -25,7 +26,7 @@ const values = z.object({
25
26
  const frames = { "16:9": [1920, 1080], "9:16": [1080, 1920], "1:1": [1080, 1080] };
26
27
  export async function executeFigureCard(deps, context, piece) {
27
28
  if (piece.input.kind !== "local")
28
- throw new Error("Slopify hit an internal error (a figure card step was set up wrongly). Try again; if it happens again, use Download diagnostics in Settings and report it.");
29
+ throw new Error(internalError("a figure card step was set up wrongly"));
29
30
  const card = values.parse(piece.input.values);
30
31
  const view = executionView(deps, context.work.projectId, context.work.revisionId);
31
32
  // The article's own picture for a figure, when the project has it as an upload.
@@ -1,6 +1,7 @@
1
1
  import { readFileSync } from "node:fs";
2
- import { z } from "zod";
2
+ import { editSettings } from "../../kernel/ports/settings-places.js";
3
3
  import { imageBlob } from "../channels/repo.js";
4
+ import { assetPathOrUndefined } from "../revisions/mutation-assets.js";
4
5
  import { outputPath } from "../storage/layout.js";
5
6
  // The provider call an image step makes: its request, the effort when one is chosen and, while
6
7
  // the project's Establishing image is on, that image's bytes as the visual reference.
@@ -22,27 +23,22 @@ function referenceImage(deps, projectId, input) {
22
23
  if (input.reference === undefined)
23
24
  return undefined;
24
25
  // The step waits for the establishing image, so a missing one went away since.
25
- const missing = "The establishing image this image is drawn from is missing. Use Regenerate on the establishing image in the Images section (or upload it again in Edit project → Images), then use Try again.";
26
+ const missing = `The establishing image this image is drawn from is missing. Press Make the establishing image again in the project's Images section (or upload it again in ${editSettings.images}), then use Try again.`;
26
27
  if (input.reference.assetId === null)
27
28
  throw new Error(missing);
28
- const row = z
29
- .object({ path: z.string() })
30
- .optional()
31
- .parse(deps.db
32
- .prepare("SELECT path FROM project_assets WHERE id=? AND project_id=?")
33
- .get(input.reference.assetId, projectId));
34
- if (row === undefined)
29
+ const saved = assetPathOrUndefined(deps, projectId, input.reference.assetId);
30
+ if (saved === undefined)
35
31
  throw new Error(missing);
36
32
  let bytes;
37
33
  try {
38
- bytes = new Uint8Array(readFileSync(outputPath(deps.paths, projectId, row.path)));
34
+ bytes = new Uint8Array(readFileSync(outputPath(deps.paths, projectId, saved)));
39
35
  }
40
36
  catch {
41
37
  throw new Error(missing);
42
38
  }
43
39
  const mime = imageMime(bytes);
44
40
  if (mime === undefined)
45
- throw new Error("The establishing image is not a PNG or JPEG file, so it can't be sent as a reference. Upload a PNG or JPEG in Edit project → Images → Establishing image, then use Try again.");
41
+ throw new Error(`The establishing image is not a PNG or JPEG file, so it can't be sent as a reference. Upload a PNG or JPEG under Establishing image in ${editSettings.images}, then use Try again.`);
46
42
  return { bytes, mime };
47
43
  }
48
44
  // The cast pictures the request names, read by hash from the database. They are never removed
@@ -56,7 +52,7 @@ function castImages(deps, input) {
56
52
  images: member.images.map((sha256) => {
57
53
  const blob = imageBlob(deps.db, sha256);
58
54
  if (blob === undefined)
59
- throw new Error(`A picture of ${member.name} that this project was started with is not in this Slopify's database, so it can't be sent as a reference. Add the picture again in Channels → Cast, then start the video again from Play.`);
55
+ throw new Error(`A picture of ${member.name} that this project was started with is not in this Slopify's database, so it can't be sent as a reference. Add the picture again in Channels → Cast, then start the video again from Create.`);
60
56
  return blob;
61
57
  }),
62
58
  }));
@@ -49,5 +49,7 @@ function label(key) {
49
49
  return "Intro";
50
50
  if (key === "entry:outro:text")
51
51
  return "Outro";
52
+ if (key === "article:digest")
53
+ return "Keyword digest";
52
54
  return "Thumbnail instructions";
53
55
  }
@@ -0,0 +1,59 @@
1
+ import { errorText } from "../../kernel/errors.js";
2
+ import { fillLateKeywords, mentionsLateKeyword } from "../keywords/fill.js";
3
+ import { durationText, releaseDateText } from "../keywords/values.js";
4
+ import { releasesOf } from "../studio/releases.js";
5
+ import { revisionAudio } from "./runtime-export-inputs.js";
6
+ import { executionView } from "./runtime-plan.js";
7
+ // The release date and the video's length are filled into a request only as it is sent: the
8
+ // planned request keeps `{{Slopify_Release_Date}}` and `{{Slopify_Duration}}`, so moving the
9
+ // date later, or a new length, never makes anything outdated. Every other call goes through as
10
+ // it was.
11
+ export function lateKeywordProviders(providers, values) {
12
+ let once;
13
+ const late = () => {
14
+ once ??= values();
15
+ return once;
16
+ };
17
+ const wrap = (inner) => ({
18
+ ...inner,
19
+ llm: async (call, onEvent) => {
20
+ if (!call.messages.some((message) => mentionsLateKeyword(message.content)))
21
+ return inner.llm(call, onEvent);
22
+ const filled = await late();
23
+ return inner.llm({
24
+ ...call,
25
+ messages: call.messages.map((message) => ({
26
+ ...message,
27
+ content: fillLateKeywords(message.content, filled),
28
+ })),
29
+ }, onEvent);
30
+ },
31
+ image: async (call) => mentionsLateKeyword(call.prompt)
32
+ ? inner.image({ ...call, prompt: fillLateKeywords(call.prompt, await late()) })
33
+ : inner.image(call),
34
+ forPiece: (pieceId) => wrap(inner.forPiece(pieceId)),
35
+ });
36
+ return wrap(providers);
37
+ }
38
+ // The long video's release time from the posting plan, and the narration's length once it is
39
+ // there. A length that can't be read yet (an article review runs before the narration) is
40
+ // blank, so its line is left out, and the reason is logged.
41
+ export async function lateValuesOf(deps, context) {
42
+ const releaseDate = releaseDateText(releasesOf(deps.db, context.work.projectId).find((release) => release.short === 0)?.at);
43
+ const view = executionView(deps, context.work.projectId, context.work.revisionId);
44
+ let duration;
45
+ if (view !== undefined && view.revision.config.sources.audio !== "off")
46
+ try {
47
+ const audio = await revisionAudio(deps, context, view);
48
+ const seconds = audio.reduce((sum, segment) => sum + segment.seconds, 0);
49
+ duration = seconds > 0 ? durationText(seconds) : undefined;
50
+ }
51
+ catch (error) {
52
+ deps.log.write("warn", "keywords.duration", {
53
+ projectId: context.work.projectId,
54
+ stage: context.work.kind,
55
+ detail: errorText(error),
56
+ });
57
+ }
58
+ return { releaseDate, duration };
59
+ }
@@ -1,9 +1,7 @@
1
- import { readFileSync } from "node:fs";
2
1
  import { join } from "node:path";
3
2
  import { levelLines } from "../loudness/line-level.js";
4
- import { outputPath } from "../storage/layout.js";
5
3
  import { linesLevelled } from "./recipe-lines.js";
6
- import { wordsSchema } from "./runtime-subtitles.js";
4
+ import { readTimingWords } from "./runtime-subtitles.js";
7
5
  // The sound a multi-voice run plays, with its body narration levelled line by line
8
6
  // (`loudness/line-level.ts`) into `directory`, for the video, the audio export and the audio
9
7
  // files alike. The words are timed on the timeline these segments lay out, so the body starts
@@ -11,14 +9,7 @@ import { wordsSchema } from "./runtime-subtitles.js";
11
9
  export async function lineLevelledAudio(deps, context, view, audio, directory) {
12
10
  if (!linesLevelled(view.revision.config))
13
11
  return audio;
14
- const timing = view.outputs.find((row) => row.selected &&
15
- row.available &&
16
- row.state === "ready" &&
17
- row.workKey === "subtitles:timing" &&
18
- row.output.role === "subtitle_words");
19
- if (timing === undefined)
20
- throw new Error("The narration's word timing, which evens out the speakers' volume, is missing. Use More → Render the video again in the Video section, then Try again.");
21
- const { words } = wordsSchema.parse(JSON.parse(readFileSync(outputPath(deps.paths, context.work.projectId, timing.output.path), "utf8")));
12
+ const { words } = readTimingWords(deps, context, view, "evens out the speakers' volume");
22
13
  const run = { bin: deps.ffmpeg, log: deps.log, signal: context.signal };
23
14
  const levelled = [];
24
15
  let offset = 0;