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,11 @@
1
+ import type { TimelineExportOptions } from './types.js';
2
+ /**
3
+ * Emit an FCPXML 1.10 file. Returns the output path.
4
+ *
5
+ * The function is pure orchestration — segment timing and audio placement
6
+ * decisions must already be made by the caller. The standard placement
7
+ * pattern (shot-anchored offsets from + music cues from ) builds
8
+ * the inputs naturally.
9
+ */
10
+ export declare function exportFcpxml(opts: TimelineExportOptions): Promise<string>;
11
+ //# sourceMappingURL=fcpxml.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fcpxml.d.ts","sourceRoot":"","sources":["../../../src/mini-drama/timeline-export/fcpxml.ts"],"names":[],"mappings":"AAgCA,OAAO,KAAK,EAAqB,qBAAqB,EAAmB,MAAM,YAAY,CAAC;AAa5F;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,CAiH/E"}
@@ -0,0 +1,140 @@
1
+ // ---------------------------------------------------------------------------
2
+ // FCPXML 1.10 export (Final Cut Pro X).
3
+ //
4
+ // Emits an FCPXML 1.10 that imports cleanly into FCP X 10.7+ with:
5
+ // - one <format> declaration sized to the series' aspect ratio + fps
6
+ // - one <asset> per unique video segment AND per unique audio file, each
7
+ // with a child <media-rep kind="original-media" src="file://...">.
8
+ // (FCPXML 1.10 puts src on <media-rep>, NOT on <asset> itself — putting
9
+ // it on <asset> fails DTD validation.)
10
+ // - per-asset audioRate + audioChannels probed from each file
11
+ // - primary storyline: one <asset-clip> per video segment in sequence, with
12
+ // <adjust-volume amount="-96dB"/> to mute the segment's silent audio
13
+ // - connected audio MUST be children of their containing primary clip (NOT
14
+ // siblings on the spine). Each child <audio> uses lane="-N" (negative =
15
+ // connected below primary) and an offset expressed in the parent's local
16
+ // media time (= timeline offset − parent's spine offset).
17
+ // - lane plan: -1 dialogue (role dialogue.dialogue), -2 SFX (effects.effects),
18
+ // -3 music (music.music).
19
+ // - time format: rational `<frames>/<fps>s` (e.g. 4345/24s = 181.04s @24fps).
20
+ // Snap to frame boundary.
21
+ // ---------------------------------------------------------------------------
22
+ import { mkdir, writeFile } from 'node:fs/promises';
23
+ import { existsSync } from 'node:fs';
24
+ import { dirname } from 'node:path';
25
+ import { pathToFileUri, probeAudioInfo, segmentContaining, toRationalTime, xmlEscape, } from './probe.js';
26
+ /**
27
+ * Emit an FCPXML 1.10 file. Returns the output path.
28
+ *
29
+ * The function is pure orchestration — segment timing and audio placement
30
+ * decisions must already be made by the caller. The standard placement
31
+ * pattern (shot-anchored offsets from + music cues from ) builds
32
+ * the inputs naturally.
33
+ */
34
+ export async function exportFcpxml(opts) {
35
+ const fps = opts.fps ?? 24;
36
+ const width = opts.width ?? 1920;
37
+ const height = opts.height ?? 1080;
38
+ const eventName = opts.eventName ?? 'Episode';
39
+ const projectName = opts.projectName ?? `${eventName} — fine-tune`;
40
+ // 1. Build assets. One asset per unique file path.
41
+ const assetById = new Map();
42
+ let counter = 1; // r1 is reserved for the <format>; assets start at r2.
43
+ const FORMAT_ID = 'r1';
44
+ async function registerAsset(path, hasVideo, durSec, name) {
45
+ const existing = assetById.get(path);
46
+ if (existing)
47
+ return existing;
48
+ const id = `r${++counter + 1}`; // r2, r3, ...
49
+ let audioRate = 48000;
50
+ let audioChannels = 2;
51
+ try {
52
+ const info = await probeAudioInfo(path);
53
+ audioRate = info.sampleRate;
54
+ audioChannels = info.channels;
55
+ }
56
+ catch { /* best-effort */ }
57
+ const rec = {
58
+ id, name, path,
59
+ hasVideo,
60
+ hasAudio: true,
61
+ durRt: toRationalTime(durSec, fps),
62
+ audioRate,
63
+ audioChannels,
64
+ };
65
+ assetById.set(path, rec);
66
+ return rec;
67
+ }
68
+ for (const s of opts.segments) {
69
+ if (!existsSync(s.path)) {
70
+ throw new Error(`exportFcpxml: missing segment ${s.path}`);
71
+ }
72
+ s._asset =
73
+ await registerAsset(s.path, true, s.durSec, s.label);
74
+ }
75
+ for (const a of opts.audio) {
76
+ if (!existsSync(a.path)) {
77
+ throw new Error(`exportFcpxml: missing audio file ${a.path}`);
78
+ }
79
+ a._asset =
80
+ await registerAsset(a.path, false, a.audioDur, a.label);
81
+ }
82
+ // 2. Bucket connected audio under the segment that contains its startSec.
83
+ const buckets = opts.segments.map(() => []);
84
+ for (const a of opts.audio) {
85
+ buckets[segmentContaining(opts.segments, a.startSec)].push(a);
86
+ }
87
+ // 3. Emit XML.
88
+ const masterDur = opts.totalDurationSec
89
+ ?? opts.segments.reduce((acc, s) => Math.max(acc, s.startSec + s.durSec), 0);
90
+ const lines = [];
91
+ lines.push(`<?xml version="1.0" encoding="UTF-8"?>`);
92
+ lines.push(`<!DOCTYPE fcpxml>`);
93
+ lines.push(`<fcpxml version="1.10">`);
94
+ lines.push(` <resources>`);
95
+ lines.push(` <format id="${FORMAT_ID}" name="FFVideoFormat${width}x${height}p${fps}" frameDuration="1/${fps}s" width="${width}" height="${height}" colorSpace="1-1-1 (Rec. 709)"/>`);
96
+ for (const a of assetById.values()) {
97
+ const uri = pathToFileUri(a.path);
98
+ const hasVideo = a.hasVideo ? '1' : '0';
99
+ const formatAttr = a.hasVideo ? ` format="${FORMAT_ID}"` : '';
100
+ lines.push(` <asset id="${a.id}" name="${xmlEscape(a.name)}" start="0s" duration="${a.durRt}" hasVideo="${hasVideo}" hasAudio="1"${formatAttr} audioSources="1" audioChannels="${a.audioChannels}" audioRate="${a.audioRate}">`);
101
+ lines.push(` <media-rep kind="original-media" src="${uri}"/>`);
102
+ lines.push(` </asset>`);
103
+ }
104
+ lines.push(` </resources>`);
105
+ lines.push(` <library>`);
106
+ lines.push(` <event name="${xmlEscape(eventName)}">`);
107
+ lines.push(` <project name="${xmlEscape(projectName)}">`);
108
+ lines.push(` <sequence format="${FORMAT_ID}" duration="${toRationalTime(masterDur, fps)}" tcStart="0s" tcFormat="NDF" audioLayout="stereo" audioRate="48k">`);
109
+ lines.push(` <spine>`);
110
+ for (let i = 0; i < opts.segments.length; i++) {
111
+ const s = opts.segments[i];
112
+ const segAsset = s._asset;
113
+ const children = buckets[i];
114
+ const open = ` <asset-clip name="${xmlEscape(s.label)}" ref="${segAsset.id}" offset="${toRationalTime(s.startSec, fps)}" duration="${toRationalTime(s.durSec, fps)}" start="0s" tcFormat="NDF" audioRole="dialogue.dialogue"`;
115
+ if (children.length === 0) {
116
+ lines.push(open + `>`);
117
+ lines.push(` <adjust-volume amount="-96dB"/>`);
118
+ lines.push(` </asset-clip>`);
119
+ continue;
120
+ }
121
+ lines.push(open + `>`);
122
+ lines.push(` <adjust-volume amount="-96dB"/>`);
123
+ for (const ch of children) {
124
+ const chAsset = ch._asset;
125
+ const localOffset = ch.startSec - s.startSec;
126
+ lines.push(` <audio name="${xmlEscape(ch.label)}" ref="${chAsset.id}" offset="${toRationalTime(localOffset, fps)}" duration="${toRationalTime(ch.audioDur, fps)}" start="0s" lane="${ch.lane}" role="${ch.role}"/>`);
127
+ }
128
+ lines.push(` </asset-clip>`);
129
+ }
130
+ lines.push(` </spine>`);
131
+ lines.push(` </sequence>`);
132
+ lines.push(` </project>`);
133
+ lines.push(` </event>`);
134
+ lines.push(` </library>`);
135
+ lines.push(`</fcpxml>`);
136
+ await mkdir(dirname(opts.outputPath), { recursive: true });
137
+ await writeFile(opts.outputPath, lines.join('\n') + '\n', 'utf-8');
138
+ return opts.outputPath;
139
+ }
140
+ //# sourceMappingURL=fcpxml.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fcpxml.js","sourceRoot":"","sources":["../../../src/mini-drama/timeline-export/fcpxml.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ export type { TimelineSegment, TimelineAudioClip, TimelineExportOptions, FcpxmlSegment, FcpxmlAudioClip, FcpxmlExportOptions, } from './types.js';
2
+ export { toRationalTime, toFrames, pathToFileUri, xmlEscape, probeAudioInfo, segmentContaining, } from './probe.js';
3
+ export { exportFcpxml } from './fcpxml.js';
4
+ export { exportPremiereXml } from './premiere-xmeml.js';
5
+ export { exportDavinciFcpxml } from './davinci-fcpxml.js';
6
+ export type TimelineExportFormat = 'fcpxml' | 'premiere' | 'davinci';
7
+ import type { TimelineExportOptions } from './types.js';
8
+ /**
9
+ * Dispatch by format. Used by the CLI command so the exporter selection
10
+ * lives in one place.
11
+ */
12
+ export declare function exportTimeline(format: TimelineExportFormat, opts: TimelineExportOptions): Promise<string>;
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/mini-drama/timeline-export/index.ts"],"names":[],"mappings":"AAGA,YAAY,EACV,eAAe,EACf,iBAAiB,EACjB,qBAAqB,EAErB,aAAa,EACb,eAAe,EACf,mBAAmB,GACpB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,cAAc,EACd,QAAQ,EACR,aAAa,EACb,SAAS,EACT,cAAc,EACd,iBAAiB,GAClB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE1D,MAAM,MAAM,oBAAoB,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;AAErE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAKxD;;;GAGG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,oBAAoB,EAC5B,IAAI,EAAE,qBAAqB,GAC1B,OAAO,CAAC,MAAM,CAAC,CAWjB"}
@@ -0,0 +1,26 @@
1
+ // Timeline-export barrel. Single import point for all three exporters
2
+ // (FCPXML 1.10, Resolve-tuned FCPXML, Premiere xmeml v5) and shared types.
3
+ export { toRationalTime, toFrames, pathToFileUri, xmlEscape, probeAudioInfo, segmentContaining, } from './probe.js';
4
+ export { exportFcpxml } from './fcpxml.js';
5
+ export { exportPremiereXml } from './premiere-xmeml.js';
6
+ export { exportDavinciFcpxml } from './davinci-fcpxml.js';
7
+ import { exportFcpxml } from './fcpxml.js';
8
+ import { exportPremiereXml } from './premiere-xmeml.js';
9
+ import { exportDavinciFcpxml } from './davinci-fcpxml.js';
10
+ /**
11
+ * Dispatch by format. Used by the CLI command so the exporter selection
12
+ * lives in one place.
13
+ */
14
+ export async function exportTimeline(format, opts) {
15
+ switch (format) {
16
+ case 'fcpxml': return exportFcpxml(opts);
17
+ case 'premiere': return exportPremiereXml(opts);
18
+ case 'davinci': return exportDavinciFcpxml(opts);
19
+ default: {
20
+ // Exhaustiveness check.
21
+ const _exhaustive = format;
22
+ throw new Error(`Unknown timeline export format: ${_exhaustive}`);
23
+ }
24
+ }
25
+ }
26
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/mini-drama/timeline-export/index.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,2EAA2E;AAY3E,OAAO,EACL,cAAc,EACd,QAAQ,EACR,aAAa,EACb,SAAS,EACT,cAAc,EACd,iBAAiB,GAClB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAK1D,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE1D;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,MAA4B,EAC5B,IAA2B;IAE3B,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,QAAQ,CAAC,CAAC,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC;QACzC,KAAK,UAAU,CAAC,CAAC,OAAO,iBAAiB,CAAC,IAAI,CAAC,CAAC;QAChD,KAAK,SAAS,CAAC,CAAC,OAAO,mBAAmB,CAAC,IAAI,CAAC,CAAC;QACjD,OAAO,CAAC,CAAC,CAAC;YACR,wBAAwB;YACxB,MAAM,WAAW,GAAU,MAAM,CAAC;YAClC,MAAM,IAAI,KAAK,CAAC,mCAAmC,WAAW,EAAE,CAAC,CAAC;QACpE,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { TimelineExportOptions } from './types.js';
2
+ export declare function exportPremiereXml(opts: TimelineExportOptions): Promise<string>;
3
+ //# sourceMappingURL=premiere-xmeml.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"premiere-xmeml.d.ts","sourceRoot":"","sources":["../../../src/mini-drama/timeline-export/premiere-xmeml.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAqB,qBAAqB,EAAmB,MAAM,YAAY,CAAC;AAyB5F,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,CA8KpF"}
@@ -0,0 +1,211 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Adobe Premiere Pro export via Final Cut Pro 7 XML (xmeml v5).
3
+ //
4
+ // Premiere imports xmeml v5 natively via File > Import > XML… The format is
5
+ // Apple's predecessor to FCPXML — different shape, same idea. Differences
6
+ // from the FCPXML 1.10 path this module mirrors:
7
+ //
8
+ // - Root is <xmeml version="5"> with <sequence> directly inside. No
9
+ // <library> / <event> / <project> wrapper.
10
+ // - Times are integer FRAMES, not the rational <frames>/<fps>s form.
11
+ // - Rate appears as <rate><ntsc>FALSE</ntsc><timebase>24</timebase></rate>.
12
+ // - Connected audio is a separate <media><audio> block with <track>
13
+ // elements, NOT children of video clips.
14
+ // - One <track> per lane: V1 for video, A1 dialogue, A2 SFX, A3 music.
15
+ // - Each unique source file gets a <file id="..."> the first time it
16
+ // appears. Subsequent <clipitem> references reuse it via <file id="..."/>.
17
+ //
18
+ // References used to derive this format:
19
+ // - "Final Cut Pro X XML Interchange Format" — Apple developer docs.
20
+ // - Adobe Premiere "Import a Final Cut Pro project" support article.
21
+ // Both confirm xmeml v5 as the canonical Premiere import format.
22
+ //
23
+ // Open question (flag in PR body): test live import in Premiere on a real
24
+ // machine before merge — frame-count math is sensitive to ntsc-vs-drop-frame
25
+ // nuance that's hard to verify without the actual NLE.
26
+ // ---------------------------------------------------------------------------
27
+ import { mkdir, writeFile } from 'node:fs/promises';
28
+ import { existsSync } from 'node:fs';
29
+ import { dirname } from 'node:path';
30
+ import { pathToFileUri, probeAudioInfo, toFrames, xmlEscape } from './probe.js';
31
+ /**
32
+ * Map the format-neutral lane to a Premiere audio-track index (1-based).
33
+ *
34
+ * lane -1 (dialogue) -> A1
35
+ * lane -2 (SFX) -> A2
36
+ * lane -3 (music) -> A3
37
+ */
38
+ function laneToTrackIndex(lane) {
39
+ if (lane === -1)
40
+ return 1;
41
+ if (lane === -2)
42
+ return 2;
43
+ return 3;
44
+ }
45
+ export async function exportPremiereXml(opts) {
46
+ const fps = opts.fps ?? 24;
47
+ const width = opts.width ?? 1920;
48
+ const height = opts.height ?? 1080;
49
+ const sequenceName = opts.projectName ?? opts.eventName ?? 'Episode';
50
+ // 1. Register every unique source file as a <file> entry. Premiere
51
+ // references each <file> by id from each <clipitem>; only the first
52
+ // appearance carries the full <pathurl> block.
53
+ let counter = 0;
54
+ const fileById = new Map();
55
+ async function registerFile(path, hasVideo, durSec, name) {
56
+ const existing = fileById.get(path);
57
+ if (existing)
58
+ return existing;
59
+ const id = `file-${++counter}`;
60
+ let sampleRate = 48000;
61
+ let channels = 2;
62
+ try {
63
+ const info = await probeAudioInfo(path);
64
+ sampleRate = info.sampleRate;
65
+ channels = info.channels;
66
+ }
67
+ catch { /* best-effort — keep defaults */ }
68
+ const rec = {
69
+ id, name, path,
70
+ durFrames: toFrames(durSec, fps),
71
+ hasVideo,
72
+ sampleRate,
73
+ channels,
74
+ };
75
+ fileById.set(path, rec);
76
+ return rec;
77
+ }
78
+ for (const s of opts.segments) {
79
+ if (!existsSync(s.path)) {
80
+ throw new Error(`exportPremiereXml: missing segment ${s.path}`);
81
+ }
82
+ s._file =
83
+ await registerFile(s.path, true, s.durSec, s.label);
84
+ }
85
+ for (const a of opts.audio) {
86
+ if (!existsSync(a.path)) {
87
+ throw new Error(`exportPremiereXml: missing audio file ${a.path}`);
88
+ }
89
+ a._file =
90
+ await registerFile(a.path, false, a.audioDur, a.label);
91
+ }
92
+ // 2. Bucket audio clips by Premiere track index.
93
+ const audioByTrack = { 1: [], 2: [], 3: [] };
94
+ for (const a of opts.audio) {
95
+ audioByTrack[laneToTrackIndex(a.lane)].push(a);
96
+ }
97
+ const masterDurSec = opts.totalDurationSec
98
+ ?? opts.segments.reduce((acc, s) => Math.max(acc, s.startSec + s.durSec), 0);
99
+ const masterDurFrames = toFrames(masterDurSec, fps);
100
+ // 3. Track files that have already emitted their <pathurl>. Subsequent
101
+ // references in <clipitem> use the bare <file id="..."/> form.
102
+ const emittedFiles = new Set();
103
+ function fileElement(rec, includePath, durFrames) {
104
+ if (!includePath) {
105
+ return [` <file id="${rec.id}"/>`];
106
+ }
107
+ const uri = pathToFileUri(rec.path);
108
+ const lines = [
109
+ ` <file id="${rec.id}">`,
110
+ ` <name>${xmlEscape(rec.name)}</name>`,
111
+ ` <pathurl>${uri}</pathurl>`,
112
+ ` <rate><ntsc>FALSE</ntsc><timebase>${fps}</timebase></rate>`,
113
+ ` <duration>${rec.durFrames}</duration>`,
114
+ ` <media>`,
115
+ ];
116
+ if (rec.hasVideo) {
117
+ lines.push(` <video>`);
118
+ lines.push(` <samplecharacteristics>`);
119
+ lines.push(` <rate><ntsc>FALSE</ntsc><timebase>${fps}</timebase></rate>`);
120
+ lines.push(` <width>${width}</width>`);
121
+ lines.push(` <height>${height}</height>`);
122
+ lines.push(` </samplecharacteristics>`);
123
+ lines.push(` </video>`);
124
+ }
125
+ lines.push(` <audio>`);
126
+ lines.push(` <samplecharacteristics>`);
127
+ lines.push(` <depth>16</depth>`);
128
+ lines.push(` <samplerate>${rec.sampleRate}</samplerate>`);
129
+ lines.push(` </samplecharacteristics>`);
130
+ lines.push(` <channelcount>${rec.channels}</channelcount>`);
131
+ lines.push(` </audio>`);
132
+ lines.push(` </media>`);
133
+ lines.push(` </file>`);
134
+ // Suppress full pathurl on subsequent references to this same file.
135
+ emittedFiles.add(rec.id);
136
+ return lines;
137
+ }
138
+ // 4. Emit XML.
139
+ const lines = [];
140
+ lines.push(`<?xml version="1.0" encoding="UTF-8"?>`);
141
+ lines.push(`<!DOCTYPE xmeml>`);
142
+ lines.push(`<xmeml version="5">`);
143
+ lines.push(` <sequence id="seq-1">`);
144
+ lines.push(` <name>${xmlEscape(sequenceName)}</name>`);
145
+ lines.push(` <duration>${masterDurFrames}</duration>`);
146
+ lines.push(` <rate><ntsc>FALSE</ntsc><timebase>${fps}</timebase></rate>`);
147
+ lines.push(` <media>`);
148
+ // --- Video track (V1) ---
149
+ lines.push(` <video>`);
150
+ lines.push(` <format>`);
151
+ lines.push(` <samplecharacteristics>`);
152
+ lines.push(` <rate><ntsc>FALSE</ntsc><timebase>${fps}</timebase></rate>`);
153
+ lines.push(` <width>${width}</width>`);
154
+ lines.push(` <height>${height}</height>`);
155
+ lines.push(` </samplecharacteristics>`);
156
+ lines.push(` </format>`);
157
+ lines.push(` <track>`);
158
+ for (let i = 0; i < opts.segments.length; i++) {
159
+ const s = opts.segments[i];
160
+ const rec = s._file;
161
+ const startFr = toFrames(s.startSec, fps);
162
+ const endFr = startFr + toFrames(s.durSec, fps);
163
+ const includePath = !emittedFiles.has(rec.id);
164
+ lines.push(` <clipitem id="vclip-${i + 1}">`);
165
+ lines.push(` <name>${xmlEscape(s.label)}</name>`);
166
+ lines.push(` <duration>${rec.durFrames}</duration>`);
167
+ lines.push(` <rate><ntsc>FALSE</ntsc><timebase>${fps}</timebase></rate>`);
168
+ lines.push(` <start>${startFr}</start>`);
169
+ lines.push(` <end>${endFr}</end>`);
170
+ lines.push(` <in>0</in>`);
171
+ lines.push(` <out>${toFrames(s.durSec, fps)}</out>`);
172
+ for (const ln of fileElement(rec, includePath, rec.durFrames))
173
+ lines.push(ln);
174
+ lines.push(` </clipitem>`);
175
+ }
176
+ lines.push(` </track>`);
177
+ lines.push(` </video>`);
178
+ // --- Audio tracks (A1 dialogue, A2 SFX, A3 music) ---
179
+ lines.push(` <audio>`);
180
+ for (const trackIdx of [1, 2, 3]) {
181
+ lines.push(` <track>`);
182
+ let clipNum = 0;
183
+ for (const a of audioByTrack[trackIdx]) {
184
+ const rec = a._file;
185
+ const startFr = toFrames(a.startSec, fps);
186
+ const endFr = startFr + toFrames(a.audioDur, fps);
187
+ const includePath = !emittedFiles.has(rec.id);
188
+ clipNum += 1;
189
+ lines.push(` <clipitem id="a${trackIdx}-clip-${clipNum}">`);
190
+ lines.push(` <name>${xmlEscape(a.label)}</name>`);
191
+ lines.push(` <duration>${rec.durFrames}</duration>`);
192
+ lines.push(` <rate><ntsc>FALSE</ntsc><timebase>${fps}</timebase></rate>`);
193
+ lines.push(` <start>${startFr}</start>`);
194
+ lines.push(` <end>${endFr}</end>`);
195
+ lines.push(` <in>0</in>`);
196
+ lines.push(` <out>${toFrames(a.audioDur, fps)}</out>`);
197
+ for (const ln of fileElement(rec, includePath, rec.durFrames))
198
+ lines.push(ln);
199
+ lines.push(` </clipitem>`);
200
+ }
201
+ lines.push(` </track>`);
202
+ }
203
+ lines.push(` </audio>`);
204
+ lines.push(` </media>`);
205
+ lines.push(` </sequence>`);
206
+ lines.push(`</xmeml>`);
207
+ await mkdir(dirname(opts.outputPath), { recursive: true });
208
+ await writeFile(opts.outputPath, lines.join('\n') + '\n', 'utf-8');
209
+ return opts.outputPath;
210
+ }
211
+ //# sourceMappingURL=premiere-xmeml.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"premiere-xmeml.js","sourceRoot":"","sources":["../../../src/mini-drama/timeline-export/premiere-xmeml.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import type { TimelineSegment } from './types.js';
2
+ /**
3
+ * Convert a duration in seconds to FCPXML's rational form, snapped to the
4
+ * nearest frame boundary.
5
+ *
6
+ * Example: `toRationalTime(181.04, 24)` → `"4345/24s"`.
7
+ */
8
+ export declare function toRationalTime(seconds: number, fps: number): string;
9
+ /**
10
+ * Convert a duration in seconds to an integer frame count.
11
+ *
12
+ * Premiere's xmeml v5 uses integer frames for `start` / `end` / `in` / `out`
13
+ * fields — NOT the rational FCPXML form.
14
+ */
15
+ export declare function toFrames(seconds: number, fps: number): number;
16
+ /**
17
+ * Convert a filesystem path to a `file://` URI. URI-encodes spaces and most
18
+ * special characters but leaves `/` and `:` alone.
19
+ *
20
+ * Used by FCPXML 1.10 (`<media-rep src>`) and Premiere xmeml v5 (`<pathurl>`).
21
+ * The DaVinci exporter has its own variant that avoids URI encoding for
22
+ * non-ASCII paths on Linux Resolve versions.
23
+ */
24
+ export declare function pathToFileUri(p: string): string;
25
+ /**
26
+ * Escape a string for inclusion in XML text or attribute values.
27
+ *
28
+ * Handles the five XML entity characters. Suitable for both element content
29
+ * and attribute values; the result is safe to drop into either context.
30
+ */
31
+ export declare function xmlEscape(s: string): string;
32
+ /**
33
+ * ffprobe wrapper returning normalized audio info for a single file.
34
+ *
35
+ * Returns sane defaults (48 kHz / stereo) if the probe fails or the file
36
+ * has no audio stream. The FCPXML 1.10 exporter requires both
37
+ * `audioRate` and `audioChannels` on every `<asset>` declaration; lying
38
+ * about either is fine as long as the values exist.
39
+ */
40
+ export declare function probeAudioInfo(path: string): Promise<{
41
+ durSec: number;
42
+ sampleRate: number;
43
+ channels: number;
44
+ }>;
45
+ /**
46
+ * Locate the spine segment index that contains a given timeline timestamp.
47
+ *
48
+ * Past-end timestamps clamp to the last segment so off-by-frame audio
49
+ * placement at the very end of the episode still emits a clip rather than
50
+ * being dropped.
51
+ */
52
+ export declare function segmentContaining(segments: TimelineSegment[], timeSec: number): number;
53
+ //# sourceMappingURL=probe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"probe.d.ts","sourceRoot":"","sources":["../../../src/mini-drama/timeline-export/probe.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAIlD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAGnE;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAI3C;AAED;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC1D,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC,CAcD;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,eAAe,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAMtF"}
@@ -0,0 +1,89 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Format-neutral helpers shared across all timeline exporters.
3
+ //
4
+ // Pure utilities — no exporter-specific logic. The FCPXML, Premiere, and
5
+ // DaVinci exporters all share these.
6
+ // ---------------------------------------------------------------------------
7
+ import { execFile } from 'node:child_process';
8
+ import { promisify } from 'node:util';
9
+ const execFileAsync = promisify(execFile);
10
+ /**
11
+ * Convert a duration in seconds to FCPXML's rational form, snapped to the
12
+ * nearest frame boundary.
13
+ *
14
+ * Example: `toRationalTime(181.04, 24)` → `"4345/24s"`.
15
+ */
16
+ export function toRationalTime(seconds, fps) {
17
+ const frames = Math.max(0, Math.round(seconds * fps));
18
+ return `${frames}/${fps}s`;
19
+ }
20
+ /**
21
+ * Convert a duration in seconds to an integer frame count.
22
+ *
23
+ * Premiere's xmeml v5 uses integer frames for `start` / `end` / `in` / `out`
24
+ * fields — NOT the rational FCPXML form.
25
+ */
26
+ export function toFrames(seconds, fps) {
27
+ return Math.max(0, Math.round(seconds * fps));
28
+ }
29
+ /**
30
+ * Convert a filesystem path to a `file://` URI. URI-encodes spaces and most
31
+ * special characters but leaves `/` and `:` alone.
32
+ *
33
+ * Used by FCPXML 1.10 (`<media-rep src>`) and Premiere xmeml v5 (`<pathurl>`).
34
+ * The DaVinci exporter has its own variant that avoids URI encoding for
35
+ * non-ASCII paths on Linux Resolve versions.
36
+ */
37
+ export function pathToFileUri(p) {
38
+ return 'file://' + encodeURI(p).replace(/'/g, '%27');
39
+ }
40
+ /**
41
+ * Escape a string for inclusion in XML text or attribute values.
42
+ *
43
+ * Handles the five XML entity characters. Suitable for both element content
44
+ * and attribute values; the result is safe to drop into either context.
45
+ */
46
+ export function xmlEscape(s) {
47
+ return String(s).replace(/[<>&'"]/g, c => ({
48
+ '<': '&lt;', '>': '&gt;', '&': '&amp;', "'": '&apos;', '"': '&quot;',
49
+ }[c]));
50
+ }
51
+ /**
52
+ * ffprobe wrapper returning normalized audio info for a single file.
53
+ *
54
+ * Returns sane defaults (48 kHz / stereo) if the probe fails or the file
55
+ * has no audio stream. The FCPXML 1.10 exporter requires both
56
+ * `audioRate` and `audioChannels` on every `<asset>` declaration; lying
57
+ * about either is fine as long as the values exist.
58
+ */
59
+ export async function probeAudioInfo(path) {
60
+ const { stdout } = await execFileAsync('ffprobe', [
61
+ '-v', 'quiet',
62
+ '-print_format', 'json',
63
+ '-show_format', '-show_streams',
64
+ path,
65
+ ]);
66
+ const j = JSON.parse(stdout);
67
+ const a = j.streams.find((s) => s.codec_type === 'audio');
68
+ return {
69
+ durSec: parseFloat(a?.duration ?? j.format.duration ?? '0'),
70
+ sampleRate: parseInt(a?.sample_rate ?? '48000', 10),
71
+ channels: parseInt(a?.channels ?? '2', 10),
72
+ };
73
+ }
74
+ /**
75
+ * Locate the spine segment index that contains a given timeline timestamp.
76
+ *
77
+ * Past-end timestamps clamp to the last segment so off-by-frame audio
78
+ * placement at the very end of the episode still emits a clip rather than
79
+ * being dropped.
80
+ */
81
+ export function segmentContaining(segments, timeSec) {
82
+ for (let i = 0; i < segments.length; i++) {
83
+ const s = segments[i];
84
+ if (timeSec >= s.startSec && timeSec < s.startSec + s.durSec)
85
+ return i;
86
+ }
87
+ return Math.max(0, segments.length - 1);
88
+ }
89
+ //# sourceMappingURL=probe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"probe.js","sourceRoot":"","sources":["../../../src/mini-drama/timeline-export/probe.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,+DAA+D;AAC/D,EAAE;AACF,yEAAyE;AACzE,qCAAqC;AACrC,8EAA8E;AAE9E,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAGtC,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAE1C;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,OAAe,EAAE,GAAW;IACzD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,GAAG,CAAC,CAAC,CAAC;IACtD,OAAO,GAAG,MAAM,IAAI,GAAG,GAAG,CAAC;AAC7B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CAAC,OAAe,EAAE,GAAW;IACnD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,GAAG,CAAC,CAAC,CAAC;AAChD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,CAAS;IACrC,OAAO,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AACvD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CAAC,CAAS;IACjC,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;QACzC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ;KACrE,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC;AACV,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAY;IAK/C,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,SAAS,EAAE;QAChD,IAAI,EAAE,OAAO;QACb,eAAe,EAAE,MAAM;QACvB,cAAc,EAAE,eAAe;QAC/B,IAAI;KACL,CAAC,CAAC;IACH,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC7B,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAyB,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,OAAO,CAAC,CAAC;IAClF,OAAO;QACL,MAAM,EAAE,UAAU,CAAC,CAAC,EAAE,QAAQ,IAAI,CAAC,CAAC,MAAM,CAAC,QAAQ,IAAI,GAAG,CAAC;QAC3D,UAAU,EAAE,QAAQ,CAAC,CAAC,EAAE,WAAW,IAAI,OAAO,EAAE,EAAE,CAAC;QACnD,QAAQ,EAAE,QAAQ,CAAC,CAAC,EAAE,QAAQ,IAAI,GAAG,EAAE,EAAE,CAAC;KAC3C,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAA2B,EAAE,OAAe;IAC5E,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACzC,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,OAAO,IAAI,CAAC,CAAC,QAAQ,IAAI,OAAO,GAAG,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,MAAM;YAAE,OAAO,CAAC,CAAC;IACzE,CAAC;IACD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AAC1C,CAAC"}