@skyphusion-labs/vivijure-core 0.9.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 (267) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +87 -0
  3. package/dist/audio-routing.d.ts +2 -0
  4. package/dist/audio-routing.d.ts.map +1 -0
  5. package/dist/audio-routing.js +4 -0
  6. package/dist/audio-routing.js.map +1 -0
  7. package/dist/audio-stage.d.ts +4 -0
  8. package/dist/audio-stage.d.ts.map +1 -0
  9. package/dist/audio-stage.js +25 -0
  10. package/dist/audio-stage.js.map +1 -0
  11. package/dist/beat-analyze.d.ts +22 -0
  12. package/dist/beat-analyze.d.ts.map +1 -0
  13. package/dist/beat-analyze.js +111 -0
  14. package/dist/beat-analyze.js.map +1 -0
  15. package/dist/beat-sync-types.d.ts +33 -0
  16. package/dist/beat-sync-types.d.ts.map +1 -0
  17. package/dist/beat-sync-types.js +4 -0
  18. package/dist/beat-sync-types.js.map +1 -0
  19. package/dist/bundle-assembler.d.ts +54 -0
  20. package/dist/bundle-assembler.d.ts.map +1 -0
  21. package/dist/bundle-assembler.js +376 -0
  22. package/dist/bundle-assembler.js.map +1 -0
  23. package/dist/bundle-durations.d.ts +5 -0
  24. package/dist/bundle-durations.d.ts.map +1 -0
  25. package/dist/bundle-durations.js +68 -0
  26. package/dist/bundle-durations.js.map +1 -0
  27. package/dist/bundle-storyboard.d.ts +11 -0
  28. package/dist/bundle-storyboard.d.ts.map +1 -0
  29. package/dist/bundle-storyboard.js +82 -0
  30. package/dist/bundle-storyboard.js.map +1 -0
  31. package/dist/captions.d.ts +25 -0
  32. package/dist/captions.d.ts.map +1 -0
  33. package/dist/captions.js +56 -0
  34. package/dist/captions.js.map +1 -0
  35. package/dist/cast-db.d.ts +61 -0
  36. package/dist/cast-db.d.ts.map +1 -0
  37. package/dist/cast-db.js +324 -0
  38. package/dist/cast-db.js.map +1 -0
  39. package/dist/cast-lora-train.d.ts +18 -0
  40. package/dist/cast-lora-train.d.ts.map +1 -0
  41. package/dist/cast-lora-train.js +215 -0
  42. package/dist/cast-lora-train.js.map +1 -0
  43. package/dist/cast-loras.d.ts +21 -0
  44. package/dist/cast-loras.d.ts.map +1 -0
  45. package/dist/cast-loras.js +69 -0
  46. package/dist/cast-loras.js.map +1 -0
  47. package/dist/clip-content-validate.d.ts +43 -0
  48. package/dist/clip-content-validate.d.ts.map +1 -0
  49. package/dist/clip-content-validate.js +115 -0
  50. package/dist/clip-content-validate.js.map +1 -0
  51. package/dist/clip-job-model.d.ts +42 -0
  52. package/dist/clip-job-model.d.ts.map +1 -0
  53. package/dist/clip-job-model.js +8 -0
  54. package/dist/clip-job-model.js.map +1 -0
  55. package/dist/clip-validate.d.ts +33 -0
  56. package/dist/clip-validate.d.ts.map +1 -0
  57. package/dist/clip-validate.js +247 -0
  58. package/dist/clip-validate.js.map +1 -0
  59. package/dist/d1-retry.d.ts +20 -0
  60. package/dist/d1-retry.d.ts.map +1 -0
  61. package/dist/d1-retry.js +67 -0
  62. package/dist/d1-retry.js.map +1 -0
  63. package/dist/db-env.d.ts +6 -0
  64. package/dist/db-env.d.ts.map +1 -0
  65. package/dist/db-env.js +2 -0
  66. package/dist/db-env.js.map +1 -0
  67. package/dist/dialogue-lines.d.ts +23 -0
  68. package/dist/dialogue-lines.d.ts.map +1 -0
  69. package/dist/dialogue-lines.js +84 -0
  70. package/dist/dialogue-lines.js.map +1 -0
  71. package/dist/film-advance-lease.d.ts +9 -0
  72. package/dist/film-advance-lease.d.ts.map +1 -0
  73. package/dist/film-advance-lease.js +19 -0
  74. package/dist/film-advance-lease.js.map +1 -0
  75. package/dist/film-model.d.ts +415 -0
  76. package/dist/film-model.d.ts.map +1 -0
  77. package/dist/film-model.js +541 -0
  78. package/dist/film-model.js.map +1 -0
  79. package/dist/film-orchestrator.d.ts +216 -0
  80. package/dist/film-orchestrator.d.ts.map +1 -0
  81. package/dist/film-orchestrator.js +2204 -0
  82. package/dist/film-orchestrator.js.map +1 -0
  83. package/dist/film-render-bridge.d.ts +26 -0
  84. package/dist/film-render-bridge.d.ts.map +1 -0
  85. package/dist/film-render-bridge.js +176 -0
  86. package/dist/film-render-bridge.js.map +1 -0
  87. package/dist/finish-hash.d.ts +10 -0
  88. package/dist/finish-hash.d.ts.map +1 -0
  89. package/dist/finish-hash.js +60 -0
  90. package/dist/finish-hash.js.map +1 -0
  91. package/dist/index.d.ts +48 -0
  92. package/dist/index.d.ts.map +1 -0
  93. package/dist/index.js +45 -0
  94. package/dist/index.js.map +1 -0
  95. package/dist/key-safety.d.ts +5 -0
  96. package/dist/key-safety.d.ts.map +1 -0
  97. package/dist/key-safety.js +23 -0
  98. package/dist/key-safety.js.map +1 -0
  99. package/dist/lora-bundle.d.ts +19 -0
  100. package/dist/lora-bundle.d.ts.map +1 -0
  101. package/dist/lora-bundle.js +75 -0
  102. package/dist/lora-bundle.js.map +1 -0
  103. package/dist/lora-keys.d.ts +2 -0
  104. package/dist/lora-keys.d.ts.map +1 -0
  105. package/dist/lora-keys.js +4 -0
  106. package/dist/lora-keys.js.map +1 -0
  107. package/dist/modules/conformance.d.ts +58 -0
  108. package/dist/modules/conformance.d.ts.map +1 -0
  109. package/dist/modules/conformance.js +369 -0
  110. package/dist/modules/conformance.js.map +1 -0
  111. package/dist/modules/manifest-validate.d.ts +5 -0
  112. package/dist/modules/manifest-validate.d.ts.map +1 -0
  113. package/dist/modules/manifest-validate.js +60 -0
  114. package/dist/modules/manifest-validate.js.map +1 -0
  115. package/dist/modules/registry.d.ts +184 -0
  116. package/dist/modules/registry.d.ts.map +1 -0
  117. package/dist/modules/registry.js +653 -0
  118. package/dist/modules/registry.js.map +1 -0
  119. package/dist/modules/render-pipeline.d.ts +38 -0
  120. package/dist/modules/render-pipeline.d.ts.map +1 -0
  121. package/dist/modules/render-pipeline.js +40 -0
  122. package/dist/modules/render-pipeline.js.map +1 -0
  123. package/dist/modules/types.d.ts +544 -0
  124. package/dist/modules/types.d.ts.map +1 -0
  125. package/dist/modules/types.js +57 -0
  126. package/dist/modules/types.js.map +1 -0
  127. package/dist/operator-config.d.ts +20 -0
  128. package/dist/operator-config.d.ts.map +1 -0
  129. package/dist/operator-config.js +85 -0
  130. package/dist/operator-config.js.map +1 -0
  131. package/dist/output-extract.d.ts +8 -0
  132. package/dist/output-extract.d.ts.map +1 -0
  133. package/dist/output-extract.js +129 -0
  134. package/dist/output-extract.js.map +1 -0
  135. package/dist/planner-prompt.d.ts +12 -0
  136. package/dist/planner-prompt.d.ts.map +1 -0
  137. package/dist/planner-prompt.js +337 -0
  138. package/dist/planner-prompt.js.map +1 -0
  139. package/dist/planner-yaml.d.ts +15 -0
  140. package/dist/planner-yaml.d.ts.map +1 -0
  141. package/dist/planner-yaml.js +157 -0
  142. package/dist/planner-yaml.js.map +1 -0
  143. package/dist/platform/fetcher.d.ts +3 -0
  144. package/dist/platform/fetcher.d.ts.map +1 -0
  145. package/dist/platform/fetcher.js +7 -0
  146. package/dist/platform/fetcher.js.map +1 -0
  147. package/dist/platform/index.d.ts +6 -0
  148. package/dist/platform/index.d.ts.map +1 -0
  149. package/dist/platform/index.js +5 -0
  150. package/dist/platform/index.js.map +1 -0
  151. package/dist/platform/object-store-r2.d.ts +26 -0
  152. package/dist/platform/object-store-r2.d.ts.map +1 -0
  153. package/dist/platform/object-store-r2.js +74 -0
  154. package/dist/platform/object-store-r2.js.map +1 -0
  155. package/dist/platform/orchestrator-context.d.ts +20 -0
  156. package/dist/platform/orchestrator-context.d.ts.map +1 -0
  157. package/dist/platform/orchestrator-context.js +28 -0
  158. package/dist/platform/orchestrator-context.js.map +1 -0
  159. package/dist/platform/r2-types.d.ts +41 -0
  160. package/dist/platform/r2-types.d.ts.map +1 -0
  161. package/dist/platform/r2-types.js +3 -0
  162. package/dist/platform/r2-types.js.map +1 -0
  163. package/dist/platform/types.d.ts +90 -0
  164. package/dist/platform/types.d.ts.map +1 -0
  165. package/dist/platform/types.js +23 -0
  166. package/dist/platform/types.js.map +1 -0
  167. package/dist/preflight.d.ts +57 -0
  168. package/dist/preflight.d.ts.map +1 -0
  169. package/dist/preflight.js +243 -0
  170. package/dist/preflight.js.map +1 -0
  171. package/dist/presign.d.ts +5 -0
  172. package/dist/presign.d.ts.map +1 -0
  173. package/dist/presign.js +24 -0
  174. package/dist/presign.js.map +1 -0
  175. package/dist/public-id.d.ts +8 -0
  176. package/dist/public-id.d.ts.map +1 -0
  177. package/dist/public-id.js +25 -0
  178. package/dist/public-id.js.map +1 -0
  179. package/dist/render-adopt.d.ts +3 -0
  180. package/dist/render-adopt.d.ts.map +1 -0
  181. package/dist/render-adopt.js +92 -0
  182. package/dist/render-adopt.js.map +1 -0
  183. package/dist/render-log.d.ts +25 -0
  184. package/dist/render-log.d.ts.map +1 -0
  185. package/dist/render-log.js +139 -0
  186. package/dist/render-log.js.map +1 -0
  187. package/dist/render-module-config.d.ts +28 -0
  188. package/dist/render-module-config.d.ts.map +1 -0
  189. package/dist/render-module-config.js +124 -0
  190. package/dist/render-module-config.js.map +1 -0
  191. package/dist/render-mux.d.ts +18 -0
  192. package/dist/render-mux.d.ts.map +1 -0
  193. package/dist/render-mux.js +69 -0
  194. package/dist/render-mux.js.map +1 -0
  195. package/dist/render-orchestrator.d.ts +71 -0
  196. package/dist/render-orchestrator.d.ts.map +1 -0
  197. package/dist/render-orchestrator.js +376 -0
  198. package/dist/render-orchestrator.js.map +1 -0
  199. package/dist/render-sweep.d.ts +5 -0
  200. package/dist/render-sweep.d.ts.map +1 -0
  201. package/dist/render-sweep.js +62 -0
  202. package/dist/render-sweep.js.map +1 -0
  203. package/dist/renders-db.d.ts +129 -0
  204. package/dist/renders-db.d.ts.map +1 -0
  205. package/dist/renders-db.js +752 -0
  206. package/dist/renders-db.js.map +1 -0
  207. package/dist/runpod-submit.d.ts +151 -0
  208. package/dist/runpod-submit.d.ts.map +1 -0
  209. package/dist/runpod-submit.js +469 -0
  210. package/dist/runpod-submit.js.map +1 -0
  211. package/dist/runpod-types.d.ts +2 -0
  212. package/dist/runpod-types.d.ts.map +1 -0
  213. package/dist/runpod-types.js +4 -0
  214. package/dist/runpod-types.js.map +1 -0
  215. package/dist/scatter-notify.d.ts +4 -0
  216. package/dist/scatter-notify.d.ts.map +1 -0
  217. package/dist/scatter-notify.js +43 -0
  218. package/dist/scatter-notify.js.map +1 -0
  219. package/dist/scatter-orchestrator-types.d.ts +56 -0
  220. package/dist/scatter-orchestrator-types.d.ts.map +1 -0
  221. package/dist/scatter-orchestrator-types.js +3 -0
  222. package/dist/scatter-orchestrator-types.js.map +1 -0
  223. package/dist/scatter-orchestrator.d.ts +70 -0
  224. package/dist/scatter-orchestrator.d.ts.map +1 -0
  225. package/dist/scatter-orchestrator.js +614 -0
  226. package/dist/scatter-orchestrator.js.map +1 -0
  227. package/dist/scatter.d.ts +35 -0
  228. package/dist/scatter.d.ts.map +1 -0
  229. package/dist/scatter.js +171 -0
  230. package/dist/scatter.js.map +1 -0
  231. package/dist/secret-store.d.ts +5 -0
  232. package/dist/secret-store.d.ts.map +1 -0
  233. package/dist/secret-store.js +19 -0
  234. package/dist/secret-store.js.map +1 -0
  235. package/dist/shot-durations-parse.d.ts +3 -0
  236. package/dist/shot-durations-parse.d.ts.map +1 -0
  237. package/dist/shot-durations-parse.js +43 -0
  238. package/dist/shot-durations-parse.js.map +1 -0
  239. package/dist/srt.d.ts +11 -0
  240. package/dist/srt.d.ts.map +1 -0
  241. package/dist/srt.js +54 -0
  242. package/dist/srt.js.map +1 -0
  243. package/dist/storyboard-ids.d.ts +2 -0
  244. package/dist/storyboard-ids.d.ts.map +1 -0
  245. package/dist/storyboard-ids.js +12 -0
  246. package/dist/storyboard-ids.js.map +1 -0
  247. package/dist/storyboard-projects-db.d.ts +31 -0
  248. package/dist/storyboard-projects-db.d.ts.map +1 -0
  249. package/dist/storyboard-projects-db.js +153 -0
  250. package/dist/storyboard-projects-db.js.map +1 -0
  251. package/dist/storyboard-validate.d.ts +72 -0
  252. package/dist/storyboard-validate.d.ts.map +1 -0
  253. package/dist/storyboard-validate.js +595 -0
  254. package/dist/storyboard-validate.js.map +1 -0
  255. package/dist/structured-events.d.ts +6 -0
  256. package/dist/structured-events.d.ts.map +1 -0
  257. package/dist/structured-events.js +15 -0
  258. package/dist/structured-events.js.map +1 -0
  259. package/dist/tar.d.ts +9 -0
  260. package/dist/tar.d.ts.map +1 -0
  261. package/dist/tar.js +151 -0
  262. package/dist/tar.js.map +1 -0
  263. package/dist/voices.d.ts +12 -0
  264. package/dist/voices.d.ts.map +1 -0
  265. package/dist/voices.js +37 -0
  266. package/dist/voices.js.map +1 -0
  267. package/package.json +71 -0
@@ -0,0 +1,35 @@
1
+ import type { RenderSubmitArgs } from "./runpod-submit.js";
2
+ export declare function splitShots(shotIds: string[], shardCount: number): string[][];
3
+ export interface ScatterArgs {
4
+ project: string;
5
+ bundleKey: string;
6
+ qualityTier?: "draft" | "standard" | "final";
7
+ pretrainedLoras: Record<string, string>;
8
+ shotIds: string[];
9
+ shardCount: number;
10
+ renderOverrides?: Record<string, unknown>;
11
+ shotSlots?: Record<string, string[]>;
12
+ }
13
+ export declare function buildShardJobs(args: ScatterArgs): RenderSubmitArgs[];
14
+ export declare function scopePretrainedToShard(pretrainedLoras: Record<string, string>, shard: string[], shotSlots?: Record<string, string[]>): Record<string, string>;
15
+ export declare function scatterShards(args: Pick<ScatterArgs, "shotIds" | "shardCount" | "pretrainedLoras" | "shotSlots">): Array<{
16
+ shots: string[];
17
+ pretrainedLoras: Record<string, string>;
18
+ }>;
19
+ export type GatherDecision = {
20
+ kind: "finish";
21
+ } | {
22
+ kind: "waiting";
23
+ remaining: number;
24
+ } | {
25
+ kind: "failed";
26
+ reason: string;
27
+ };
28
+ export interface ShardStatus {
29
+ status: string;
30
+ shots: string[];
31
+ }
32
+ export declare function gatherDecision(present: string[], expected: string[], shards: ShardStatus[]): GatherDecision;
33
+ export declare function scatterParentJobId(token: string): string;
34
+ export declare function isScatterParentJobId(jobId: string | null | undefined): boolean;
35
+ //# sourceMappingURL=scatter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scatter.d.ts","sourceRoot":"","sources":["../src/scatter.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAa3D,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,CAc5E;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,GAAG,UAAU,GAAG,OAAO,CAAC;IAI7C,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAExC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAK1C,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CACtC;AAKD,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,GAAG,gBAAgB,EAAE,CAyBpE;AAMD,wBAAgB,sBAAsB,CACpC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACvC,KAAK,EAAE,MAAM,EAAE,EACf,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GACnC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAWxB;AAED,wBAAgB,aAAa,CAC3B,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,YAAY,GAAG,iBAAiB,GAAG,WAAW,CAAC,GAClF,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAAC,CAOrE;AAOD,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAMvC,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAqBD,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EAAE,EACjB,QAAQ,EAAE,MAAM,EAAE,EAClB,MAAM,EAAE,WAAW,EAAE,GACpB,cAAc,CA6BhB;AAOD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAE9E"}
@@ -0,0 +1,171 @@
1
+ // Distributed scatter/gather render conductor (pure core).
2
+ //
3
+ // A normal render runs every shot in one RunPod job. A *scatter* render splits
4
+ // the storyboard across N jobs that run in parallel, then *gathers* their
5
+ // per-shot clips into one MP4. The win is wall-clock: a 10-shot film that takes
6
+ // ~T on one worker takes ~T/N across N, at the same GPU-second cost.
7
+ //
8
+ // Three things make this safe and cheap, and they already exist on the backend:
9
+ // 1. `process_shot_ids` lets a job render only its slice (orchestrator.plan
10
+ // scopes scenes to it).
11
+ // 2. `pretrained_loras` lets every shard REUSE the character adapters trained
12
+ // ONCE up front (handler._stage_pretrained_loras pulls them from R2), so a
13
+ // shard never retrains, and every shot shares byte-identical identity.
14
+ // 3. `finish_offloaded` makes a shard emit per-shot clips + a manifest and
15
+ // skip the merge, so the control plane owns the single final assembly.
16
+ //
17
+ // This module is the CONDUCTOR's pure logic: how to split the shots, how to
18
+ // shape each shard's submit args, and how to decide when the gather can finish.
19
+ // No I/O, no Worker runtime: trivially unit-testable. The orchestration that
20
+ // submits jobs, writes the parent/child `renders` rows, and drives the merge
21
+ // lives in index.ts and reuses these helpers plus the gather core already in
22
+ // video-finish.ts (gatherClipPresence / finishInputFromClipKeys / runVideoFinish).
23
+ // Split an ordered shot list into `shardCount` CONTIGUOUS, balanced slices.
24
+ //
25
+ // Contiguous (not round-robin) so each shard is a coherent run of the
26
+ // storyboard: shots that chain from their predecessor (chain_from_previous)
27
+ // stay together in one job, and the gather just concatenates shards in
28
+ // storyboard order. Balanced with a front-loaded remainder, so 10 shots over 3
29
+ // shards => [4, 3, 3], never an empty trailing shard.
30
+ //
31
+ // `shardCount` is clamped to [1, shots.length]: asking for more shards than
32
+ // shots would mint empty jobs (GPU-seconds for nothing), and asking for <1 is
33
+ // just a normal single render. An empty shot list returns [].
34
+ export function splitShots(shotIds, shardCount) {
35
+ const shots = shotIds.filter((s) => typeof s === "string" && s.length > 0);
36
+ if (shots.length === 0)
37
+ return [];
38
+ const n = Math.max(1, Math.min(Math.floor(shardCount) || 1, shots.length));
39
+ const base = Math.floor(shots.length / n);
40
+ const extra = shots.length % n; // the first `extra` shards carry one more
41
+ const shards = [];
42
+ let i = 0;
43
+ for (let s = 0; s < n; s++) {
44
+ const size = base + (s < extra ? 1 : 0);
45
+ shards.push(shots.slice(i, i + size));
46
+ i += size;
47
+ }
48
+ return shards;
49
+ }
50
+ // Build one RenderSubmitArgs per shard. Each is a finish-offloaded subset render
51
+ // that reuses the pre-trained LoRAs, so it draws keyframes + i2v for ITS shots
52
+ // and writes per-shot clips to R2 without assembling anything.
53
+ export function buildShardJobs(args) {
54
+ const shards = splitShots(args.shotIds, args.shardCount);
55
+ // Explicit empty-shard guard: a shard with no shots would submit
56
+ // processShotIds: [], and the backend reads an empty process_shot_ids as
57
+ // "render the WHOLE storyboard". One stray empty shard would therefore render
58
+ // the entire film (N times over the scatter), so never emit a job for one.
59
+ // splitShots already avoids empty slices for a non-empty input; this is the
60
+ // load-bearing safety net, not a redundant check.
61
+ return shards
62
+ .filter((shard) => shard.length > 0)
63
+ .map((shard) => {
64
+ const pretrained = scopePretrainedToShard(args.pretrainedLoras, shard, args.shotSlots);
65
+ const job = {
66
+ project: args.project,
67
+ bundleKey: args.bundleKey,
68
+ qualityTier: args.qualityTier,
69
+ processShotIds: shard,
70
+ // finish_offloaded is the one raw routing flag the backend reads off the
71
+ // overrides dict (buildSubmitPayload._OVERRIDE_FLAGS). It makes the shard
72
+ // emit clips + manifest and skip the merge.
73
+ renderOverrides: { ...(args.renderOverrides ?? {}), finish_offloaded: true },
74
+ };
75
+ if (Object.keys(pretrained).length > 0)
76
+ job.pretrainedLoras = pretrained;
77
+ return job;
78
+ });
79
+ }
80
+ // Restrict the pretrained-LoRA map to the slots a shard's shots actually use.
81
+ // Falls back to the full map when we have no per-shot slot information (the safe
82
+ // default: staging a spare adapter is wasteful, dropping a needed one renders
83
+ // the wrong identity).
84
+ export function scopePretrainedToShard(pretrainedLoras, shard, shotSlots) {
85
+ if (!shotSlots)
86
+ return { ...pretrainedLoras };
87
+ const used = new Set();
88
+ for (const shotId of shard) {
89
+ for (const slot of shotSlots[shotId] ?? [])
90
+ used.add(slot);
91
+ }
92
+ const scoped = {};
93
+ for (const [slot, key] of Object.entries(pretrainedLoras)) {
94
+ if (used.has(slot))
95
+ scoped[slot] = key;
96
+ }
97
+ return scoped;
98
+ }
99
+ export function scatterShards(args) {
100
+ return splitShots(args.shotIds, args.shardCount)
101
+ .filter((shard) => shard.length > 0)
102
+ .map((shots) => ({
103
+ shots,
104
+ pretrainedLoras: scopePretrainedToShard(args.pretrainedLoras, shots, args.shotSlots),
105
+ }));
106
+ }
107
+ // A shard's terminal RunPod statuses that mean "this slice will never produce
108
+ // its clips" (so if its shots are still missing, the gather is doomed, not just
109
+ // slow). Mirrors RunpodStatus.
110
+ const SHARD_DEAD_STATUSES = new Set(["FAILED", "CANCELLED", "TIMED_OUT"]);
111
+ // Decide what the gather watcher should do, from the clip-presence signal
112
+ // (video-finish.gatherClipPresence) and the shards' last RunPod statuses.
113
+ //
114
+ // finish -> every EXPECTED shot has a clip in R2: assemble the MP4.
115
+ // failed -> a missing shot can never arrive (its owning shard is dead, or no
116
+ // shard owns it): stop waiting.
117
+ // waiting -> shots still missing but a live shard could still produce them.
118
+ //
119
+ // `expected` is the AUTHORITATIVE full storyboard shot-id set; we compute the
120
+ // missing set here from it rather than trusting a caller-supplied count, so a
121
+ // truncated expected-set can never let the gather "finish" a partial film. Finish
122
+ // requires present to cover expected in full (present superset-of expected).
123
+ //
124
+ // Dead-ness is correlated to the specific missing shots' owning shard, not the
125
+ // whole gather: a single dead shard does NOT fail shots that live shards still
126
+ // own (or that the dead shard already re-emitted into `present`). The presence
127
+ // signal leads -- a shard can be COMPLETED yet have re-emitted clips, or a
128
+ // retried shard can fill holes -- so we only call "failed" for shots that are
129
+ // missing AND unrecoverable.
130
+ export function gatherDecision(present, expected, shards) {
131
+ const expectedShots = expected.filter((s) => typeof s === "string" && s.length > 0);
132
+ // No authoritative shots means there is nothing to assemble; never silently
133
+ // "finish" an empty film.
134
+ if (expectedShots.length === 0) {
135
+ return { kind: "failed", reason: "no expected shots: nothing to gather" };
136
+ }
137
+ const presentSet = new Set(present);
138
+ const missing = expectedShots.filter((id) => !presentSet.has(id));
139
+ if (missing.length === 0)
140
+ return { kind: "finish" };
141
+ // A missing shot can still arrive only if a NON-dead shard owns it. Missing
142
+ // shots whose owning shard is dead -- or that no shard owns at all -- can never
143
+ // land, so the gather is doomed for exactly those shots.
144
+ const recoverable = new Set();
145
+ for (const shard of shards) {
146
+ if (!SHARD_DEAD_STATUSES.has(shard.status)) {
147
+ for (const shot of shard.shots)
148
+ recoverable.add(shot);
149
+ }
150
+ }
151
+ const doomed = missing.filter((id) => !recoverable.has(id));
152
+ if (doomed.length > 0) {
153
+ return {
154
+ kind: "failed",
155
+ reason: `${doomed.length} shot(s) can never arrive (owning shard dead or unassigned): ${doomed.join(", ")}`,
156
+ };
157
+ }
158
+ return { kind: "waiting", remaining: missing.length };
159
+ }
160
+ // Synthetic job_id for the scatter PARENT renders row. The parent is not a
161
+ // RunPod job (it owns N child jobs), but renders.job_id is NOT NULL UNIQUE, so
162
+ // it needs a stable, collision-free id. The caller passes a unique token (e.g.
163
+ // the first child job id, or a timestamp+slug); kept pure so tests are
164
+ // deterministic and the Worker chooses the entropy.
165
+ export function scatterParentJobId(token) {
166
+ return `scatter-${token}`;
167
+ }
168
+ export function isScatterParentJobId(jobId) {
169
+ return typeof jobId === "string" && jobId.startsWith("scatter-");
170
+ }
171
+ //# sourceMappingURL=scatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scatter.js","sourceRoot":"","sources":["../src/scatter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export interface SecretsStoreSecret {
2
+ get(): Promise<string>;
3
+ }
4
+ export declare function secretValue(s: SecretsStoreSecret | string | undefined): Promise<string>;
5
+ //# sourceMappingURL=secret-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret-store.d.ts","sourceRoot":"","sources":["../src/secret-store.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,kBAAkB;IACjC,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CACxB;AAOD,wBAAsB,WAAW,CAAC,CAAC,EAAE,kBAAkB,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAS7F"}
@@ -0,0 +1,19 @@
1
+ // Resolve a Secrets Store binding (production) or a plain string (tests / local dev) to its
2
+ // value. Returns "" if unset/unreadable so the existing "not configured" guards still fire
3
+ // FAIL CLOSED. Mirrors the per-module helper (modules/*/src/index.ts). Part of #238 (core half):
4
+ // the credential secrets move from `wrangler secret put` to declarative [[secrets_store_secrets]]
5
+ // bindings, and every read goes through here.
6
+ export async function secretValue(s) {
7
+ if (typeof s === "string")
8
+ return s;
9
+ if (!s)
10
+ return "";
11
+ try {
12
+ return await s.get();
13
+ }
14
+ catch (e) {
15
+ console.warn("secrets-store get failed: " + e.message);
16
+ return "";
17
+ }
18
+ }
19
+ //# sourceMappingURL=secret-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret-store.js","sourceRoot":"","sources":["../src/secret-store.ts"],"names":[],"mappings":"AAIA,4FAA4F;AAC5F,2FAA2F;AAC3F,iGAAiG;AACjG,kGAAkG;AAClG,8CAA8C;AAC9C,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,CAA0C;IAC1E,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,CAAC,CAAC;IACpC,IAAI,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IAClB,IAAI,CAAC;QACH,OAAO,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC;IACvB,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,CAAC,IAAI,CAAC,4BAA4B,GAAI,CAAW,CAAC,OAAO,CAAC,CAAC;QAClE,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
@@ -0,0 +1,3 @@
1
+ /** Parse beat-trimmed per-shot target_seconds from a storyboard.yaml string. */
2
+ export declare function parseShotDurations(yaml: string): Record<string, number>;
3
+ //# sourceMappingURL=shot-durations-parse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shot-durations-parse.d.ts","sourceRoot":"","sources":["../src/shot-durations-parse.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAsCvE"}
@@ -0,0 +1,43 @@
1
+ /** Parse beat-trimmed per-shot target_seconds from a storyboard.yaml string. */
2
+ export function parseShotDurations(yaml) {
3
+ const out = {};
4
+ let inScenes = false;
5
+ let idx = 0;
6
+ let curId = null;
7
+ let curTarget = null;
8
+ const flush = () => {
9
+ if (idx === 0)
10
+ return;
11
+ const shot = curId || `shot_${String(idx).padStart(2, "0")}`;
12
+ if (curTarget !== null && Number.isFinite(curTarget) && curTarget > 0) {
13
+ out[shot] = curTarget;
14
+ }
15
+ };
16
+ for (const line of yaml.split(/\r?\n/)) {
17
+ if (!inScenes) {
18
+ if (/^scenes:\s*$/.test(line))
19
+ inScenes = true;
20
+ continue;
21
+ }
22
+ if (/^ {2}-\s/.test(line)) {
23
+ flush();
24
+ idx++;
25
+ curId = null;
26
+ curTarget = null;
27
+ continue;
28
+ }
29
+ const idM = line.match(/^ {4}id:\s*"((?:[^"\\]|\\.)*)"\s*$/);
30
+ if (idM) {
31
+ curId = idM[1].replace(/\\(.)/g, "$1");
32
+ continue;
33
+ }
34
+ const tsM = line.match(/^ {4}target_seconds:\s*([0-9]+(?:\.[0-9]+)?)\s*$/);
35
+ if (tsM) {
36
+ curTarget = parseFloat(tsM[1]);
37
+ continue;
38
+ }
39
+ }
40
+ flush();
41
+ return out;
42
+ }
43
+ //# sourceMappingURL=shot-durations-parse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shot-durations-parse.js","sourceRoot":"","sources":["../src/shot-durations-parse.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,IAAI,KAAK,GAAkB,IAAI,CAAC;IAChC,IAAI,SAAS,GAAkB,IAAI,CAAC;IACpC,MAAM,KAAK,GAAG,GAAS,EAAE;QACvB,IAAI,GAAG,KAAK,CAAC;YAAE,OAAO;QACtB,MAAM,IAAI,GAAG,KAAK,IAAI,QAAQ,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;QAC7D,IAAI,SAAS,KAAK,IAAI,IAAI,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;YACtE,GAAG,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC;QACxB,CAAC;IACH,CAAC,CAAC;IACF,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QACvC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC;gBAAE,QAAQ,GAAG,IAAI,CAAC;YAC/C,SAAS;QACX,CAAC;QACD,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1B,KAAK,EAAE,CAAC;YACR,GAAG,EAAE,CAAC;YACN,KAAK,GAAG,IAAI,CAAC;YACb,SAAS,GAAG,IAAI,CAAC;YACjB,SAAS;QACX,CAAC;QACD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,oCAAoC,CAAC,CAAC;QAC7D,IAAI,GAAG,EAAE,CAAC;YACR,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;YACvC,SAAS;QACX,CAAC;QACD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,kDAAkD,CAAC,CAAC;QAC3E,IAAI,GAAG,EAAE,CAAC;YACR,SAAS,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAC/B,SAAS;QACX,CAAC;IACH,CAAC;IACD,KAAK,EAAE,CAAC;IACR,OAAO,GAAG,CAAC;AACb,CAAC"}
package/dist/srt.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ /** Parse an SRT `HH:MM:SS,mmm` timestamp to whole milliseconds (0 on a non-match, defensively). */
2
+ export declare function parseTimestamp(ts: string): number;
3
+ /** Format whole milliseconds as an SRT `HH:MM:SS,mmm` timestamp, rolling seconds into minutes and
4
+ * minutes into hours. A negative input clamps to zero; hours are not capped (SubRip zero-pads to 2 but
5
+ * tolerates more, and a very long film can in theory exceed 99h). */
6
+ export declare function formatTimestamp(ms: number): string;
7
+ /** Shift every cue timestamp in an SRT document by `offsetSeconds`. A zero (or non-finite) offset returns
8
+ * the input UNCHANGED (the no-op the core relies on when there is no title card / credits-only). Only
9
+ * lines carrying the `-->` cue arrow are rewritten, so indices and caption text are preserved exactly. */
10
+ export declare function retimeSrt(srt: string, offsetSeconds: number): string;
11
+ //# sourceMappingURL=srt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"srt.d.ts","sourceRoot":"","sources":["../src/srt.ts"],"names":[],"mappings":"AAeA,mGAAmG;AACnG,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAIjD;AAED;;sEAEsE;AACtE,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CASlD;AAED;;2GAE2G;AAC3G,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CAapE"}
package/dist/srt.js ADDED
@@ -0,0 +1,54 @@
1
+ // SRT (SubRip) sidecar re-timer for the film.finish chain (#663).
2
+ //
3
+ // The subtitle film.finish module (ui.order 5) writes its soft .srt sidecar against the assembled
4
+ // 0-based timeline: each cue starts at the film`s real start, because the timed cues the core hands it
5
+ // are measured from the assembled film`s 0 (see src/captions.ts). A LATER film.finish step -- film-titles
6
+ // (ui.order 10) -- then PREPENDS an opening title card, shifting the FINAL film forward by title_seconds.
7
+ // The BURNED captions ride the video through that prepend (they are part of the frames), but the soft
8
+ // .srt sidecar is a SEPARATE artifact and does NOT, so against the final film every cue runs early by the
9
+ // prepend duration. The core re-times the sidecar AFTER the chain by that known offset (module-reported
10
+ // prepend_seconds), so the soft subtitles line up with the delivered film.
11
+ //
12
+ // Pure string transform, no I/O: parse each `HH:MM:SS,mmm --> HH:MM:SS,mmm` cue line, add the offset
13
+ // (a negative result clamps to 0), reformat with minute/hour rollover. Everything else (cue indices,
14
+ // caption text, blank lines, any trailing position extension on the cue line) passes through verbatim.
15
+ /** Parse an SRT `HH:MM:SS,mmm` timestamp to whole milliseconds (0 on a non-match, defensively). */
16
+ export function parseTimestamp(ts) {
17
+ const m = /(\d{1,2}):(\d{2}):(\d{2}),(\d{3})/.exec(ts);
18
+ if (!m)
19
+ return 0;
20
+ return Number(m[1]) * 3600000 + Number(m[2]) * 60000 + Number(m[3]) * 1000 + Number(m[4]);
21
+ }
22
+ /** Format whole milliseconds as an SRT `HH:MM:SS,mmm` timestamp, rolling seconds into minutes and
23
+ * minutes into hours. A negative input clamps to zero; hours are not capped (SubRip zero-pads to 2 but
24
+ * tolerates more, and a very long film can in theory exceed 99h). */
25
+ export function formatTimestamp(ms) {
26
+ const totalMs = Math.max(0, Math.round(ms));
27
+ const msPart = totalMs % 1000;
28
+ const totalSec = Math.floor(totalMs / 1000);
29
+ const s = totalSec % 60;
30
+ const m = Math.floor(totalSec / 60) % 60;
31
+ const h = Math.floor(totalSec / 3600);
32
+ const pad = (n, w = 2) => String(n).padStart(w, "0");
33
+ return `${pad(h)}:${pad(m)}:${pad(s)},${pad(msPart, 3)}`;
34
+ }
35
+ /** Shift every cue timestamp in an SRT document by `offsetSeconds`. A zero (or non-finite) offset returns
36
+ * the input UNCHANGED (the no-op the core relies on when there is no title card / credits-only). Only
37
+ * lines carrying the `-->` cue arrow are rewritten, so indices and caption text are preserved exactly. */
38
+ export function retimeSrt(srt, offsetSeconds) {
39
+ if (!Number.isFinite(offsetSeconds) || offsetSeconds === 0)
40
+ return srt;
41
+ const offsetMs = Math.round(offsetSeconds * 1000);
42
+ return srt
43
+ .split("\n")
44
+ .map((line) => {
45
+ if (!line.includes("-->"))
46
+ return line;
47
+ return line.replace(/(\d{1,2}):(\d{2}):(\d{2}),(\d{3})/g, (_m, h, mm, ss, mmm) => {
48
+ const base = Number(h) * 3600000 + Number(mm) * 60000 + Number(ss) * 1000 + Number(mmm);
49
+ return formatTimestamp(base + offsetMs);
50
+ });
51
+ })
52
+ .join("\n");
53
+ }
54
+ //# sourceMappingURL=srt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"srt.js","sourceRoot":"","sources":["../src/srt.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,EAAE;AACF,kGAAkG;AAClG,uGAAuG;AACvG,0GAA0G;AAC1G,0GAA0G;AAC1G,sGAAsG;AACtG,0GAA0G;AAC1G,wGAAwG;AACxG,2EAA2E;AAC3E,EAAE;AACF,qGAAqG;AACrG,qGAAqG;AACrG,uGAAuG;AAEvG,mGAAmG;AACnG,MAAM,UAAU,cAAc,CAAC,EAAU;IACvC,MAAM,CAAC,GAAG,mCAAmC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACvD,IAAI,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC;IACjB,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC5F,CAAC;AAED;;sEAEsE;AACtE,MAAM,UAAU,eAAe,CAAC,EAAU;IACxC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5C,MAAM,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;IAC9B,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IAC5C,MAAM,CAAC,GAAG,QAAQ,GAAG,EAAE,CAAC;IACxB,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC;IACzC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;IACtC,MAAM,GAAG,GAAG,CAAC,CAAS,EAAE,CAAC,GAAG,CAAC,EAAU,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACrE,OAAO,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC;AAC3D,CAAC;AAED;;2GAE2G;AAC3G,MAAM,UAAU,SAAS,CAAC,GAAW,EAAE,aAAqB;IAC1D,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,aAAa,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC;IACvE,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IAClD,OAAO,GAAG;SACP,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACZ,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QACvC,OAAO,IAAI,CAAC,OAAO,CAAC,oCAAoC,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE;YAC/E,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,OAAO,GAAG,MAAM,CAAC,EAAE,CAAC,GAAG,KAAK,GAAG,MAAM,CAAC,EAAE,CAAC,GAAG,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;YACxF,OAAO,eAAe,CAAC,IAAI,GAAG,QAAQ,CAAC,CAAC;QAC1C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function coerceShotId(rawId: string | undefined, index: number): string;
2
+ //# sourceMappingURL=storyboard-ids.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storyboard-ids.d.ts","sourceRoot":"","sources":["../src/storyboard-ids.ts"],"names":[],"mappings":"AAIA,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAM7E"}
@@ -0,0 +1,12 @@
1
+ // Storyboard shot-id coercion (pure). Full validator: storyboard-validate.ts.
2
+ const SHOT_ID_RE = /^shot_\d+$/;
3
+ export function coerceShotId(rawId, index) {
4
+ const desired = `shot_${String(index + 1).padStart(2, "0")}`;
5
+ if (typeof rawId !== "string")
6
+ return desired;
7
+ const trimmed = rawId.trim();
8
+ if (trimmed.length === 0)
9
+ return desired;
10
+ return SHOT_ID_RE.test(trimmed) ? trimmed : desired;
11
+ }
12
+ //# sourceMappingURL=storyboard-ids.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storyboard-ids.js","sourceRoot":"","sources":["../src/storyboard-ids.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAE9E,MAAM,UAAU,GAAG,YAAY,CAAC;AAEhC,MAAM,UAAU,YAAY,CAAC,KAAyB,EAAE,KAAa;IACnE,MAAM,OAAO,GAAG,QAAQ,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;IAC7D,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC;IAC9C,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,OAAO,CAAC;IACzC,OAAO,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;AACtD,CAAC"}
@@ -0,0 +1,31 @@
1
+ import type { DbEnv } from "./db-env.js";
2
+ export interface StoryboardProject {
3
+ id: number;
4
+ public_id: string;
5
+ slug: string;
6
+ name: string;
7
+ prefs: Record<string, unknown>;
8
+ last_storyboard: unknown | null;
9
+ created_at: string;
10
+ updated_at: string;
11
+ }
12
+ export type PublicStoryboardProject = Omit<StoryboardProject, "id" | "public_id"> & {
13
+ id: string;
14
+ };
15
+ export declare function toPublicProject(row: StoryboardProject): PublicStoryboardProject;
16
+ export declare function slugifyProject(name: string): string;
17
+ export declare function allocateProjectSlug(env: DbEnv, base: string): Promise<string>;
18
+ export declare function listProjects(env: DbEnv): Promise<StoryboardProject[]>;
19
+ export declare function getProjectIdByPublicId(env: DbEnv, publicId: string): Promise<number | null>;
20
+ export declare function getProjectById(env: DbEnv, id: number): Promise<StoryboardProject | null>;
21
+ export declare function createProject(env: DbEnv, input: {
22
+ name: string;
23
+ prefs?: Record<string, unknown>;
24
+ }): Promise<StoryboardProject>;
25
+ export declare function updateProjectMeta(env: DbEnv, id: number, patch: {
26
+ name?: string;
27
+ prefs?: Record<string, unknown>;
28
+ }): Promise<StoryboardProject | null>;
29
+ export declare function setLastStoryboard(env: DbEnv, id: number, storyboard: unknown): Promise<StoryboardProject | null>;
30
+ export declare function deleteProject(env: DbEnv, id: number): Promise<StoryboardProject | null>;
31
+ //# sourceMappingURL=storyboard-projects-db.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storyboard-projects-db.d.ts","sourceRoot":"","sources":["../src/storyboard-projects-db.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAGzC,MAAM,WAAW,iBAAiB;IAEhC,EAAE,EAAE,MAAM,CAAC;IAEX,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,eAAe,EAAE,OAAO,GAAG,IAAI,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAwCD,MAAM,MAAM,uBAAuB,GAAG,IAAI,CAAC,iBAAiB,EAAE,IAAI,GAAG,WAAW,CAAC,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnG,wBAAgB,eAAe,CAAC,GAAG,EAAE,iBAAiB,GAAG,uBAAuB,CAG/E;AAID,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CASnD;AAED,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAcnF;AAMD,wBAAsB,YAAY,CAAC,GAAG,EAAE,KAAK,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAW3E;AAID,wBAAsB,sBAAsB,CAAC,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAOjG;AAED,wBAAsB,cAAc,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAW9F;AAED,wBAAsB,aAAa,CACjC,GAAG,EAAE,KAAK,EACV,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GACvD,OAAO,CAAC,iBAAiB,CAAC,CAc5B;AAED,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,KAAK,EACV,EAAE,EAAE,MAAM,EACV,KAAK,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GACxD,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAyBnC;AAED,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,KAAK,EACV,EAAE,EAAE,MAAM,EACV,UAAU,EAAE,OAAO,GAClB,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAYnC;AAED,wBAAsB,aAAa,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAS7F"}
@@ -0,0 +1,153 @@
1
+ // D1 helpers for persisted storyboard projects (v0.53.0). One row per project; holds a free-form
2
+ // prefs object and a snapshot of the last saved storyboard so the planner can resume across
3
+ // sessions and devices.
4
+ //
5
+ // Vivijure is a SINGLE-OPERATOR studio: there is no per-user scoping. Slugs are globally unique;
6
+ // every query is unscoped (the legacy identity column was removed in the identity strip; memory:
7
+ // vivijure-user-email-strip). Mirrors src/cast-db.ts shape: pure-row interface, slug allocation
8
+ // bounded at 200 attempts.
9
+ import { newPublicId } from "./public-id.js";
10
+ function parseJson(raw, fallback) {
11
+ if (!raw)
12
+ return fallback;
13
+ try {
14
+ return JSON.parse(raw);
15
+ }
16
+ catch {
17
+ return fallback;
18
+ }
19
+ }
20
+ function rowToProject(row) {
21
+ return {
22
+ id: row.id,
23
+ public_id: row.public_id,
24
+ slug: row.slug,
25
+ name: row.name,
26
+ prefs: parseJson(row.prefs_json, {}),
27
+ last_storyboard: row.last_storyboard_json
28
+ ? parseJson(row.last_storyboard_json, null)
29
+ : null,
30
+ created_at: row.created_at,
31
+ updated_at: row.updated_at,
32
+ };
33
+ }
34
+ export function toPublicProject(row) {
35
+ const { id: _internalId, public_id, ...rest } = row;
36
+ return { ...rest, id: public_id };
37
+ }
38
+ // URL-safe slug from a display name. Empty / all-punctuation falls
39
+ // back to "project". Matches the planner-side slug rules.
40
+ export function slugifyProject(name) {
41
+ const s = name
42
+ .toLowerCase()
43
+ .normalize("NFKD")
44
+ .replace(/[^a-z0-9\s-]+/g, "")
45
+ .trim()
46
+ .replace(/[\s-]+/g, "-")
47
+ .replace(/^-+|-+$/g, "");
48
+ return s || "project";
49
+ }
50
+ export async function allocateProjectSlug(env, base) {
51
+ let candidate = base;
52
+ let suffix = 2;
53
+ while (suffix < 200) {
54
+ const existing = await env.DB.prepare(`SELECT id FROM storyboard_projects WHERE slug = ? LIMIT 1`)
55
+ .bind(candidate)
56
+ .first();
57
+ if (!existing)
58
+ return candidate;
59
+ candidate = `${base}-${suffix}`;
60
+ suffix++;
61
+ }
62
+ throw new Error(`Could not allocate project slug after 200 attempts (base='${base}')`);
63
+ }
64
+ // Bound the project list so it can never scan unboundedly (issue #12). Generous, so the
65
+ // newest-first list is effectively complete while the query stays capped.
66
+ const PROJECT_LIST_LIMIT = 500;
67
+ export async function listProjects(env) {
68
+ const result = await env.DB.prepare(`SELECT id, public_id, slug, name, prefs_json, last_storyboard_json,
69
+ created_at, updated_at
70
+ FROM storyboard_projects
71
+ ORDER BY created_at DESC
72
+ LIMIT ?`)
73
+ .bind(PROJECT_LIST_LIMIT)
74
+ .all();
75
+ return (result.results || []).map(rowToProject);
76
+ }
77
+ // Resolve an opaque public id to the internal integer PK (the :id route + ?project_id boundary).
78
+ // Null when no project carries that public_id -- a bare integer matches nothing, so callers 404.
79
+ export async function getProjectIdByPublicId(env, publicId) {
80
+ const row = await env.DB.prepare(`SELECT id FROM storyboard_projects WHERE public_id = ? LIMIT 1`)
81
+ .bind(publicId)
82
+ .first();
83
+ return row ? Number(row.id) : null;
84
+ }
85
+ export async function getProjectById(env, id) {
86
+ const row = await env.DB.prepare(`SELECT id, public_id, slug, name, prefs_json, last_storyboard_json,
87
+ created_at, updated_at
88
+ FROM storyboard_projects
89
+ WHERE id = ?
90
+ LIMIT 1`)
91
+ .bind(id)
92
+ .first();
93
+ return row ? rowToProject(row) : null;
94
+ }
95
+ export async function createProject(env, input) {
96
+ const baseSlug = slugifyProject(input.name);
97
+ const slug = await allocateProjectSlug(env, baseSlug);
98
+ const prefsJson = JSON.stringify(input.prefs ?? {});
99
+ const row = await env.DB.prepare(`INSERT INTO storyboard_projects (public_id, slug, name, prefs_json)
100
+ VALUES (?, ?, ?, ?)
101
+ RETURNING id, public_id, slug, name, prefs_json, last_storyboard_json,
102
+ created_at, updated_at`)
103
+ .bind(newPublicId(), slug, input.name, prefsJson)
104
+ .first();
105
+ if (!row)
106
+ throw new Error("createProject: INSERT...RETURNING produced no row");
107
+ return rowToProject(row);
108
+ }
109
+ export async function updateProjectMeta(env, id, patch) {
110
+ const fields = [];
111
+ const values = [];
112
+ if (patch.name !== undefined) {
113
+ fields.push("name = ?");
114
+ values.push(patch.name);
115
+ }
116
+ if (patch.prefs !== undefined) {
117
+ fields.push("prefs_json = ?");
118
+ values.push(JSON.stringify(patch.prefs));
119
+ }
120
+ if (fields.length === 0) {
121
+ return getProjectById(env, id);
122
+ }
123
+ fields.push("updated_at = datetime('now')");
124
+ values.push(id);
125
+ const row = await env.DB.prepare(`UPDATE storyboard_projects SET ${fields.join(", ")}
126
+ WHERE id = ?
127
+ RETURNING id, public_id, slug, name, prefs_json, last_storyboard_json,
128
+ created_at, updated_at`)
129
+ .bind(...values)
130
+ .first();
131
+ return row ? rowToProject(row) : null;
132
+ }
133
+ export async function setLastStoryboard(env, id, storyboard) {
134
+ const sbJson = JSON.stringify(storyboard);
135
+ const row = await env.DB.prepare(`UPDATE storyboard_projects
136
+ SET last_storyboard_json = ?, updated_at = datetime('now')
137
+ WHERE id = ?
138
+ RETURNING id, public_id, slug, name, prefs_json, last_storyboard_json,
139
+ created_at, updated_at`)
140
+ .bind(sbJson, id)
141
+ .first();
142
+ return row ? rowToProject(row) : null;
143
+ }
144
+ export async function deleteProject(env, id) {
145
+ const cur = await getProjectById(env, id);
146
+ if (!cur)
147
+ return null;
148
+ await env.DB.prepare(`DELETE FROM storyboard_projects WHERE id = ?`)
149
+ .bind(id)
150
+ .run();
151
+ return cur;
152
+ }
153
+ //# sourceMappingURL=storyboard-projects-db.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storyboard-projects-db.js","sourceRoot":"","sources":["../src/storyboard-projects-db.ts"],"names":[],"mappings":"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"}