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.
- package/CHANGELOG.md +28 -0
- package/LICENSE +21 -0
- package/README.md +598 -0
- package/dist/assembly/manifest-builder.d.ts +8 -0
- package/dist/assembly/manifest-builder.d.ts.map +1 -0
- package/dist/assembly/manifest-builder.js +196 -0
- package/dist/assembly/manifest-builder.js.map +1 -0
- package/dist/assembly/remotion-scaffold.d.ts +14 -0
- package/dist/assembly/remotion-scaffold.d.ts.map +1 -0
- package/dist/assembly/remotion-scaffold.js +292 -0
- package/dist/assembly/remotion-scaffold.js.map +1 -0
- package/dist/assembly/types.d.ts +67 -0
- package/dist/assembly/types.d.ts.map +1 -0
- package/dist/assembly/types.js +5 -0
- package/dist/assembly/types.js.map +1 -0
- package/dist/characters/describer.d.ts +32 -0
- package/dist/characters/describer.d.ts.map +1 -0
- package/dist/characters/describer.js +309 -0
- package/dist/characters/describer.js.map +1 -0
- package/dist/characters/extractor.d.ts +27 -0
- package/dist/characters/extractor.d.ts.map +1 -0
- package/dist/characters/extractor.js +274 -0
- package/dist/characters/extractor.js.map +1 -0
- package/dist/characters/reference-manager.d.ts +95 -0
- package/dist/characters/reference-manager.d.ts.map +1 -0
- package/dist/characters/reference-manager.js +346 -0
- package/dist/characters/reference-manager.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +310 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +22 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +66 -0
- package/dist/config.js.map +1 -0
- package/dist/editing/aligner.d.ts +44 -0
- package/dist/editing/aligner.d.ts.map +1 -0
- package/dist/editing/aligner.js +201 -0
- package/dist/editing/aligner.js.map +1 -0
- package/dist/editing/edl.d.ts +45 -0
- package/dist/editing/edl.d.ts.map +1 -0
- package/dist/editing/edl.js +133 -0
- package/dist/editing/edl.js.map +1 -0
- package/dist/editing/overlays.d.ts +97 -0
- package/dist/editing/overlays.d.ts.map +1 -0
- package/dist/editing/overlays.js +41 -0
- package/dist/editing/overlays.js.map +1 -0
- package/dist/editing/packer.d.ts +52 -0
- package/dist/editing/packer.d.ts.map +1 -0
- package/dist/editing/packer.js +170 -0
- package/dist/editing/packer.js.map +1 -0
- package/dist/editing/providers/whisper-cpp.d.ts +32 -0
- package/dist/editing/providers/whisper-cpp.d.ts.map +1 -0
- package/dist/editing/providers/whisper-cpp.js +239 -0
- package/dist/editing/providers/whisper-cpp.js.map +1 -0
- package/dist/editing/render.d.ts +36 -0
- package/dist/editing/render.d.ts.map +1 -0
- package/dist/editing/render.js +227 -0
- package/dist/editing/render.js.map +1 -0
- package/dist/editing/self-eval.d.ts +48 -0
- package/dist/editing/self-eval.d.ts.map +1 -0
- package/dist/editing/self-eval.js +293 -0
- package/dist/editing/self-eval.js.map +1 -0
- package/dist/editing/silence.d.ts +76 -0
- package/dist/editing/silence.d.ts.map +1 -0
- package/dist/editing/silence.js +189 -0
- package/dist/editing/silence.js.map +1 -0
- package/dist/editing/types.d.ts +178 -0
- package/dist/editing/types.d.ts.map +1 -0
- package/dist/editing/types.js +13 -0
- package/dist/editing/types.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/interactive.d.ts +12 -0
- package/dist/interactive.d.ts.map +1 -0
- package/dist/interactive.js +78 -0
- package/dist/interactive.js.map +1 -0
- package/dist/mini-drama/assembler.d.ts +61 -0
- package/dist/mini-drama/assembler.d.ts.map +1 -0
- package/dist/mini-drama/assembler.js +466 -0
- package/dist/mini-drama/assembler.js.map +1 -0
- package/dist/mini-drama/audio-mix.d.ts +65 -0
- package/dist/mini-drama/audio-mix.d.ts.map +1 -0
- package/dist/mini-drama/audio-mix.js +118 -0
- package/dist/mini-drama/audio-mix.js.map +1 -0
- package/dist/mini-drama/cli.d.ts +3 -0
- package/dist/mini-drama/cli.d.ts.map +1 -0
- package/dist/mini-drama/cli.js +2869 -0
- package/dist/mini-drama/cli.js.map +1 -0
- package/dist/mini-drama/fcpxml-export.d.ts +2 -0
- package/dist/mini-drama/fcpxml-export.d.ts.map +1 -0
- package/dist/mini-drama/fcpxml-export.js +5 -0
- package/dist/mini-drama/fcpxml-export.js.map +1 -0
- package/dist/mini-drama/generation-planner.d.ts +24 -0
- package/dist/mini-drama/generation-planner.d.ts.map +1 -0
- package/dist/mini-drama/generation-planner.js +304 -0
- package/dist/mini-drama/generation-planner.js.map +1 -0
- package/dist/mini-drama/location-generator.d.ts +23 -0
- package/dist/mini-drama/location-generator.d.ts.map +1 -0
- package/dist/mini-drama/location-generator.js +144 -0
- package/dist/mini-drama/location-generator.js.map +1 -0
- package/dist/mini-drama/music-cues.d.ts +96 -0
- package/dist/mini-drama/music-cues.d.ts.map +1 -0
- package/dist/mini-drama/music-cues.js +332 -0
- package/dist/mini-drama/music-cues.js.map +1 -0
- package/dist/mini-drama/panel-fixer.d.ts +12 -0
- package/dist/mini-drama/panel-fixer.d.ts.map +1 -0
- package/dist/mini-drama/panel-fixer.js +319 -0
- package/dist/mini-drama/panel-fixer.js.map +1 -0
- package/dist/mini-drama/prompt-builder.d.ts +149 -0
- package/dist/mini-drama/prompt-builder.d.ts.map +1 -0
- package/dist/mini-drama/prompt-builder.js +722 -0
- package/dist/mini-drama/prompt-builder.js.map +1 -0
- package/dist/mini-drama/reference-slots.d.ts +31 -0
- package/dist/mini-drama/reference-slots.d.ts.map +1 -0
- package/dist/mini-drama/reference-slots.js +161 -0
- package/dist/mini-drama/reference-slots.js.map +1 -0
- package/dist/mini-drama/shot-paths.d.ts +77 -0
- package/dist/mini-drama/shot-paths.d.ts.map +1 -0
- package/dist/mini-drama/shot-paths.js +101 -0
- package/dist/mini-drama/shot-paths.js.map +1 -0
- package/dist/mini-drama/storyboard-reference-generator.d.ts +47 -0
- package/dist/mini-drama/storyboard-reference-generator.d.ts.map +1 -0
- package/dist/mini-drama/storyboard-reference-generator.js +293 -0
- package/dist/mini-drama/storyboard-reference-generator.js.map +1 -0
- package/dist/mini-drama/subtitle-generator.d.ts +12 -0
- package/dist/mini-drama/subtitle-generator.d.ts.map +1 -0
- package/dist/mini-drama/subtitle-generator.js +139 -0
- package/dist/mini-drama/subtitle-generator.js.map +1 -0
- package/dist/mini-drama/timeline-export/davinci-fcpxml.d.ts +3 -0
- package/dist/mini-drama/timeline-export/davinci-fcpxml.d.ts.map +1 -0
- package/dist/mini-drama/timeline-export/davinci-fcpxml.js +150 -0
- package/dist/mini-drama/timeline-export/davinci-fcpxml.js.map +1 -0
- package/dist/mini-drama/timeline-export/fcpxml.d.ts +11 -0
- package/dist/mini-drama/timeline-export/fcpxml.d.ts.map +1 -0
- package/dist/mini-drama/timeline-export/fcpxml.js +140 -0
- package/dist/mini-drama/timeline-export/fcpxml.js.map +1 -0
- package/dist/mini-drama/timeline-export/index.d.ts +13 -0
- package/dist/mini-drama/timeline-export/index.d.ts.map +1 -0
- package/dist/mini-drama/timeline-export/index.js +26 -0
- package/dist/mini-drama/timeline-export/index.js.map +1 -0
- package/dist/mini-drama/timeline-export/premiere-xmeml.d.ts +3 -0
- package/dist/mini-drama/timeline-export/premiere-xmeml.d.ts.map +1 -0
- package/dist/mini-drama/timeline-export/premiere-xmeml.js +211 -0
- package/dist/mini-drama/timeline-export/premiere-xmeml.js.map +1 -0
- package/dist/mini-drama/timeline-export/probe.d.ts +53 -0
- package/dist/mini-drama/timeline-export/probe.d.ts.map +1 -0
- package/dist/mini-drama/timeline-export/probe.js +89 -0
- package/dist/mini-drama/timeline-export/probe.js.map +1 -0
- package/dist/mini-drama/timeline-export/types.d.ts +57 -0
- package/dist/mini-drama/timeline-export/types.d.ts.map +1 -0
- package/dist/mini-drama/timeline-export/types.js +14 -0
- package/dist/mini-drama/timeline-export/types.js.map +1 -0
- package/dist/mini-drama/video-generator.d.ts +22 -0
- package/dist/mini-drama/video-generator.d.ts.map +1 -0
- package/dist/mini-drama/video-generator.js +1305 -0
- package/dist/mini-drama/video-generator.js.map +1 -0
- package/dist/mini-drama/voice-reference.d.ts +42 -0
- package/dist/mini-drama/voice-reference.d.ts.map +1 -0
- package/dist/mini-drama/voice-reference.js +170 -0
- package/dist/mini-drama/voice-reference.js.map +1 -0
- package/dist/output/html-renderer.d.ts +2 -0
- package/dist/output/html-renderer.d.ts.map +1 -0
- package/dist/output/html-renderer.js +37 -0
- package/dist/output/html-renderer.js.map +1 -0
- package/dist/parsers/fountain-parser.d.ts +34 -0
- package/dist/parsers/fountain-parser.d.ts.map +1 -0
- package/dist/parsers/fountain-parser.js +71 -0
- package/dist/parsers/fountain-parser.js.map +1 -0
- package/dist/parsers/pdf-parser.d.ts +24 -0
- package/dist/parsers/pdf-parser.d.ts.map +1 -0
- package/dist/parsers/pdf-parser.js +237 -0
- package/dist/parsers/pdf-parser.js.map +1 -0
- package/dist/parsers/scene-extractor.d.ts +41 -0
- package/dist/parsers/scene-extractor.d.ts.map +1 -0
- package/dist/parsers/scene-extractor.js +400 -0
- package/dist/parsers/scene-extractor.js.map +1 -0
- package/dist/series/manager.d.ts +64 -0
- package/dist/series/manager.d.ts.map +1 -0
- package/dist/series/manager.js +282 -0
- package/dist/series/manager.js.map +1 -0
- package/dist/series/types.d.ts +641 -0
- package/dist/series/types.d.ts.map +1 -0
- package/dist/series/types.js +304 -0
- package/dist/series/types.js.map +1 -0
- package/dist/storyboard/assembler.d.ts +134 -0
- package/dist/storyboard/assembler.d.ts.map +1 -0
- package/dist/storyboard/assembler.js +319 -0
- package/dist/storyboard/assembler.js.map +1 -0
- package/dist/storyboard/prompt-builder.d.ts +113 -0
- package/dist/storyboard/prompt-builder.d.ts.map +1 -0
- package/dist/storyboard/prompt-builder.js +578 -0
- package/dist/storyboard/prompt-builder.js.map +1 -0
- package/dist/storyboard/shot-planner.d.ts +34 -0
- package/dist/storyboard/shot-planner.d.ts.map +1 -0
- package/dist/storyboard/shot-planner.js +446 -0
- package/dist/storyboard/shot-planner.js.map +1 -0
- package/dist/user-config.d.ts +15 -0
- package/dist/user-config.d.ts.map +1 -0
- package/dist/user-config.js +103 -0
- package/dist/user-config.js.map +1 -0
- package/dist/venice/audio-preflight.d.ts +80 -0
- package/dist/venice/audio-preflight.d.ts.map +1 -0
- package/dist/venice/audio-preflight.js +142 -0
- package/dist/venice/audio-preflight.js.map +1 -0
- package/dist/venice/audio.d.ts +88 -0
- package/dist/venice/audio.d.ts.map +1 -0
- package/dist/venice/audio.js +234 -0
- package/dist/venice/audio.js.map +1 -0
- package/dist/venice/client.d.ts +86 -0
- package/dist/venice/client.d.ts.map +1 -0
- package/dist/venice/client.js +375 -0
- package/dist/venice/client.js.map +1 -0
- package/dist/venice/edit.d.ts +57 -0
- package/dist/venice/edit.d.ts.map +1 -0
- package/dist/venice/edit.js +100 -0
- package/dist/venice/edit.js.map +1 -0
- package/dist/venice/generate.d.ts +47 -0
- package/dist/venice/generate.d.ts.map +1 -0
- package/dist/venice/generate.js +150 -0
- package/dist/venice/generate.js.map +1 -0
- package/dist/venice/image-bytes.d.ts +26 -0
- package/dist/venice/image-bytes.d.ts.map +1 -0
- package/dist/venice/image-bytes.js +101 -0
- package/dist/venice/image-bytes.js.map +1 -0
- package/dist/venice/models.d.ts +143 -0
- package/dist/venice/models.d.ts.map +1 -0
- package/dist/venice/models.js +984 -0
- package/dist/venice/models.js.map +1 -0
- package/dist/venice/multi-edit.d.ts +40 -0
- package/dist/venice/multi-edit.d.ts.map +1 -0
- package/dist/venice/multi-edit.js +97 -0
- package/dist/venice/multi-edit.js.map +1 -0
- package/dist/venice/provenance.d.ts +66 -0
- package/dist/venice/provenance.d.ts.map +1 -0
- package/dist/venice/provenance.js +151 -0
- package/dist/venice/provenance.js.map +1 -0
- package/dist/venice/recipe.d.ts +69 -0
- package/dist/venice/recipe.d.ts.map +1 -0
- package/dist/venice/recipe.js +103 -0
- package/dist/venice/recipe.js.map +1 -0
- package/dist/venice/rejection.d.ts +63 -0
- package/dist/venice/rejection.d.ts.map +1 -0
- package/dist/venice/rejection.js +111 -0
- package/dist/venice/rejection.js.map +1 -0
- package/dist/venice/seedance-preflight.d.ts +38 -0
- package/dist/venice/seedance-preflight.d.ts.map +1 -0
- package/dist/venice/seedance-preflight.js +29 -0
- package/dist/venice/seedance-preflight.js.map +1 -0
- package/dist/venice/types.d.ts +212 -0
- package/dist/venice/types.d.ts.map +1 -0
- package/dist/venice/types.js +9 -0
- package/dist/venice/types.js.map +1 -0
- package/dist/venice/video.d.ts +77 -0
- package/dist/venice/video.d.ts.map +1 -0
- package/dist/venice/video.js +163 -0
- package/dist/venice/video.js.map +1 -0
- package/dist/venice/voices.d.ts +40 -0
- package/dist/venice/voices.d.ts.map +1 -0
- package/dist/venice/voices.js +194 -0
- package/dist/venice/voices.js.map +1 -0
- 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 @@
|
|
|
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
|
+
'<': '<', '>': '>', '&': '&', "'": ''', '"': '"',
|
|
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"}
|