venice-video-harness 2.4.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 (264) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/LICENSE +21 -0
  3. package/README.md +598 -0
  4. package/dist/assembly/manifest-builder.d.ts +8 -0
  5. package/dist/assembly/manifest-builder.d.ts.map +1 -0
  6. package/dist/assembly/manifest-builder.js +196 -0
  7. package/dist/assembly/manifest-builder.js.map +1 -0
  8. package/dist/assembly/remotion-scaffold.d.ts +14 -0
  9. package/dist/assembly/remotion-scaffold.d.ts.map +1 -0
  10. package/dist/assembly/remotion-scaffold.js +292 -0
  11. package/dist/assembly/remotion-scaffold.js.map +1 -0
  12. package/dist/assembly/types.d.ts +67 -0
  13. package/dist/assembly/types.d.ts.map +1 -0
  14. package/dist/assembly/types.js +5 -0
  15. package/dist/assembly/types.js.map +1 -0
  16. package/dist/characters/describer.d.ts +32 -0
  17. package/dist/characters/describer.d.ts.map +1 -0
  18. package/dist/characters/describer.js +309 -0
  19. package/dist/characters/describer.js.map +1 -0
  20. package/dist/characters/extractor.d.ts +27 -0
  21. package/dist/characters/extractor.d.ts.map +1 -0
  22. package/dist/characters/extractor.js +274 -0
  23. package/dist/characters/extractor.js.map +1 -0
  24. package/dist/characters/reference-manager.d.ts +95 -0
  25. package/dist/characters/reference-manager.d.ts.map +1 -0
  26. package/dist/characters/reference-manager.js +346 -0
  27. package/dist/characters/reference-manager.js.map +1 -0
  28. package/dist/cli.d.ts +3 -0
  29. package/dist/cli.d.ts.map +1 -0
  30. package/dist/cli.js +310 -0
  31. package/dist/cli.js.map +1 -0
  32. package/dist/config.d.ts +22 -0
  33. package/dist/config.d.ts.map +1 -0
  34. package/dist/config.js +66 -0
  35. package/dist/config.js.map +1 -0
  36. package/dist/editing/aligner.d.ts +44 -0
  37. package/dist/editing/aligner.d.ts.map +1 -0
  38. package/dist/editing/aligner.js +201 -0
  39. package/dist/editing/aligner.js.map +1 -0
  40. package/dist/editing/edl.d.ts +45 -0
  41. package/dist/editing/edl.d.ts.map +1 -0
  42. package/dist/editing/edl.js +133 -0
  43. package/dist/editing/edl.js.map +1 -0
  44. package/dist/editing/overlays.d.ts +97 -0
  45. package/dist/editing/overlays.d.ts.map +1 -0
  46. package/dist/editing/overlays.js +41 -0
  47. package/dist/editing/overlays.js.map +1 -0
  48. package/dist/editing/packer.d.ts +52 -0
  49. package/dist/editing/packer.d.ts.map +1 -0
  50. package/dist/editing/packer.js +170 -0
  51. package/dist/editing/packer.js.map +1 -0
  52. package/dist/editing/providers/whisper-cpp.d.ts +32 -0
  53. package/dist/editing/providers/whisper-cpp.d.ts.map +1 -0
  54. package/dist/editing/providers/whisper-cpp.js +239 -0
  55. package/dist/editing/providers/whisper-cpp.js.map +1 -0
  56. package/dist/editing/render.d.ts +36 -0
  57. package/dist/editing/render.d.ts.map +1 -0
  58. package/dist/editing/render.js +227 -0
  59. package/dist/editing/render.js.map +1 -0
  60. package/dist/editing/self-eval.d.ts +48 -0
  61. package/dist/editing/self-eval.d.ts.map +1 -0
  62. package/dist/editing/self-eval.js +293 -0
  63. package/dist/editing/self-eval.js.map +1 -0
  64. package/dist/editing/silence.d.ts +76 -0
  65. package/dist/editing/silence.d.ts.map +1 -0
  66. package/dist/editing/silence.js +189 -0
  67. package/dist/editing/silence.js.map +1 -0
  68. package/dist/editing/types.d.ts +178 -0
  69. package/dist/editing/types.d.ts.map +1 -0
  70. package/dist/editing/types.js +13 -0
  71. package/dist/editing/types.js.map +1 -0
  72. package/dist/index.d.ts +6 -0
  73. package/dist/index.d.ts.map +1 -0
  74. package/dist/index.js +5 -0
  75. package/dist/index.js.map +1 -0
  76. package/dist/interactive.d.ts +12 -0
  77. package/dist/interactive.d.ts.map +1 -0
  78. package/dist/interactive.js +78 -0
  79. package/dist/interactive.js.map +1 -0
  80. package/dist/mini-drama/assembler.d.ts +61 -0
  81. package/dist/mini-drama/assembler.d.ts.map +1 -0
  82. package/dist/mini-drama/assembler.js +466 -0
  83. package/dist/mini-drama/assembler.js.map +1 -0
  84. package/dist/mini-drama/audio-mix.d.ts +65 -0
  85. package/dist/mini-drama/audio-mix.d.ts.map +1 -0
  86. package/dist/mini-drama/audio-mix.js +118 -0
  87. package/dist/mini-drama/audio-mix.js.map +1 -0
  88. package/dist/mini-drama/cli.d.ts +3 -0
  89. package/dist/mini-drama/cli.d.ts.map +1 -0
  90. package/dist/mini-drama/cli.js +2869 -0
  91. package/dist/mini-drama/cli.js.map +1 -0
  92. package/dist/mini-drama/fcpxml-export.d.ts +2 -0
  93. package/dist/mini-drama/fcpxml-export.d.ts.map +1 -0
  94. package/dist/mini-drama/fcpxml-export.js +5 -0
  95. package/dist/mini-drama/fcpxml-export.js.map +1 -0
  96. package/dist/mini-drama/generation-planner.d.ts +24 -0
  97. package/dist/mini-drama/generation-planner.d.ts.map +1 -0
  98. package/dist/mini-drama/generation-planner.js +304 -0
  99. package/dist/mini-drama/generation-planner.js.map +1 -0
  100. package/dist/mini-drama/location-generator.d.ts +23 -0
  101. package/dist/mini-drama/location-generator.d.ts.map +1 -0
  102. package/dist/mini-drama/location-generator.js +144 -0
  103. package/dist/mini-drama/location-generator.js.map +1 -0
  104. package/dist/mini-drama/music-cues.d.ts +96 -0
  105. package/dist/mini-drama/music-cues.d.ts.map +1 -0
  106. package/dist/mini-drama/music-cues.js +332 -0
  107. package/dist/mini-drama/music-cues.js.map +1 -0
  108. package/dist/mini-drama/panel-fixer.d.ts +12 -0
  109. package/dist/mini-drama/panel-fixer.d.ts.map +1 -0
  110. package/dist/mini-drama/panel-fixer.js +319 -0
  111. package/dist/mini-drama/panel-fixer.js.map +1 -0
  112. package/dist/mini-drama/prompt-builder.d.ts +149 -0
  113. package/dist/mini-drama/prompt-builder.d.ts.map +1 -0
  114. package/dist/mini-drama/prompt-builder.js +722 -0
  115. package/dist/mini-drama/prompt-builder.js.map +1 -0
  116. package/dist/mini-drama/reference-slots.d.ts +31 -0
  117. package/dist/mini-drama/reference-slots.d.ts.map +1 -0
  118. package/dist/mini-drama/reference-slots.js +161 -0
  119. package/dist/mini-drama/reference-slots.js.map +1 -0
  120. package/dist/mini-drama/shot-paths.d.ts +77 -0
  121. package/dist/mini-drama/shot-paths.d.ts.map +1 -0
  122. package/dist/mini-drama/shot-paths.js +101 -0
  123. package/dist/mini-drama/shot-paths.js.map +1 -0
  124. package/dist/mini-drama/storyboard-reference-generator.d.ts +47 -0
  125. package/dist/mini-drama/storyboard-reference-generator.d.ts.map +1 -0
  126. package/dist/mini-drama/storyboard-reference-generator.js +293 -0
  127. package/dist/mini-drama/storyboard-reference-generator.js.map +1 -0
  128. package/dist/mini-drama/subtitle-generator.d.ts +12 -0
  129. package/dist/mini-drama/subtitle-generator.d.ts.map +1 -0
  130. package/dist/mini-drama/subtitle-generator.js +139 -0
  131. package/dist/mini-drama/subtitle-generator.js.map +1 -0
  132. package/dist/mini-drama/timeline-export/davinci-fcpxml.d.ts +3 -0
  133. package/dist/mini-drama/timeline-export/davinci-fcpxml.d.ts.map +1 -0
  134. package/dist/mini-drama/timeline-export/davinci-fcpxml.js +150 -0
  135. package/dist/mini-drama/timeline-export/davinci-fcpxml.js.map +1 -0
  136. package/dist/mini-drama/timeline-export/fcpxml.d.ts +11 -0
  137. package/dist/mini-drama/timeline-export/fcpxml.d.ts.map +1 -0
  138. package/dist/mini-drama/timeline-export/fcpxml.js +140 -0
  139. package/dist/mini-drama/timeline-export/fcpxml.js.map +1 -0
  140. package/dist/mini-drama/timeline-export/index.d.ts +13 -0
  141. package/dist/mini-drama/timeline-export/index.d.ts.map +1 -0
  142. package/dist/mini-drama/timeline-export/index.js +26 -0
  143. package/dist/mini-drama/timeline-export/index.js.map +1 -0
  144. package/dist/mini-drama/timeline-export/premiere-xmeml.d.ts +3 -0
  145. package/dist/mini-drama/timeline-export/premiere-xmeml.d.ts.map +1 -0
  146. package/dist/mini-drama/timeline-export/premiere-xmeml.js +211 -0
  147. package/dist/mini-drama/timeline-export/premiere-xmeml.js.map +1 -0
  148. package/dist/mini-drama/timeline-export/probe.d.ts +53 -0
  149. package/dist/mini-drama/timeline-export/probe.d.ts.map +1 -0
  150. package/dist/mini-drama/timeline-export/probe.js +89 -0
  151. package/dist/mini-drama/timeline-export/probe.js.map +1 -0
  152. package/dist/mini-drama/timeline-export/types.d.ts +57 -0
  153. package/dist/mini-drama/timeline-export/types.d.ts.map +1 -0
  154. package/dist/mini-drama/timeline-export/types.js +14 -0
  155. package/dist/mini-drama/timeline-export/types.js.map +1 -0
  156. package/dist/mini-drama/video-generator.d.ts +22 -0
  157. package/dist/mini-drama/video-generator.d.ts.map +1 -0
  158. package/dist/mini-drama/video-generator.js +1305 -0
  159. package/dist/mini-drama/video-generator.js.map +1 -0
  160. package/dist/mini-drama/voice-reference.d.ts +42 -0
  161. package/dist/mini-drama/voice-reference.d.ts.map +1 -0
  162. package/dist/mini-drama/voice-reference.js +170 -0
  163. package/dist/mini-drama/voice-reference.js.map +1 -0
  164. package/dist/output/html-renderer.d.ts +2 -0
  165. package/dist/output/html-renderer.d.ts.map +1 -0
  166. package/dist/output/html-renderer.js +37 -0
  167. package/dist/output/html-renderer.js.map +1 -0
  168. package/dist/parsers/fountain-parser.d.ts +34 -0
  169. package/dist/parsers/fountain-parser.d.ts.map +1 -0
  170. package/dist/parsers/fountain-parser.js +71 -0
  171. package/dist/parsers/fountain-parser.js.map +1 -0
  172. package/dist/parsers/pdf-parser.d.ts +24 -0
  173. package/dist/parsers/pdf-parser.d.ts.map +1 -0
  174. package/dist/parsers/pdf-parser.js +237 -0
  175. package/dist/parsers/pdf-parser.js.map +1 -0
  176. package/dist/parsers/scene-extractor.d.ts +41 -0
  177. package/dist/parsers/scene-extractor.d.ts.map +1 -0
  178. package/dist/parsers/scene-extractor.js +400 -0
  179. package/dist/parsers/scene-extractor.js.map +1 -0
  180. package/dist/series/manager.d.ts +64 -0
  181. package/dist/series/manager.d.ts.map +1 -0
  182. package/dist/series/manager.js +282 -0
  183. package/dist/series/manager.js.map +1 -0
  184. package/dist/series/types.d.ts +641 -0
  185. package/dist/series/types.d.ts.map +1 -0
  186. package/dist/series/types.js +304 -0
  187. package/dist/series/types.js.map +1 -0
  188. package/dist/storyboard/assembler.d.ts +134 -0
  189. package/dist/storyboard/assembler.d.ts.map +1 -0
  190. package/dist/storyboard/assembler.js +319 -0
  191. package/dist/storyboard/assembler.js.map +1 -0
  192. package/dist/storyboard/prompt-builder.d.ts +113 -0
  193. package/dist/storyboard/prompt-builder.d.ts.map +1 -0
  194. package/dist/storyboard/prompt-builder.js +578 -0
  195. package/dist/storyboard/prompt-builder.js.map +1 -0
  196. package/dist/storyboard/shot-planner.d.ts +34 -0
  197. package/dist/storyboard/shot-planner.d.ts.map +1 -0
  198. package/dist/storyboard/shot-planner.js +446 -0
  199. package/dist/storyboard/shot-planner.js.map +1 -0
  200. package/dist/user-config.d.ts +15 -0
  201. package/dist/user-config.d.ts.map +1 -0
  202. package/dist/user-config.js +103 -0
  203. package/dist/user-config.js.map +1 -0
  204. package/dist/venice/audio-preflight.d.ts +80 -0
  205. package/dist/venice/audio-preflight.d.ts.map +1 -0
  206. package/dist/venice/audio-preflight.js +142 -0
  207. package/dist/venice/audio-preflight.js.map +1 -0
  208. package/dist/venice/audio.d.ts +88 -0
  209. package/dist/venice/audio.d.ts.map +1 -0
  210. package/dist/venice/audio.js +234 -0
  211. package/dist/venice/audio.js.map +1 -0
  212. package/dist/venice/client.d.ts +86 -0
  213. package/dist/venice/client.d.ts.map +1 -0
  214. package/dist/venice/client.js +375 -0
  215. package/dist/venice/client.js.map +1 -0
  216. package/dist/venice/edit.d.ts +57 -0
  217. package/dist/venice/edit.d.ts.map +1 -0
  218. package/dist/venice/edit.js +100 -0
  219. package/dist/venice/edit.js.map +1 -0
  220. package/dist/venice/generate.d.ts +47 -0
  221. package/dist/venice/generate.d.ts.map +1 -0
  222. package/dist/venice/generate.js +150 -0
  223. package/dist/venice/generate.js.map +1 -0
  224. package/dist/venice/image-bytes.d.ts +26 -0
  225. package/dist/venice/image-bytes.d.ts.map +1 -0
  226. package/dist/venice/image-bytes.js +101 -0
  227. package/dist/venice/image-bytes.js.map +1 -0
  228. package/dist/venice/models.d.ts +143 -0
  229. package/dist/venice/models.d.ts.map +1 -0
  230. package/dist/venice/models.js +984 -0
  231. package/dist/venice/models.js.map +1 -0
  232. package/dist/venice/multi-edit.d.ts +40 -0
  233. package/dist/venice/multi-edit.d.ts.map +1 -0
  234. package/dist/venice/multi-edit.js +97 -0
  235. package/dist/venice/multi-edit.js.map +1 -0
  236. package/dist/venice/provenance.d.ts +66 -0
  237. package/dist/venice/provenance.d.ts.map +1 -0
  238. package/dist/venice/provenance.js +151 -0
  239. package/dist/venice/provenance.js.map +1 -0
  240. package/dist/venice/recipe.d.ts +69 -0
  241. package/dist/venice/recipe.d.ts.map +1 -0
  242. package/dist/venice/recipe.js +103 -0
  243. package/dist/venice/recipe.js.map +1 -0
  244. package/dist/venice/rejection.d.ts +63 -0
  245. package/dist/venice/rejection.d.ts.map +1 -0
  246. package/dist/venice/rejection.js +111 -0
  247. package/dist/venice/rejection.js.map +1 -0
  248. package/dist/venice/seedance-preflight.d.ts +38 -0
  249. package/dist/venice/seedance-preflight.d.ts.map +1 -0
  250. package/dist/venice/seedance-preflight.js +29 -0
  251. package/dist/venice/seedance-preflight.js.map +1 -0
  252. package/dist/venice/types.d.ts +212 -0
  253. package/dist/venice/types.d.ts.map +1 -0
  254. package/dist/venice/types.js +9 -0
  255. package/dist/venice/types.js.map +1 -0
  256. package/dist/venice/video.d.ts +77 -0
  257. package/dist/venice/video.d.ts.map +1 -0
  258. package/dist/venice/video.js +163 -0
  259. package/dist/venice/video.js.map +1 -0
  260. package/dist/venice/voices.d.ts +40 -0
  261. package/dist/venice/voices.d.ts.map +1 -0
  262. package/dist/venice/voices.js +194 -0
  263. package/dist/venice/voices.js.map +1 -0
  264. package/package.json +79 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generation-planner.js","sourceRoot":"","sources":["../../src/mini-drama/generation-planner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { VeniceClient } from '../venice/client.js';
2
+ import type { Location, SeriesState } from '../series/types.js';
3
+ export declare const LOCATION_ANGLES: readonly ["wide", "medium", "detail"];
4
+ export type LocationAngle = (typeof LOCATION_ANGLES)[number];
5
+ export interface GenerateLocationReferencesOptions {
6
+ /** Override the image-generation model (default nano-banana-pro). */
7
+ model?: string;
8
+ /** cfg_scale (default 10). */
9
+ cfgScale?: number;
10
+ /** Regenerate angles that already exist on disk. */
11
+ force?: boolean;
12
+ }
13
+ /**
14
+ * Generate the three reference angles for a location. Writes
15
+ * `locations/<slug>/{wide,medium,detail}.png` plus per-angle `.prompt.json`
16
+ * sidecars, provenance (hasFace:false), and recipe passes. Returns the paths
17
+ * that were (re)generated.
18
+ */
19
+ export declare function generateLocationReferences(client: VeniceClient, series: SeriesState, location: Location, options?: GenerateLocationReferencesOptions): Promise<{
20
+ generated: string[];
21
+ skipped: string[];
22
+ }>;
23
+ //# sourceMappingURL=location-generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"location-generator.d.ts","sourceRoot":"","sources":["../../src/mini-drama/location-generator.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAQhE,eAAO,MAAM,eAAe,uCAAwC,CAAC;AACrE,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAoB7D,MAAM,WAAW,iCAAiC;IAChD,qEAAqE;IACrE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8BAA8B;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAsB,0BAA0B,CAC9C,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,QAAQ,EAClB,OAAO,GAAE,iCAAsC,GAC9C,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CA0GrD"}
@@ -0,0 +1,144 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Location Reference Generation
3
+ //
4
+ // Locations are first-class environment entities (see Location in
5
+ // src/series/types.ts). Their reference images anchor the environment across
6
+ // storyboard panels, starting frames, and video generations — mirroring how
7
+ // character references anchor identity, and directly serving the
8
+ // lighting-consistency anti-pattern (AGENTS.md anti-pattern 7).
9
+ //
10
+ // Reference angles: wide (establishing), medium (mid-distance), detail
11
+ // (distinctive feature). Generated FACELESS with nano-banana-pro (default) and
12
+ // provenance hasFace:false, so they pass the Seedance pre-flight gate without
13
+ // laundering. Shared by the `add-location` CLI command and workshop-episode's
14
+ // auto-extraction so both paths produce identical assets.
15
+ // ---------------------------------------------------------------------------
16
+ import { join, basename } from 'node:path';
17
+ import { existsSync } from 'node:fs';
18
+ import { mkdir, writeFile, rename } from 'node:fs/promises';
19
+ import { getLocationDir } from '../series/manager.js';
20
+ import { generateImage } from '../venice/generate.js';
21
+ import { writeImageBytesSmart } from '../venice/image-bytes.js';
22
+ import { appendRecipePass } from '../venice/recipe.js';
23
+ import { DEFAULT_IMAGE_GENERATION_MODEL } from '../series/types.js';
24
+ export const LOCATION_ANGLES = ['wide', 'medium', 'detail'];
25
+ const ANGLE_PROMPTS = {
26
+ wide: 'wide establishing shot of the location, full environment visible, cinematic widescreen framing',
27
+ medium: 'medium shot of the location, mid-distance framing showing the key features and spatial layout',
28
+ detail: 'close detail shot of a distinctive feature of the location, texture and material detail',
29
+ };
30
+ function buildAestheticString(aesthetic) {
31
+ return [
32
+ aesthetic.style,
33
+ aesthetic.palette,
34
+ aesthetic.lighting,
35
+ aesthetic.lensCharacteristics,
36
+ aesthetic.filmStock ? `shot on ${aesthetic.filmStock}` : '',
37
+ ]
38
+ .filter(Boolean)
39
+ .join(', ');
40
+ }
41
+ /**
42
+ * Generate the three reference angles for a location. Writes
43
+ * `locations/<slug>/{wide,medium,detail}.png` plus per-angle `.prompt.json`
44
+ * sidecars, provenance (hasFace:false), and recipe passes. Returns the paths
45
+ * that were (re)generated.
46
+ */
47
+ export async function generateLocationReferences(client, series, location, options = {}) {
48
+ if (!series.aesthetic) {
49
+ throw new Error('Series aesthetic must be set before generating location references.');
50
+ }
51
+ const dir = getLocationDir(series, location.slug);
52
+ await mkdir(dir, { recursive: true });
53
+ const model = options.model ?? location.referenceModel ?? DEFAULT_IMAGE_GENERATION_MODEL;
54
+ const cfgScale = options.cfgScale ?? 10;
55
+ const aspect = series.storyboardAspectRatio ?? '16:9';
56
+ const aestheticStr = buildAestheticString(series.aesthetic);
57
+ const seed = location.seed;
58
+ const generated = [];
59
+ const skipped = [];
60
+ for (const angle of LOCATION_ANGLES) {
61
+ const imgPath = join(dir, `${angle}.png`);
62
+ if (existsSync(imgPath) && !options.force) {
63
+ skipped.push(imgPath);
64
+ continue;
65
+ }
66
+ // Archive existing when forcing (asset-safety: never destructive).
67
+ if (existsSync(imgPath) && options.force) {
68
+ const archive = imgPath.replace(/\.png$/, `-force-archive-${Date.now()}.png`);
69
+ await rename(imgPath, archive);
70
+ }
71
+ // Front-load STYLE (rule 11) so the environment holds the series look.
72
+ const promptParts = [
73
+ `STYLE: ${aestheticStr}.`,
74
+ `${ANGLE_PROMPTS[angle]}.`,
75
+ `${location.description}.`,
76
+ location.lightingNotes ? `Lighting: ${location.lightingNotes}.` : '',
77
+ 'Empty environment, no people present, no human figures, uninhabited scene.',
78
+ `STYLE REMINDER: ${aestheticStr}.`,
79
+ ].filter(Boolean);
80
+ const prompt = promptParts.join(' ');
81
+ const negativePrompt = [
82
+ 'people', 'person', 'human', 'figure', 'silhouette', 'crowd',
83
+ 'deformed', 'blurry', 'low quality', 'watermark', 'text', 'signature',
84
+ 'comic panels', 'panel borders', 'multiple frames',
85
+ ].join(', ');
86
+ try {
87
+ const response = await generateImage(client, {
88
+ model,
89
+ prompt,
90
+ negative_prompt: negativePrompt,
91
+ resolution: '1K',
92
+ aspect_ratio: aspect,
93
+ steps: 30,
94
+ cfg_scale: cfgScale,
95
+ seed,
96
+ safe_mode: false,
97
+ hide_watermark: true,
98
+ });
99
+ if (!response.images?.[0]) {
100
+ console.warn(` ${angle}: no image returned`);
101
+ continue;
102
+ }
103
+ const imgBuffer = Buffer.from(response.images[0].b64_json, 'base64');
104
+ const finalPath = await writeImageBytesSmart(imgBuffer, imgPath);
105
+ console.log(` ${angle}: saved -> ${basename(finalPath)}`);
106
+ const returnedSeed = response.images[0].seed;
107
+ await writeFile(join(dir, `${angle}.prompt.json`), JSON.stringify({
108
+ location: location.name,
109
+ slug: location.slug,
110
+ angle,
111
+ model,
112
+ prompt,
113
+ negative_prompt: negativePrompt,
114
+ cfg_scale: cfgScale,
115
+ aspect_ratio: aspect,
116
+ resolution: '1K',
117
+ seed,
118
+ returnedSeed,
119
+ generatedAt: new Date().toISOString(),
120
+ }, null, 2), 'utf-8');
121
+ // Faceless by design → provenance hasFace:false so the Seedance
122
+ // pre-flight gate (which checks sceneImagePaths + referenceImagePaths)
123
+ // passes without laundering.
124
+ await appendRecipePass(finalPath, {
125
+ kind: 'generate',
126
+ role: 'content',
127
+ model,
128
+ label: `location reference (${location.name}, ${angle})`,
129
+ prompt,
130
+ negativePrompt,
131
+ seed,
132
+ cfgScale,
133
+ aspectRatio: aspect,
134
+ resolution: '1K',
135
+ }, { provenance: 'generate', hasFace: false });
136
+ generated.push(finalPath);
137
+ }
138
+ catch (err) {
139
+ console.warn(` ${angle}: failed - ${err}`);
140
+ }
141
+ }
142
+ return { generated, skipped };
143
+ }
144
+ //# sourceMappingURL=location-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"location-generator.js","sourceRoot":"","sources":["../../src/mini-drama/location-generator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,96 @@
1
+ import type { MusicCueSpec, ShotScript } from '../series/types.js';
2
+ /** A cue annotated with its resolved timeline window. */
3
+ export interface ResolvedMusicCue {
4
+ spec: MusicCueSpec;
5
+ /** Source audio file, after generation / passthrough. */
6
+ audioPath: string;
7
+ startSec: number;
8
+ endSec: number;
9
+ }
10
+ export interface PlacementMap {
11
+ /** Shot id (string, zero-padded for numeric shots like "003" / "003b"). */
12
+ [shotId: string]: {
13
+ startSec: number;
14
+ endSec: number;
15
+ };
16
+ }
17
+ /** Normalize a shot id (number or string) to a string key. */
18
+ export declare function shotIdKey(id: number | string): string;
19
+ /**
20
+ * Resolve a cue's timeline window from the placement map.
21
+ * Returns null when the start or end shot can't be found — callers should
22
+ * warn and skip the cue rather than emit a misaligned cue.
23
+ */
24
+ export declare function resolveCueWindow(cue: MusicCueSpec, placementMap: PlacementMap): {
25
+ startSec: number;
26
+ endSec: number;
27
+ } | null;
28
+ /**
29
+ * Assemble a list of resolved cues into a single MP3 track with crossfades.
30
+ *
31
+ * For each cue:
32
+ * - trim/pad to the resolved window length,
33
+ * - apply gain and fade-in / fade-out,
34
+ * - acrossfade with the previous track at the trailing fade point.
35
+ *
36
+ * The output sample rate is fixed at 48000 Hz / stereo for compatibility
37
+ * with the assembler's final aac encode.
38
+ */
39
+ export declare function renderMusicCuesTrack(opts: {
40
+ cues: ResolvedMusicCue[];
41
+ outputPath: string;
42
+ totalDurationSec: number;
43
+ /**
44
+ * Placement map used to resolve `gainStops[].atShot`. Optional — when
45
+ * omitted, gain stops are skipped (cues still honour the base `gain`).
46
+ */
47
+ placementMap?: PlacementMap;
48
+ }): Promise<string>;
49
+ /**
50
+ * Build a single ffmpeg `volume=` expression that ramps gain through a cue's
51
+ * `gainStops[]`. Stops are ordered along the timeline and produce a smooth
52
+ * piecewise-linear curve.
53
+ *
54
+ * The expression evaluates `t` (timeline seconds) and returns a linear gain
55
+ * multiplier. Callers should pass the result with `volume=<expr>:eval=frame`.
56
+ *
57
+ * Returns null when the cue has no stops or none of them resolve to the
58
+ * placement map.
59
+ */
60
+ export declare function buildGainStopsExpr(cue: ResolvedMusicCue, placementMap: PlacementMap): string | null;
61
+ /**
62
+ * derive a per-shot volume curve for music automation.
63
+ *
64
+ * Combines `shot.musicHold` with the containing cue's `musicHold` and
65
+ * returns an ffmpeg `volume=` expression suitable for layering over the
66
+ * already-rendered cues track. Returns `null` when no automation is needed
67
+ * (everything is sustain).
68
+ *
69
+ * The expression evaluates `t` (timeline seconds) and produces a multiplier
70
+ * in [0, 4] — 1.0 is unity, sub-1 ducks, super-1 swells.
71
+ *
72
+ * stinger: 0.4s pulse +6 dB at the start of the shot, return to 1.0
73
+ * swell: linear ramp from 1.0 to 1.58 (+4 dB) across the shot
74
+ * drop: constant 0.001 (≈ -60 dB) for the shot's duration
75
+ * sustain: no change
76
+ */
77
+ export declare function buildMusicHoldExpr(shots: ShotScript[], placementMap: PlacementMap): string | null;
78
+ /**
79
+ * Apply a `volume=` expression to an existing music track and emit a new file.
80
+ * Use this AFTER `renderMusicCuesTrack` to layer `musicHold` automation
81
+ * without re-rendering the full filter graph.
82
+ */
83
+ export declare function applyMusicHoldAutomation(opts: {
84
+ inputPath: string;
85
+ outputPath: string;
86
+ volumeExpr: string;
87
+ }): Promise<string>;
88
+ /**
89
+ * Convenience: convert a flat list of cue specs into resolved cues by
90
+ * looking up each in the placement map. Cues whose start or end shot
91
+ * cannot be resolved are skipped (with a console.warn) — they were
92
+ * scripted but the placement map doesn't have the shot yet, e.g. because
93
+ * the shot is out of range or was removed.
94
+ */
95
+ export declare function resolveMusicCues(specs: MusicCueSpec[], placementMap: PlacementMap, audioPathFor: (spec: MusicCueSpec) => string | undefined): ResolvedMusicCue[];
96
+ //# sourceMappingURL=music-cues.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"music-cues.d.ts","sourceRoot":"","sources":["../../src/mini-drama/music-cues.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAQnE,yDAAyD;AACzD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,YAAY,CAAC;IACnB,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,2EAA2E;IAC3E,CAAC,MAAM,EAAE,MAAM,GAAG;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CACxD;AAED,8DAA8D;AAC9D,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAMrD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,YAAY,EACjB,YAAY,EAAE,YAAY,GACzB;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAO7C;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,oBAAoB,CAAC,IAAI,EAAE;IAC/C,IAAI,EAAE,gBAAgB,EAAE,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B,GAAG,OAAO,CAAC,MAAM,CAAC,CAmIlB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,gBAAgB,EACrB,YAAY,EAAE,YAAY,GACzB,MAAM,GAAG,IAAI,CA0Cf;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,UAAU,EAAE,EACnB,YAAY,EAAE,YAAY,GACzB,MAAM,GAAG,IAAI,CA4Bf;AAED;;;;GAIG;AACH,wBAAsB,wBAAwB,CAAC,IAAI,EAAE;IACnD,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,MAAM,CAAC,CAclB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,YAAY,EAAE,EACrB,YAAY,EAAE,YAAY,EAC1B,YAAY,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,MAAM,GAAG,SAAS,GACvD,gBAAgB,EAAE,CAgBpB"}
@@ -0,0 +1,332 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Per-act music cues with ffmpeg crossfades.
3
+ //
4
+ // Replaces the assembler's single-static-bed model with an ordered list of
5
+ // cues, each anchored to a shot id. Cues crossfade at their configured fade
6
+ // points so beats can change with the story: bed → ominous → bed → climactic.
7
+ //
8
+ // Per-shot musicHold (sustain / swell / drop / stinger) layers automation on
9
+ // top of the cue: volume ramps, sidechain ducks, transient stingers.
10
+ //
11
+ // This module is pure orchestration — it shells ffmpeg via execFile but does
12
+ // not know how to call Venice. Cue audio is either supplied via
13
+ // `cue.audioPath` or rendered separately and the path threaded in by the
14
+ // caller (see the music-cue reference scripts).
15
+ // ---------------------------------------------------------------------------
16
+ import { execFile } from 'node:child_process';
17
+ import { mkdir } from 'node:fs/promises';
18
+ import { existsSync } from 'node:fs';
19
+ import { dirname } from 'node:path';
20
+ import { promisify } from 'node:util';
21
+ const execFileAsync = promisify(execFile);
22
+ const DEFAULT_GAIN_DB = -22;
23
+ const DEFAULT_FADE_IN = 1.0;
24
+ const DEFAULT_FADE_OUT = 1.5;
25
+ /** Normalize a shot id (number or string) to a string key. */
26
+ export function shotIdKey(id) {
27
+ if (typeof id === 'number')
28
+ return String(id).padStart(3, '0');
29
+ // Suffixed forms like "3b" -> "003b". Suffix letters preserved as-is.
30
+ const match = id.match(/^(\d+)([a-zA-Z]*)$/);
31
+ if (match)
32
+ return String(match[1]).padStart(3, '0') + match[2];
33
+ return id;
34
+ }
35
+ /**
36
+ * Resolve a cue's timeline window from the placement map.
37
+ * Returns null when the start or end shot can't be found — callers should
38
+ * warn and skip the cue rather than emit a misaligned cue.
39
+ */
40
+ export function resolveCueWindow(cue, placementMap) {
41
+ const startKey = shotIdKey(cue.startShot);
42
+ const endKey = shotIdKey(cue.endShot);
43
+ const start = placementMap[startKey];
44
+ const end = placementMap[endKey];
45
+ if (!start || !end)
46
+ return null;
47
+ return { startSec: start.startSec, endSec: end.endSec };
48
+ }
49
+ /**
50
+ * Assemble a list of resolved cues into a single MP3 track with crossfades.
51
+ *
52
+ * For each cue:
53
+ * - trim/pad to the resolved window length,
54
+ * - apply gain and fade-in / fade-out,
55
+ * - acrossfade with the previous track at the trailing fade point.
56
+ *
57
+ * The output sample rate is fixed at 48000 Hz / stereo for compatibility
58
+ * with the assembler's final aac encode.
59
+ */
60
+ export async function renderMusicCuesTrack(opts) {
61
+ if (opts.cues.length === 0) {
62
+ throw new Error('renderMusicCuesTrack: at least one cue is required');
63
+ }
64
+ await mkdir(dirname(opts.outputPath), { recursive: true });
65
+ // For a single cue, just gain + fade + write.
66
+ if (opts.cues.length === 1) {
67
+ const cue = opts.cues[0];
68
+ const dur = cue.endSec - cue.startSec;
69
+ const gain = cue.spec.gain ?? DEFAULT_GAIN_DB;
70
+ const fadeIn = cue.spec.fadeIn ?? DEFAULT_FADE_IN;
71
+ const fadeOut = cue.spec.fadeOut ?? DEFAULT_FADE_OUT;
72
+ // gainStops[] runs in cue-local time, so build the expression then offset
73
+ // its `t` by the cue's start. Easier: render the base bed without
74
+ // gainStops, then apply the expression on the final track (which is
75
+ // already in timeline time).
76
+ const filter = [
77
+ `volume=${gain}dB`,
78
+ `afade=t=in:st=0:d=${fadeIn}`,
79
+ `afade=t=out:st=${Math.max(0, dur - fadeOut)}:d=${fadeOut}`,
80
+ ].join(',');
81
+ await execFileAsync('ffmpeg', [
82
+ '-y',
83
+ '-i', cue.audioPath,
84
+ '-af', filter,
85
+ '-t', String(dur),
86
+ '-ar', '48000',
87
+ '-ac', '2',
88
+ opts.outputPath,
89
+ ]);
90
+ // gainStops: the expression evaluates against timeline t. By convention
91
+ // cue 1 begins at timeline t=0 (the music bed aligns to the first shot),
92
+ // so the rendered mp3's local time axis matches the timeline. For
93
+ // single-cue renderings that begin later, callers should pre-pad the
94
+ // bed (out of scope here).
95
+ if (opts.placementMap) {
96
+ const stopsExpr = buildGainStopsExpr(cue, opts.placementMap);
97
+ if (stopsExpr) {
98
+ const tmp = opts.outputPath.replace(/\.mp3$/, '-gainstops.mp3');
99
+ await execFileAsync('ffmpeg', [
100
+ '-y',
101
+ '-i', opts.outputPath,
102
+ '-af', `volume='${stopsExpr}':eval=frame`,
103
+ '-ar', '48000',
104
+ '-ac', '2',
105
+ tmp,
106
+ ]);
107
+ const { rename } = await import('node:fs/promises');
108
+ await rename(tmp, opts.outputPath);
109
+ }
110
+ }
111
+ return opts.outputPath;
112
+ }
113
+ // Multi-cue path: build a concat-with-crossfade filter graph.
114
+ const inputs = [];
115
+ for (const cue of opts.cues) {
116
+ inputs.push('-i', cue.audioPath);
117
+ }
118
+ // Build per-input filter chains, then chain acrossfade between them.
119
+ const filterParts = [];
120
+ for (let i = 0; i < opts.cues.length; i++) {
121
+ const cue = opts.cues[i];
122
+ const dur = cue.endSec - cue.startSec;
123
+ const gain = cue.spec.gain ?? DEFAULT_GAIN_DB;
124
+ const fadeIn = cue.spec.fadeIn ?? DEFAULT_FADE_IN;
125
+ // Trim/pad to window length, apply gain. Inner fade-in only — the
126
+ // crossfade handles outgoing edges. The very first cue gets a fade-in,
127
+ // the very last cue gets a fade-out.
128
+ const chain = [`atrim=0:${dur}`, `asetpts=N/SR/TB`, `volume=${gain}dB`];
129
+ if (i === 0)
130
+ chain.push(`afade=t=in:st=0:d=${fadeIn}`);
131
+ if (i === opts.cues.length - 1) {
132
+ const fadeOut = cue.spec.fadeOut ?? DEFAULT_FADE_OUT;
133
+ chain.push(`afade=t=out:st=${Math.max(0, dur - fadeOut)}:d=${fadeOut}`);
134
+ }
135
+ filterParts.push(`[${i}:a]${chain.join(',')}[a${i}]`);
136
+ }
137
+ // Chain acrossfade: [a0][a1]acrossfade=d=X[ax1]; [ax1][a2]acrossfade=d=Y[ax2]; ...
138
+ let prevLabel = '[a0]';
139
+ for (let i = 1; i < opts.cues.length; i++) {
140
+ // Crossfade duration is the min of the trailing fadeOut on the previous
141
+ // cue and the leading fadeIn on the next cue — this is the overlap region
142
+ // both cues already attenuate.
143
+ const prev = opts.cues[i - 1].spec;
144
+ const next = opts.cues[i].spec;
145
+ const xfade = Math.min(prev.fadeOut ?? DEFAULT_FADE_OUT, next.fadeIn ?? DEFAULT_FADE_IN);
146
+ const outLabel = i === opts.cues.length - 1 ? '[aout]' : `[ax${i}]`;
147
+ filterParts.push(`${prevLabel}[a${i}]acrossfade=d=${xfade.toFixed(3)}:c1=tri:c2=tri${outLabel}`);
148
+ prevLabel = outLabel;
149
+ }
150
+ await execFileAsync('ffmpeg', [
151
+ '-y',
152
+ ...inputs,
153
+ '-filter_complex', filterParts.join(';'),
154
+ '-map', '[aout]',
155
+ '-ar', '48000',
156
+ '-ac', '2',
157
+ opts.outputPath,
158
+ ]);
159
+ // Apply per-cue gainStops on the assembled bed. The expression evaluates
160
+ // against timeline t, which matches the assembled mp3's local time axis as
161
+ // long as cue 1 starts at timeline t=0 (the standard layout).
162
+ if (opts.placementMap) {
163
+ const exprs = [];
164
+ for (const cue of opts.cues) {
165
+ const e = buildGainStopsExpr(cue, opts.placementMap);
166
+ if (e)
167
+ exprs.push(e);
168
+ }
169
+ if (exprs.length > 0) {
170
+ const filters = exprs.map(e => `volume='${e}':eval=frame`).join(',');
171
+ const tmp = opts.outputPath.replace(/\.mp3$/, '-gainstops.mp3');
172
+ await execFileAsync('ffmpeg', [
173
+ '-y',
174
+ '-i', opts.outputPath,
175
+ '-af', filters,
176
+ '-ar', '48000',
177
+ '-ac', '2',
178
+ tmp,
179
+ ]);
180
+ const { rename } = await import('node:fs/promises');
181
+ await rename(tmp, opts.outputPath);
182
+ }
183
+ }
184
+ return opts.outputPath;
185
+ }
186
+ /**
187
+ * Build a single ffmpeg `volume=` expression that ramps gain through a cue's
188
+ * `gainStops[]`. Stops are ordered along the timeline and produce a smooth
189
+ * piecewise-linear curve.
190
+ *
191
+ * The expression evaluates `t` (timeline seconds) and returns a linear gain
192
+ * multiplier. Callers should pass the result with `volume=<expr>:eval=frame`.
193
+ *
194
+ * Returns null when the cue has no stops or none of them resolve to the
195
+ * placement map.
196
+ */
197
+ export function buildGainStopsExpr(cue, placementMap) {
198
+ const stops = cue.spec.gainStops;
199
+ if (!stops || stops.length === 0)
200
+ return null;
201
+ const baseDb = cue.spec.gain ?? DEFAULT_GAIN_DB;
202
+ const baseGain = Math.pow(10, baseDb / 20);
203
+ const resolved = [];
204
+ for (const s of stops) {
205
+ const placement = placementMap[shotIdKey(s.atShot)];
206
+ if (!placement) {
207
+ console.warn(` music-cue: gain stop atShot=${s.atShot} not in placement map; skipping`);
208
+ continue;
209
+ }
210
+ if (placement.startSec < cue.startSec || placement.startSec > cue.endSec) {
211
+ console.warn(` music-cue: gain stop atShot=${s.atShot} is outside cue window; skipping`);
212
+ continue;
213
+ }
214
+ resolved.push({
215
+ t: placement.startSec,
216
+ gain: Math.pow(10, s.gainDb / 20),
217
+ rampSec: s.rampSec ?? 2.0,
218
+ });
219
+ }
220
+ if (resolved.length === 0)
221
+ return null;
222
+ resolved.sort((a, b) => a.t - b.t);
223
+ // Build a nested piecewise expression:
224
+ // t < t1 - r1/2 -> baseGain
225
+ // t1-r1/2 .. t1+r1/2 -> linear ramp baseGain -> g1
226
+ // t1+r1/2 .. t2-r2/2 -> g1
227
+ // t2-r2/2 .. t2+r2/2 -> linear ramp g1 -> g2
228
+ // ... -> gN
229
+ let prevGain = baseGain;
230
+ let expr = `${baseGain.toFixed(6)}`;
231
+ for (const s of resolved) {
232
+ const rampStart = (s.t - s.rampSec / 2).toFixed(3);
233
+ const rampEnd = (s.t + s.rampSec / 2).toFixed(3);
234
+ const ramp = `(${prevGain.toFixed(6)}+(${s.gain.toFixed(6)}-${prevGain.toFixed(6)})*(t-${rampStart})/${s.rampSec.toFixed(3)})`;
235
+ expr = `if(lt(t,${rampStart}),${expr},if(lt(t,${rampEnd}),${ramp},${s.gain.toFixed(6)}))`;
236
+ prevGain = s.gain;
237
+ }
238
+ return expr;
239
+ }
240
+ /**
241
+ * derive a per-shot volume curve for music automation.
242
+ *
243
+ * Combines `shot.musicHold` with the containing cue's `musicHold` and
244
+ * returns an ffmpeg `volume=` expression suitable for layering over the
245
+ * already-rendered cues track. Returns `null` when no automation is needed
246
+ * (everything is sustain).
247
+ *
248
+ * The expression evaluates `t` (timeline seconds) and produces a multiplier
249
+ * in [0, 4] — 1.0 is unity, sub-1 ducks, super-1 swells.
250
+ *
251
+ * stinger: 0.4s pulse +6 dB at the start of the shot, return to 1.0
252
+ * swell: linear ramp from 1.0 to 1.58 (+4 dB) across the shot
253
+ * drop: constant 0.001 (≈ -60 dB) for the shot's duration
254
+ * sustain: no change
255
+ */
256
+ export function buildMusicHoldExpr(shots, placementMap) {
257
+ const pieces = [];
258
+ for (const shot of shots) {
259
+ const hold = shot.musicHold;
260
+ if (!hold || hold === 'sustain')
261
+ continue;
262
+ const placement = placementMap[shotIdKey(shot.shotNumber)];
263
+ if (!placement)
264
+ continue;
265
+ const { startSec, endSec } = placement;
266
+ if (hold === 'stinger') {
267
+ pieces.push(`if(between(t,${startSec.toFixed(3)},${(startSec + 0.4).toFixed(3)}),2.0,`);
268
+ pieces.push(')'); // close the if — placeholder; we wrap below.
269
+ }
270
+ else if (hold === 'swell') {
271
+ // 1.0 -> 1.58 across the shot
272
+ pieces.push(`if(between(t,${startSec.toFixed(3)},${endSec.toFixed(3)}),` +
273
+ `1.0+(0.58*(t-${startSec.toFixed(3)})/${(endSec - startSec).toFixed(3)}),`);
274
+ pieces.push(')');
275
+ }
276
+ else if (hold === 'drop') {
277
+ pieces.push(`if(between(t,${startSec.toFixed(3)},${endSec.toFixed(3)}),0.001,`);
278
+ pieces.push(')');
279
+ }
280
+ }
281
+ if (pieces.length === 0)
282
+ return null;
283
+ // Nest the if-expressions with `1` (sustain) as the innermost else.
284
+ const opens = pieces.filter(p => p !== ')');
285
+ const closes = pieces.filter(p => p === ')').length;
286
+ return opens.join('') + '1' + ')'.repeat(closes);
287
+ }
288
+ /**
289
+ * Apply a `volume=` expression to an existing music track and emit a new file.
290
+ * Use this AFTER `renderMusicCuesTrack` to layer `musicHold` automation
291
+ * without re-rendering the full filter graph.
292
+ */
293
+ export async function applyMusicHoldAutomation(opts) {
294
+ if (!existsSync(opts.inputPath)) {
295
+ throw new Error(`applyMusicHoldAutomation: input not found at ${opts.inputPath}`);
296
+ }
297
+ await mkdir(dirname(opts.outputPath), { recursive: true });
298
+ await execFileAsync('ffmpeg', [
299
+ '-y',
300
+ '-i', opts.inputPath,
301
+ '-af', `volume=${opts.volumeExpr}:eval=frame`,
302
+ '-ar', '48000',
303
+ '-ac', '2',
304
+ opts.outputPath,
305
+ ]);
306
+ return opts.outputPath;
307
+ }
308
+ /**
309
+ * Convenience: convert a flat list of cue specs into resolved cues by
310
+ * looking up each in the placement map. Cues whose start or end shot
311
+ * cannot be resolved are skipped (with a console.warn) — they were
312
+ * scripted but the placement map doesn't have the shot yet, e.g. because
313
+ * the shot is out of range or was removed.
314
+ */
315
+ export function resolveMusicCues(specs, placementMap, audioPathFor) {
316
+ const out = [];
317
+ for (const spec of specs) {
318
+ const window = resolveCueWindow(spec, placementMap);
319
+ if (!window) {
320
+ console.warn(` music-cue: skipping cue ${shotIdKey(spec.startShot)}->${shotIdKey(spec.endShot)} — shot ids not in placement map`);
321
+ continue;
322
+ }
323
+ const audioPath = spec.audioPath ?? audioPathFor(spec);
324
+ if (!audioPath || !existsSync(audioPath)) {
325
+ console.warn(` music-cue: skipping cue ${shotIdKey(spec.startShot)}->${shotIdKey(spec.endShot)} — audio file missing`);
326
+ continue;
327
+ }
328
+ out.push({ spec, audioPath, startSec: window.startSec, endSec: window.endSec });
329
+ }
330
+ return out;
331
+ }
332
+ //# sourceMappingURL=music-cues.js.map