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,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ground-truth script alignment for Venice-generated content.
|
|
3
|
+
*
|
|
4
|
+
* When we already know the exact spoken text (from `ShotScript.vo` or a
|
|
5
|
+
* TTS script), we can produce more accurate word timings than pure ASR by
|
|
6
|
+
* mapping the script's canonical words onto whisper.cpp's detected word
|
|
7
|
+
* timings via longest-common-subsequence alignment.
|
|
8
|
+
*
|
|
9
|
+
* The output is still a `WordTiming[]`, but the `word` field is the
|
|
10
|
+
* script's canonical word (with correct capitalization / punctuation)
|
|
11
|
+
* rather than whisper's best guess. Unmatched script words are linearly
|
|
12
|
+
* interpolated between their neighbors.
|
|
13
|
+
*
|
|
14
|
+
* This matters in practice because TTS voices often produce subtle
|
|
15
|
+
* mispronunciations or swallowed words that whisper transcribes as
|
|
16
|
+
* homophones ("their" vs "there", "a" vs "uh"). For editing purposes
|
|
17
|
+
* we want the script's words — that's what the captions, shot manifest,
|
|
18
|
+
* and VO_TEXT array refer to.
|
|
19
|
+
*/
|
|
20
|
+
function normalize(word) {
|
|
21
|
+
return word
|
|
22
|
+
.toLowerCase()
|
|
23
|
+
.replace(/[^a-z0-9']/g, '')
|
|
24
|
+
.replace(/'/g, '');
|
|
25
|
+
}
|
|
26
|
+
function tokenizeScript(script) {
|
|
27
|
+
return script
|
|
28
|
+
.replace(/\.{2,}/g, ' ')
|
|
29
|
+
.split(/\s+/)
|
|
30
|
+
.map((w) => w.trim())
|
|
31
|
+
.filter((w) => w.length > 0);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Build an LCS-style alignment matrix between script words and ASR words
|
|
35
|
+
* on normalized forms. Returns parallel arrays of matched indices.
|
|
36
|
+
*/
|
|
37
|
+
function alignLcs(scriptWords, asrWords) {
|
|
38
|
+
const a = scriptWords.map(normalize);
|
|
39
|
+
const b = asrWords.map((w) => normalize(w.word));
|
|
40
|
+
const m = a.length;
|
|
41
|
+
const n = b.length;
|
|
42
|
+
// DP table of LCS lengths.
|
|
43
|
+
const dp = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
|
|
44
|
+
for (let i = 1; i <= m; i++) {
|
|
45
|
+
for (let j = 1; j <= n; j++) {
|
|
46
|
+
if (a[i - 1] && b[j - 1] && a[i - 1] === b[j - 1]) {
|
|
47
|
+
dp[i][j] = dp[i - 1][j - 1] + 1;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const out = [];
|
|
55
|
+
let i = m;
|
|
56
|
+
let j = n;
|
|
57
|
+
while (i > 0 && j > 0) {
|
|
58
|
+
if (a[i - 1] === b[j - 1] && a[i - 1]) {
|
|
59
|
+
out.push({ scriptIdx: i - 1, asrIdx: j - 1 });
|
|
60
|
+
i--;
|
|
61
|
+
j--;
|
|
62
|
+
}
|
|
63
|
+
else if (dp[i - 1][j] >= dp[i][j - 1]) {
|
|
64
|
+
i--;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
j--;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return out.reverse();
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Align a ground-truth script against whisper word timings.
|
|
74
|
+
*/
|
|
75
|
+
export function alignScriptToWords(script, asrWords, options = {}) {
|
|
76
|
+
const onMissing = options.onMissing ?? 'interpolate';
|
|
77
|
+
const scriptWords = tokenizeScript(script);
|
|
78
|
+
if (scriptWords.length === 0 || asrWords.length === 0)
|
|
79
|
+
return [];
|
|
80
|
+
const matches = alignLcs(scriptWords, asrWords);
|
|
81
|
+
// Produce timings per script word.
|
|
82
|
+
const timings = new Array(scriptWords.length).fill(null);
|
|
83
|
+
// Copy timings from matched ASR words (script wording wins).
|
|
84
|
+
for (const { scriptIdx, asrIdx } of matches) {
|
|
85
|
+
const asr = asrWords[asrIdx];
|
|
86
|
+
timings[scriptIdx] = {
|
|
87
|
+
word: scriptWords[scriptIdx],
|
|
88
|
+
startSec: asr.startSec,
|
|
89
|
+
endSec: asr.endSec,
|
|
90
|
+
confidence: asr.confidence,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
if (onMissing === 'drop') {
|
|
94
|
+
return timings.filter((t) => t !== null);
|
|
95
|
+
}
|
|
96
|
+
// Interpolate missing timings based on surrounding anchors.
|
|
97
|
+
const firstMatched = timings.findIndex((t) => t !== null);
|
|
98
|
+
const lastMatched = (() => {
|
|
99
|
+
for (let k = timings.length - 1; k >= 0; k--)
|
|
100
|
+
if (timings[k])
|
|
101
|
+
return k;
|
|
102
|
+
return -1;
|
|
103
|
+
})();
|
|
104
|
+
if (firstMatched === -1) {
|
|
105
|
+
// Nothing matched — fall back to linear distribution across ASR span.
|
|
106
|
+
const firstAsr = asrWords[0];
|
|
107
|
+
const lastAsr = asrWords[asrWords.length - 1];
|
|
108
|
+
const span = Math.max(0.001, lastAsr.endSec - firstAsr.startSec);
|
|
109
|
+
const per = span / scriptWords.length;
|
|
110
|
+
return scriptWords.map((w, k) => ({
|
|
111
|
+
word: w,
|
|
112
|
+
startSec: firstAsr.startSec + k * per,
|
|
113
|
+
endSec: firstAsr.startSec + (k + 1) * per,
|
|
114
|
+
}));
|
|
115
|
+
}
|
|
116
|
+
// Extrapolate before the first matched word.
|
|
117
|
+
if (firstMatched > 0 && timings[firstMatched]) {
|
|
118
|
+
const anchor = timings[firstMatched];
|
|
119
|
+
const pre = firstMatched;
|
|
120
|
+
// Assume ~0.28s per pre-word if no earlier ASR — conservative.
|
|
121
|
+
const avg = 0.28;
|
|
122
|
+
for (let k = 0; k < firstMatched; k++) {
|
|
123
|
+
const offset = (firstMatched - k) * avg;
|
|
124
|
+
timings[k] = {
|
|
125
|
+
word: scriptWords[k],
|
|
126
|
+
startSec: Math.max(0, anchor.startSec - offset),
|
|
127
|
+
endSec: Math.max(0, anchor.startSec - offset + avg * 0.8),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// Extrapolate after the last matched word.
|
|
132
|
+
if (lastMatched !== -1 && lastMatched < scriptWords.length - 1 && timings[lastMatched]) {
|
|
133
|
+
const anchor = timings[lastMatched];
|
|
134
|
+
const avg = 0.28;
|
|
135
|
+
for (let k = lastMatched + 1; k < scriptWords.length; k++) {
|
|
136
|
+
const offset = (k - lastMatched) * avg;
|
|
137
|
+
timings[k] = {
|
|
138
|
+
word: scriptWords[k],
|
|
139
|
+
startSec: anchor.endSec + offset - avg,
|
|
140
|
+
endSec: anchor.endSec + offset,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// Interpolate gaps between matched words.
|
|
145
|
+
for (let k = 0; k < timings.length; k++) {
|
|
146
|
+
if (timings[k])
|
|
147
|
+
continue;
|
|
148
|
+
// Find anchor before and after.
|
|
149
|
+
let before = k - 1;
|
|
150
|
+
while (before >= 0 && !timings[before])
|
|
151
|
+
before--;
|
|
152
|
+
let after = k + 1;
|
|
153
|
+
while (after < timings.length && !timings[after])
|
|
154
|
+
after++;
|
|
155
|
+
if (before < 0 || after >= timings.length)
|
|
156
|
+
continue;
|
|
157
|
+
const b = timings[before];
|
|
158
|
+
const a = timings[after];
|
|
159
|
+
const span = Math.max(0.001, a.startSec - b.endSec);
|
|
160
|
+
const slots = after - before;
|
|
161
|
+
const localIdx = k - before;
|
|
162
|
+
const per = span / slots;
|
|
163
|
+
timings[k] = {
|
|
164
|
+
word: scriptWords[k],
|
|
165
|
+
startSec: b.endSec + (localIdx - 1) * per,
|
|
166
|
+
endSec: b.endSec + localIdx * per,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
return timings.filter((t) => t !== null);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Detect VO truncation: returns the portion of the script (if any) that
|
|
173
|
+
* could not be aligned to ASR words because the audio ended before it.
|
|
174
|
+
* Useful as a VO-truncation check (AGENTS.md anti-pattern, rule 26).
|
|
175
|
+
*/
|
|
176
|
+
export function detectTruncation(script, asrWords) {
|
|
177
|
+
const scriptWords = tokenizeScript(script);
|
|
178
|
+
if (scriptWords.length === 0) {
|
|
179
|
+
return { truncated: false, lostWords: [], lastAlignedSec: 0 };
|
|
180
|
+
}
|
|
181
|
+
if (asrWords.length === 0) {
|
|
182
|
+
return { truncated: true, lostWords: scriptWords, lastAlignedSec: 0 };
|
|
183
|
+
}
|
|
184
|
+
const matches = alignLcs(scriptWords, asrWords);
|
|
185
|
+
if (matches.length === 0) {
|
|
186
|
+
return { truncated: true, lostWords: scriptWords, lastAlignedSec: 0 };
|
|
187
|
+
}
|
|
188
|
+
const lastMatch = matches[matches.length - 1];
|
|
189
|
+
const lost = scriptWords.slice(lastMatch.scriptIdx + 1);
|
|
190
|
+
// Treat as truncated only if more than 5% of script words are lost and
|
|
191
|
+
// the lost tail is more than 2 words (avoid false positives on single
|
|
192
|
+
// trailing filler).
|
|
193
|
+
const lossRatio = lost.length / scriptWords.length;
|
|
194
|
+
const truncated = lost.length > 2 && lossRatio > 0.05;
|
|
195
|
+
return {
|
|
196
|
+
truncated,
|
|
197
|
+
lostWords: lost,
|
|
198
|
+
lastAlignedSec: asrWords[lastMatch.asrIdx].endSec,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
//# sourceMappingURL=aligner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aligner.js","sourceRoot":"","sources":["../../src/editing/aligner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EDL (Edit Decision List) authoring and validation.
|
|
3
|
+
*
|
|
4
|
+
* The EDL is a JSON document the LLM reads and mutates. This module
|
|
5
|
+
* provides helpers that keep invariants tight: no negative durations,
|
|
6
|
+
* every `sourceId` resolves, transition ms within ffmpeg's sensible
|
|
7
|
+
* range, output settings present.
|
|
8
|
+
*/
|
|
9
|
+
import type { Edl, EdlClip, EdlTransition, Take } from './types.js';
|
|
10
|
+
export declare const DEFAULT_EDL_OUTPUT: Edl['output'];
|
|
11
|
+
export declare const DEFAULT_AUDIO_FADE_MS = 30;
|
|
12
|
+
export declare const DEFAULT_TRANSITION_MS = 250;
|
|
13
|
+
export interface CreateEdlOptions {
|
|
14
|
+
audioFadeMs?: number;
|
|
15
|
+
output?: Partial<Edl['output']>;
|
|
16
|
+
}
|
|
17
|
+
export declare function createEmptyEdl(options?: CreateEdlOptions): Edl;
|
|
18
|
+
export interface EdlValidationError {
|
|
19
|
+
clipIndex: number;
|
|
20
|
+
message: string;
|
|
21
|
+
}
|
|
22
|
+
export declare function validateEdl(edl: Edl, takes: Take[]): EdlValidationError[];
|
|
23
|
+
/** Effective clip duration in seconds after applying trimStart/trimEnd. */
|
|
24
|
+
export declare function effectiveDurationSec(clip: EdlClip): number;
|
|
25
|
+
/** Sum of clip durations minus crossfade overlaps. */
|
|
26
|
+
export declare function estimateOutputDurationSec(edl: Edl): number;
|
|
27
|
+
export interface AddClipInput {
|
|
28
|
+
sourceId: string;
|
|
29
|
+
startSec: number;
|
|
30
|
+
endSec: number;
|
|
31
|
+
trimStartMs?: number;
|
|
32
|
+
trimEndMs?: number;
|
|
33
|
+
transitionIn?: EdlTransition;
|
|
34
|
+
transitionMs?: number;
|
|
35
|
+
rationale?: string;
|
|
36
|
+
}
|
|
37
|
+
export declare function addClip(edl: Edl, input: AddClipInput): void;
|
|
38
|
+
/**
|
|
39
|
+
* Insert a crossfade at a cut boundary (between clip `i - 1` and `i`).
|
|
40
|
+
* Used by the cut-qa agent's `insert-crossfade` fix.
|
|
41
|
+
*/
|
|
42
|
+
export declare function insertCrossfade(edl: Edl, clipIndex: number, transitionMs?: number): void;
|
|
43
|
+
export declare function readEdl(path: string): Edl;
|
|
44
|
+
export declare function writeEdl(edl: Edl, path: string): void;
|
|
45
|
+
//# sourceMappingURL=edl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edl.d.ts","sourceRoot":"","sources":["../../src/editing/edl.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEpE,eAAO,MAAM,kBAAkB,EAAE,GAAG,CAAC,QAAQ,CAI5C,CAAC;AAEF,eAAO,MAAM,qBAAqB,KAAK,CAAC;AACxC,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAEzC,MAAM,WAAW,gBAAgB;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC;CACjC;AAED,wBAAgB,cAAc,CAAC,OAAO,GAAE,gBAAqB,GAAG,GAAG,CAMlE;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,kBAAkB,EAAE,CA8CzE;AAED,2EAA2E;AAC3E,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAI1D;AAED,sDAAsD;AACtD,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CAS1D;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,aAAa,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI,CAe3D;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,MAAM,EACjB,YAAY,GAAE,MAAY,GACzB,IAAI,CAON;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAgBzC;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAGrD"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EDL (Edit Decision List) authoring and validation.
|
|
3
|
+
*
|
|
4
|
+
* The EDL is a JSON document the LLM reads and mutates. This module
|
|
5
|
+
* provides helpers that keep invariants tight: no negative durations,
|
|
6
|
+
* every `sourceId` resolves, transition ms within ffmpeg's sensible
|
|
7
|
+
* range, output settings present.
|
|
8
|
+
*/
|
|
9
|
+
import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
10
|
+
import { dirname } from 'node:path';
|
|
11
|
+
export const DEFAULT_EDL_OUTPUT = {
|
|
12
|
+
videoCodec: 'libx264',
|
|
13
|
+
audioCodec: 'aac',
|
|
14
|
+
crf: 18,
|
|
15
|
+
};
|
|
16
|
+
export const DEFAULT_AUDIO_FADE_MS = 30;
|
|
17
|
+
export const DEFAULT_TRANSITION_MS = 250;
|
|
18
|
+
export function createEmptyEdl(options = {}) {
|
|
19
|
+
return {
|
|
20
|
+
clips: [],
|
|
21
|
+
audioFadeMs: options.audioFadeMs ?? DEFAULT_AUDIO_FADE_MS,
|
|
22
|
+
output: { ...DEFAULT_EDL_OUTPUT, ...(options.output ?? {}) },
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export function validateEdl(edl, takes) {
|
|
26
|
+
const errors = [];
|
|
27
|
+
const takeById = new Map(takes.map((t) => [t.id, t]));
|
|
28
|
+
if (edl.clips.length === 0) {
|
|
29
|
+
errors.push({ clipIndex: -1, message: 'EDL has no clips' });
|
|
30
|
+
return errors;
|
|
31
|
+
}
|
|
32
|
+
if (edl.audioFadeMs < 0 || edl.audioFadeMs > 500) {
|
|
33
|
+
errors.push({ clipIndex: -1, message: `audioFadeMs out of sensible range: ${edl.audioFadeMs}` });
|
|
34
|
+
}
|
|
35
|
+
edl.clips.forEach((clip, i) => {
|
|
36
|
+
const take = takeById.get(clip.sourceId);
|
|
37
|
+
if (!take) {
|
|
38
|
+
errors.push({ clipIndex: i, message: `Unknown sourceId: ${clip.sourceId}` });
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (!(clip.endSec > clip.startSec)) {
|
|
42
|
+
errors.push({
|
|
43
|
+
clipIndex: i,
|
|
44
|
+
message: `endSec (${clip.endSec}) must be greater than startSec (${clip.startSec})`,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
if (clip.startSec < 0) {
|
|
48
|
+
errors.push({ clipIndex: i, message: `startSec cannot be negative: ${clip.startSec}` });
|
|
49
|
+
}
|
|
50
|
+
if (clip.endSec > take.durationSec + 0.05) {
|
|
51
|
+
errors.push({
|
|
52
|
+
clipIndex: i,
|
|
53
|
+
message: `endSec (${clip.endSec}) exceeds source duration (${take.durationSec})`,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
if (clip.transitionIn && clip.transitionIn !== 'cut' && (clip.transitionMs ?? 0) <= 0) {
|
|
57
|
+
errors.push({
|
|
58
|
+
clipIndex: i,
|
|
59
|
+
message: `transitionIn=${clip.transitionIn} requires positive transitionMs`,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
if ((clip.trimStartMs ?? 0) < 0 || (clip.trimEndMs ?? 0) < 0) {
|
|
63
|
+
errors.push({ clipIndex: i, message: 'trimStartMs / trimEndMs cannot be negative' });
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
return errors;
|
|
67
|
+
}
|
|
68
|
+
/** Effective clip duration in seconds after applying trimStart/trimEnd. */
|
|
69
|
+
export function effectiveDurationSec(clip) {
|
|
70
|
+
const base = clip.endSec - clip.startSec;
|
|
71
|
+
const trim = ((clip.trimStartMs ?? 0) + (clip.trimEndMs ?? 0)) / 1000;
|
|
72
|
+
return Math.max(0, base - trim);
|
|
73
|
+
}
|
|
74
|
+
/** Sum of clip durations minus crossfade overlaps. */
|
|
75
|
+
export function estimateOutputDurationSec(edl) {
|
|
76
|
+
let total = 0;
|
|
77
|
+
edl.clips.forEach((clip, i) => {
|
|
78
|
+
total += effectiveDurationSec(clip);
|
|
79
|
+
if (i > 0 && clip.transitionIn && clip.transitionIn !== 'cut') {
|
|
80
|
+
total -= (clip.transitionMs ?? DEFAULT_TRANSITION_MS) / 1000;
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
return Math.max(0, total);
|
|
84
|
+
}
|
|
85
|
+
export function addClip(edl, input) {
|
|
86
|
+
const clip = {
|
|
87
|
+
sourceId: input.sourceId,
|
|
88
|
+
startSec: input.startSec,
|
|
89
|
+
endSec: input.endSec,
|
|
90
|
+
trimStartMs: input.trimStartMs,
|
|
91
|
+
trimEndMs: input.trimEndMs,
|
|
92
|
+
transitionIn: input.transitionIn ?? 'cut',
|
|
93
|
+
transitionMs: input.transitionIn && input.transitionIn !== 'cut'
|
|
94
|
+
? input.transitionMs ?? DEFAULT_TRANSITION_MS
|
|
95
|
+
: undefined,
|
|
96
|
+
rationale: input.rationale,
|
|
97
|
+
};
|
|
98
|
+
edl.clips.push(clip);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Insert a crossfade at a cut boundary (between clip `i - 1` and `i`).
|
|
102
|
+
* Used by the cut-qa agent's `insert-crossfade` fix.
|
|
103
|
+
*/
|
|
104
|
+
export function insertCrossfade(edl, clipIndex, transitionMs = 200) {
|
|
105
|
+
if (clipIndex <= 0 || clipIndex >= edl.clips.length) {
|
|
106
|
+
throw new Error(`insertCrossfade: clipIndex ${clipIndex} out of range`);
|
|
107
|
+
}
|
|
108
|
+
const clip = edl.clips[clipIndex];
|
|
109
|
+
clip.transitionIn = 'crossfade';
|
|
110
|
+
clip.transitionMs = transitionMs;
|
|
111
|
+
}
|
|
112
|
+
export function readEdl(path) {
|
|
113
|
+
const raw = readFileSync(path, 'utf-8');
|
|
114
|
+
const parsed = JSON.parse(raw);
|
|
115
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
116
|
+
throw new Error(`EDL at ${path} is not an object`);
|
|
117
|
+
}
|
|
118
|
+
if (!Array.isArray(parsed.clips)) {
|
|
119
|
+
throw new Error(`EDL at ${path} is missing a 'clips' array`);
|
|
120
|
+
}
|
|
121
|
+
const edl = {
|
|
122
|
+
clips: parsed.clips,
|
|
123
|
+
audioFadeMs: parsed.audioFadeMs ?? DEFAULT_AUDIO_FADE_MS,
|
|
124
|
+
colorGrade: parsed.colorGrade,
|
|
125
|
+
output: { ...DEFAULT_EDL_OUTPUT, ...(parsed.output ?? {}) },
|
|
126
|
+
};
|
|
127
|
+
return edl;
|
|
128
|
+
}
|
|
129
|
+
export function writeEdl(edl, path) {
|
|
130
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
131
|
+
writeFileSync(path, JSON.stringify(edl, null, 2), 'utf-8');
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=edl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edl.js","sourceRoot":"","sources":["../../src/editing/edl.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Overlay manifest types and helpers.
|
|
3
|
+
*
|
|
4
|
+
* Overlays are branded motion graphics composited onto a finished cut:
|
|
5
|
+
* lower thirds (speaker name + title), title cards, callouts, chapter
|
|
6
|
+
* markers. They are produced by parallel sub-agents (Remotion or ffmpeg
|
|
7
|
+
* drawtext) and composited by `scripts/render-overlay.ts`.
|
|
8
|
+
*
|
|
9
|
+
* Separate from the EDL because overlays are a post-process on top of
|
|
10
|
+
* the delivered cut — not part of the source-material cut list.
|
|
11
|
+
*/
|
|
12
|
+
export type OverlayKind = 'lower-third' | 'title-card' | 'callout' | 'chapter-marker' | 'logo-bug';
|
|
13
|
+
export type OverlayRenderer = 'remotion' | 'ffmpeg-drawtext';
|
|
14
|
+
export interface OverlayPosition {
|
|
15
|
+
/** Anchor point for the overlay. */
|
|
16
|
+
anchor: 'top-left' | 'top-center' | 'top-right' | 'center' | 'bottom-left' | 'bottom-center' | 'bottom-right';
|
|
17
|
+
/** Offset in px from the anchor. Positive = inward. */
|
|
18
|
+
offsetXPx?: number;
|
|
19
|
+
offsetYPx?: number;
|
|
20
|
+
}
|
|
21
|
+
export interface LowerThirdPayload {
|
|
22
|
+
kind: 'lower-third';
|
|
23
|
+
name: string;
|
|
24
|
+
title?: string;
|
|
25
|
+
color?: string;
|
|
26
|
+
durationMs?: number;
|
|
27
|
+
}
|
|
28
|
+
export interface TitleCardPayload {
|
|
29
|
+
kind: 'title-card';
|
|
30
|
+
heading: string;
|
|
31
|
+
subheading?: string;
|
|
32
|
+
/** Background: 'black', 'white', 'blur', or a hex color. */
|
|
33
|
+
background?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface CalloutPayload {
|
|
36
|
+
kind: 'callout';
|
|
37
|
+
text: string;
|
|
38
|
+
/**
|
|
39
|
+
* Point to this frame coordinate with an arrow. If omitted the callout
|
|
40
|
+
* is a free-floating banner.
|
|
41
|
+
*/
|
|
42
|
+
pointToXPx?: number;
|
|
43
|
+
pointToYPx?: number;
|
|
44
|
+
}
|
|
45
|
+
export interface ChapterMarkerPayload {
|
|
46
|
+
kind: 'chapter-marker';
|
|
47
|
+
chapterNumber: number;
|
|
48
|
+
title: string;
|
|
49
|
+
}
|
|
50
|
+
export interface LogoBugPayload {
|
|
51
|
+
kind: 'logo-bug';
|
|
52
|
+
/**
|
|
53
|
+
* Describe the logo geometry in text — NEVER pass the Venice AI logo PNG
|
|
54
|
+
* as a composite source (AGENTS.md anti-pattern #11 — mostly-transparent
|
|
55
|
+
* logos render as overlays, not references). For the Venice crossed-keys
|
|
56
|
+
* logo the renderer draws it procedurally in Remotion or via ffmpeg
|
|
57
|
+
* drawgeometry from a pre-rendered branded PNG asset.
|
|
58
|
+
*/
|
|
59
|
+
description: string;
|
|
60
|
+
/** Path to a pre-rendered fully-opaque logo asset. Optional. */
|
|
61
|
+
assetPath?: string;
|
|
62
|
+
}
|
|
63
|
+
export type OverlayPayload = LowerThirdPayload | TitleCardPayload | CalloutPayload | ChapterMarkerPayload | LogoBugPayload;
|
|
64
|
+
export interface Overlay {
|
|
65
|
+
id: string;
|
|
66
|
+
kind: OverlayKind;
|
|
67
|
+
startSec: number;
|
|
68
|
+
endSec: number;
|
|
69
|
+
position: OverlayPosition;
|
|
70
|
+
payload: OverlayPayload;
|
|
71
|
+
renderer: OverlayRenderer;
|
|
72
|
+
/**
|
|
73
|
+
* Path to a pre-rendered overlay asset (transparent WebM/MOV for
|
|
74
|
+
* Remotion, or omitted for ffmpeg drawtext which renders in-place).
|
|
75
|
+
*/
|
|
76
|
+
assetPath?: string;
|
|
77
|
+
}
|
|
78
|
+
export interface OverlayManifest {
|
|
79
|
+
/** Base video this manifest applies to. */
|
|
80
|
+
baseVideo: string;
|
|
81
|
+
/** Path to the composited output. */
|
|
82
|
+
outputPath: string;
|
|
83
|
+
overlays: Overlay[];
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Validate an overlay manifest. Rejects manifests that would violate the
|
|
87
|
+
* Venice logo rules (rule 17, anti-pattern #11) or produce overlapping
|
|
88
|
+
* cuts on the same anchor.
|
|
89
|
+
*/
|
|
90
|
+
export interface OverlayValidationError {
|
|
91
|
+
overlayId: string;
|
|
92
|
+
message: string;
|
|
93
|
+
}
|
|
94
|
+
export declare function validateOverlayManifest(manifest: OverlayManifest): OverlayValidationError[];
|
|
95
|
+
export declare function readOverlayManifest(path: string): OverlayManifest;
|
|
96
|
+
export declare function writeOverlayManifest(manifest: OverlayManifest, path: string): void;
|
|
97
|
+
//# sourceMappingURL=overlays.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlays.d.ts","sourceRoot":"","sources":["../../src/editing/overlays.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,MAAM,MAAM,WAAW,GACnB,aAAa,GACb,YAAY,GACZ,SAAS,GACT,gBAAgB,GAChB,UAAU,CAAC;AAEf,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,iBAAiB,CAAC;AAE7D,MAAM,WAAW,eAAe;IAC9B,oCAAoC;IACpC,MAAM,EAAE,UAAU,GAAG,YAAY,GAAG,WAAW,GAAG,QAAQ,GAAG,aAAa,GAAG,eAAe,GAAG,cAAc,CAAC;IAC9G,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,aAAa,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,YAAY,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,gBAAgB,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,UAAU,CAAC;IACjB;;;;;;OAMG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,cAAc,GACtB,iBAAiB,GACjB,gBAAgB,GAChB,cAAc,GACd,oBAAoB,GACpB,cAAc,CAAC;AAEnB,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,WAAW,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,eAAe,CAAC;IAC1B,OAAO,EAAE,cAAc,CAAC;IACxB,QAAQ,EAAE,eAAe,CAAC;IAC1B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,2CAA2C;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,eAAe,GAAG,sBAAsB,EAAE,CAmB3F;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,CAGjE;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAGlF"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Overlay manifest types and helpers.
|
|
3
|
+
*
|
|
4
|
+
* Overlays are branded motion graphics composited onto a finished cut:
|
|
5
|
+
* lower thirds (speaker name + title), title cards, callouts, chapter
|
|
6
|
+
* markers. They are produced by parallel sub-agents (Remotion or ffmpeg
|
|
7
|
+
* drawtext) and composited by `scripts/render-overlay.ts`.
|
|
8
|
+
*
|
|
9
|
+
* Separate from the EDL because overlays are a post-process on top of
|
|
10
|
+
* the delivered cut — not part of the source-material cut list.
|
|
11
|
+
*/
|
|
12
|
+
import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
13
|
+
import { dirname } from 'node:path';
|
|
14
|
+
export function validateOverlayManifest(manifest) {
|
|
15
|
+
const errs = [];
|
|
16
|
+
for (const o of manifest.overlays) {
|
|
17
|
+
if (!(o.endSec > o.startSec)) {
|
|
18
|
+
errs.push({ overlayId: o.id, message: 'endSec must be greater than startSec' });
|
|
19
|
+
}
|
|
20
|
+
if (o.payload.kind === 'logo-bug') {
|
|
21
|
+
const p = o.payload;
|
|
22
|
+
const desc = (p.description ?? '').toLowerCase();
|
|
23
|
+
if (/\b(vvv|triple[- ]?v)\b/.test(desc)) {
|
|
24
|
+
errs.push({
|
|
25
|
+
overlayId: o.id,
|
|
26
|
+
message: 'Venice logo description uses "VVV" / "triple-V". Per AGENTS.md rule 17 the logo is a crossed-keys design — describe as "two ornate skeleton keys crossed in an X with a chevron/open-book at the top".',
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return errs;
|
|
32
|
+
}
|
|
33
|
+
export function readOverlayManifest(path) {
|
|
34
|
+
const raw = readFileSync(path, 'utf-8');
|
|
35
|
+
return JSON.parse(raw);
|
|
36
|
+
}
|
|
37
|
+
export function writeOverlayManifest(manifest, path) {
|
|
38
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
39
|
+
writeFileSync(path, JSON.stringify(manifest, null, 2), 'utf-8');
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=overlays.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlays.js","sourceRoot":"","sources":["../../src/editing/overlays.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACjE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA0GpC,MAAM,UAAU,uBAAuB,CAAC,QAAyB;IAC/D,MAAM,IAAI,GAA6B,EAAE,CAAC;IAC1C,KAAK,MAAM,CAAC,IAAI,QAAQ,CAAC,QAAQ,EAAE,CAAC;QAClC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,IAAI,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,sCAAsC,EAAE,CAAC,CAAC;QAClF,CAAC;QACD,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YAClC,MAAM,CAAC,GAAG,CAAC,CAAC,OAAyB,CAAC;YACtC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;YACjD,IAAI,wBAAwB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBACxC,IAAI,CAAC,IAAI,CAAC;oBACR,SAAS,EAAE,CAAC,CAAC,EAAE;oBACf,OAAO,EACL,wMAAwM;iBAC3M,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACxC,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAoB,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,QAAyB,EAAE,IAAY;IAC1E,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;AAClE,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Packer: collapses word streams into phrases and renders takes_packed.md.
|
|
3
|
+
*
|
|
4
|
+
* The pack is the primary LLM surface for editing. Keep it compact — target
|
|
5
|
+
* ~12KB per 40 minutes of source audio. The format matches the one used by
|
|
6
|
+
* browser-use/video-use so experience across projects transfers directly.
|
|
7
|
+
*
|
|
8
|
+
* Phrase boundary heuristics, in order:
|
|
9
|
+
* 1. Gap between consecutive words > `silenceGapSec` (default 0.45s)
|
|
10
|
+
* 2. Hard sentence punctuation followed by any gap (. ! ?)
|
|
11
|
+
* 3. Soft punctuation (, ; :) only when followed by > 0.30s gap
|
|
12
|
+
*/
|
|
13
|
+
import type { AudioEvent, Take, TakesPack, WordTiming } from './types.js';
|
|
14
|
+
export interface PackOptions {
|
|
15
|
+
silenceGapSec?: number;
|
|
16
|
+
maxPhraseSec?: number;
|
|
17
|
+
/**
|
|
18
|
+
* Minimum character length for a phrase to appear in the pack — shorter
|
|
19
|
+
* fragments get merged into the next phrase.
|
|
20
|
+
*/
|
|
21
|
+
minPhraseChars?: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Derive a stable 5-char hex id (e.g. `C0103`) from the file path. The
|
|
25
|
+
* leading `C` matches video-use's naming. Stable across runs so EDLs
|
|
26
|
+
* survive re-transcription.
|
|
27
|
+
*/
|
|
28
|
+
export declare function deriveTakeId(file: string, ordinal: number): string;
|
|
29
|
+
export interface BuildTakeInput {
|
|
30
|
+
file: string;
|
|
31
|
+
words: WordTiming[];
|
|
32
|
+
durationSec: number;
|
|
33
|
+
transcriber: string;
|
|
34
|
+
ordinal: number;
|
|
35
|
+
mode: 'asr' | 'aligned';
|
|
36
|
+
defaultSpeaker?: string;
|
|
37
|
+
/** Pre-computed phrase-level speaker labels when diarization is available. */
|
|
38
|
+
phraseSpeakers?: string[];
|
|
39
|
+
audioEvents?: AudioEvent[];
|
|
40
|
+
packOptions?: PackOptions;
|
|
41
|
+
}
|
|
42
|
+
export declare function buildTake(input: BuildTakeInput): Take;
|
|
43
|
+
/**
|
|
44
|
+
* Render a TakesPack to markdown. Each take becomes a `##` heading with
|
|
45
|
+
* bracketed phrase timestamps inside — the exact format video-use uses.
|
|
46
|
+
*/
|
|
47
|
+
export declare function renderTakesPack(pack: TakesPack): string;
|
|
48
|
+
export declare function writeTakesPack(pack: TakesPack, outPath: string): void;
|
|
49
|
+
/** Write `<source>.words.json` next to a take for programmatic consumers. */
|
|
50
|
+
export declare function writeWordsJson(take: Take, outPath: string): void;
|
|
51
|
+
export declare function wordsJsonPathFor(sourceFile: string, editDir: string): string;
|
|
52
|
+
//# sourceMappingURL=packer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"packer.d.ts","sourceRoot":"","sources":["../../src/editing/packer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,KAAK,EACV,UAAU,EACV,IAAI,EAEJ,SAAS,EACT,UAAU,EACX,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,WAAW;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAQD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAKlE;AAiED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,KAAK,GAAG,SAAS,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,8EAA8E;IAC9E,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,CAmBrD;AAMD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAqCvD;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAGrE;AAED,6EAA6E;AAC7E,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAmBhE;AAED,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAG5E"}
|