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,170 @@
|
|
|
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 { createHash } from 'node:crypto';
|
|
14
|
+
import { writeFileSync, mkdirSync } from 'node:fs';
|
|
15
|
+
import { dirname, basename, extname } from 'node:path';
|
|
16
|
+
const DEFAULT_OPTIONS = {
|
|
17
|
+
silenceGapSec: 0.45,
|
|
18
|
+
maxPhraseSec: 12.0,
|
|
19
|
+
minPhraseChars: 4,
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Derive a stable 5-char hex id (e.g. `C0103`) from the file path. The
|
|
23
|
+
* leading `C` matches video-use's naming. Stable across runs so EDLs
|
|
24
|
+
* survive re-transcription.
|
|
25
|
+
*/
|
|
26
|
+
export function deriveTakeId(file, ordinal) {
|
|
27
|
+
const hash = createHash('sha1').update(file).digest('hex');
|
|
28
|
+
// Use first 4 hex chars + ordinal to avoid collisions on very large source folders.
|
|
29
|
+
const prefix = hash.slice(0, 4).toUpperCase();
|
|
30
|
+
return `C${prefix.slice(0, 3)}${String(ordinal).padStart(1, '0')}`;
|
|
31
|
+
}
|
|
32
|
+
function groupWordsIntoPhrases(words, speaker, options) {
|
|
33
|
+
if (words.length === 0)
|
|
34
|
+
return [];
|
|
35
|
+
const phrases = [];
|
|
36
|
+
let current = [words[0]];
|
|
37
|
+
const pushPhrase = () => {
|
|
38
|
+
if (current.length === 0)
|
|
39
|
+
return;
|
|
40
|
+
const startSec = current[0].startSec;
|
|
41
|
+
const endSec = current[current.length - 1].endSec;
|
|
42
|
+
const text = current.map((w) => w.word).join(' ').replace(/\s+([,.!?;:])/g, '$1').trim();
|
|
43
|
+
phrases.push({
|
|
44
|
+
speaker,
|
|
45
|
+
startSec,
|
|
46
|
+
endSec,
|
|
47
|
+
text,
|
|
48
|
+
words: current,
|
|
49
|
+
});
|
|
50
|
+
current = [];
|
|
51
|
+
};
|
|
52
|
+
for (let i = 1; i < words.length; i++) {
|
|
53
|
+
const prev = words[i - 1];
|
|
54
|
+
const w = words[i];
|
|
55
|
+
const gap = w.startSec - prev.endSec;
|
|
56
|
+
const prevText = prev.word.trim();
|
|
57
|
+
const endsHard = /[.!?]$/.test(prevText);
|
|
58
|
+
const endsSoft = /[,;:]$/.test(prevText);
|
|
59
|
+
const phraseDur = w.startSec - current[0].startSec;
|
|
60
|
+
const boundary = gap >= options.silenceGapSec ||
|
|
61
|
+
(endsHard && gap >= 0.08) ||
|
|
62
|
+
(endsSoft && gap >= 0.30) ||
|
|
63
|
+
phraseDur >= options.maxPhraseSec;
|
|
64
|
+
if (boundary) {
|
|
65
|
+
pushPhrase();
|
|
66
|
+
}
|
|
67
|
+
current.push(w);
|
|
68
|
+
}
|
|
69
|
+
pushPhrase();
|
|
70
|
+
// Merge fragments shorter than minPhraseChars into the following phrase.
|
|
71
|
+
const merged = [];
|
|
72
|
+
for (const p of phrases) {
|
|
73
|
+
if (p.text.length < options.minPhraseChars && merged.length > 0) {
|
|
74
|
+
const last = merged[merged.length - 1];
|
|
75
|
+
last.endSec = p.endSec;
|
|
76
|
+
last.text = `${last.text} ${p.text}`.trim();
|
|
77
|
+
last.words = [...last.words, ...p.words];
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
merged.push(p);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return merged;
|
|
84
|
+
}
|
|
85
|
+
export function buildTake(input) {
|
|
86
|
+
const opts = { ...DEFAULT_OPTIONS, ...(input.packOptions ?? {}) };
|
|
87
|
+
const speaker = input.defaultSpeaker ?? 'S0';
|
|
88
|
+
let phrases = groupWordsIntoPhrases(input.words, speaker, opts);
|
|
89
|
+
if (input.phraseSpeakers && input.phraseSpeakers.length === phrases.length) {
|
|
90
|
+
phrases = phrases.map((p, i) => ({ ...p, speaker: input.phraseSpeakers[i] }));
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
id: deriveTakeId(input.file, input.ordinal),
|
|
94
|
+
file: input.file,
|
|
95
|
+
durationSec: input.durationSec,
|
|
96
|
+
phrases,
|
|
97
|
+
audioEvents: input.audioEvents ?? [],
|
|
98
|
+
transcribedAt: new Date().toISOString(),
|
|
99
|
+
transcriber: input.transcriber,
|
|
100
|
+
mode: input.mode,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function formatTimestamp(sec) {
|
|
104
|
+
return sec.toFixed(2).padStart(6, '0');
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Render a TakesPack to markdown. Each take becomes a `##` heading with
|
|
108
|
+
* bracketed phrase timestamps inside — the exact format video-use uses.
|
|
109
|
+
*/
|
|
110
|
+
export function renderTakesPack(pack) {
|
|
111
|
+
const lines = [];
|
|
112
|
+
lines.push('# Takes Pack');
|
|
113
|
+
lines.push('');
|
|
114
|
+
lines.push(`Generated: ${pack.generatedAt}`);
|
|
115
|
+
lines.push(`Source: ${pack.sourceLabel}`);
|
|
116
|
+
lines.push(`Takes: ${pack.takes.length}`);
|
|
117
|
+
lines.push('');
|
|
118
|
+
lines.push('Format: each take is a heading with per-phrase `[start-end] speaker text` lines. ' +
|
|
119
|
+
'Times are in seconds from the start of the source file. Speaker labels are from ' +
|
|
120
|
+
'diarization (or the script) when available, otherwise `S0` single-speaker.');
|
|
121
|
+
lines.push('');
|
|
122
|
+
for (const take of pack.takes) {
|
|
123
|
+
const dur = take.durationSec.toFixed(1);
|
|
124
|
+
const phraseCount = take.phrases.length;
|
|
125
|
+
const fileRel = basename(take.file);
|
|
126
|
+
lines.push(`## ${take.id} (duration: ${dur}s, ${phraseCount} phrases)`);
|
|
127
|
+
lines.push(`file: ${fileRel}`);
|
|
128
|
+
if (take.mode === 'aligned')
|
|
129
|
+
lines.push('mode: aligned (ground-truth script)');
|
|
130
|
+
lines.push('');
|
|
131
|
+
for (const p of take.phrases) {
|
|
132
|
+
lines.push(` [${formatTimestamp(p.startSec)}-${formatTimestamp(p.endSec)}] ${p.speaker} ${p.text}`);
|
|
133
|
+
}
|
|
134
|
+
for (const ev of take.audioEvents) {
|
|
135
|
+
lines.push(` [${formatTimestamp(ev.startSec)}-${formatTimestamp(ev.endSec)}] _audio_ ${ev.label}`);
|
|
136
|
+
}
|
|
137
|
+
lines.push('');
|
|
138
|
+
}
|
|
139
|
+
return lines.join('\n');
|
|
140
|
+
}
|
|
141
|
+
export function writeTakesPack(pack, outPath) {
|
|
142
|
+
mkdirSync(dirname(outPath), { recursive: true });
|
|
143
|
+
writeFileSync(outPath, renderTakesPack(pack), 'utf-8');
|
|
144
|
+
}
|
|
145
|
+
/** Write `<source>.words.json` next to a take for programmatic consumers. */
|
|
146
|
+
export function writeWordsJson(take, outPath) {
|
|
147
|
+
mkdirSync(dirname(outPath), { recursive: true });
|
|
148
|
+
const payload = {
|
|
149
|
+
takeId: take.id,
|
|
150
|
+
file: take.file,
|
|
151
|
+
durationSec: take.durationSec,
|
|
152
|
+
transcribedAt: take.transcribedAt,
|
|
153
|
+
transcriber: take.transcriber,
|
|
154
|
+
mode: take.mode,
|
|
155
|
+
words: take.phrases.flatMap((p) => p.words.map((w) => ({ ...w, speaker: p.speaker }))),
|
|
156
|
+
phrases: take.phrases.map((p) => ({
|
|
157
|
+
speaker: p.speaker,
|
|
158
|
+
startSec: p.startSec,
|
|
159
|
+
endSec: p.endSec,
|
|
160
|
+
text: p.text,
|
|
161
|
+
})),
|
|
162
|
+
audioEvents: take.audioEvents,
|
|
163
|
+
};
|
|
164
|
+
writeFileSync(outPath, JSON.stringify(payload, null, 2), 'utf-8');
|
|
165
|
+
}
|
|
166
|
+
export function wordsJsonPathFor(sourceFile, editDir) {
|
|
167
|
+
const stem = basename(sourceFile, extname(sourceFile));
|
|
168
|
+
return `${editDir}/${stem}.words.json`;
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=packer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"packer.js","sourceRoot":"","sources":["../../src/editing/packer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whisper.cpp transcriber provider.
|
|
3
|
+
*
|
|
4
|
+
* Shells out to the `whisper-cpp` binary (install via `brew install whisper-cpp`).
|
|
5
|
+
* Extracts audio to 16kHz mono WAV via ffmpeg, runs whisper.cpp with JSON output,
|
|
6
|
+
* and parses word-level timestamps.
|
|
7
|
+
*
|
|
8
|
+
* whisper.cpp output format (--output-json-full):
|
|
9
|
+
* {
|
|
10
|
+
* "result": { "language": "en" },
|
|
11
|
+
* "transcription": [
|
|
12
|
+
* {
|
|
13
|
+
* "timestamps": { "from": "00:00:00,000", "to": "00:00:03,400" },
|
|
14
|
+
* "offsets": { "from": 0, "to": 3400 },
|
|
15
|
+
* "text": " Hello world.",
|
|
16
|
+
* "tokens": [
|
|
17
|
+
* { "offsets": { "from": 0, "to": 400 }, "text": " Hello", ... },
|
|
18
|
+
* ...
|
|
19
|
+
* ]
|
|
20
|
+
* }
|
|
21
|
+
* ]
|
|
22
|
+
* }
|
|
23
|
+
*
|
|
24
|
+
* Offsets are in milliseconds from the start of the source.
|
|
25
|
+
*/
|
|
26
|
+
import type { TranscriberProvider, TranscribeOptions, TranscribeResult } from '../types.js';
|
|
27
|
+
export declare class WhisperCppProvider implements TranscriberProvider {
|
|
28
|
+
readonly id = "whisper-cpp";
|
|
29
|
+
assertAvailable(): Promise<void>;
|
|
30
|
+
transcribe(file: string, options?: TranscribeOptions): Promise<TranscribeResult>;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=whisper-cpp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"whisper-cpp.d.ts","sourceRoot":"","sources":["../../../src/editing/providers/whisper-cpp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAMH,OAAO,KAAK,EACV,mBAAmB,EACnB,iBAAiB,EACjB,gBAAgB,EAEjB,MAAM,aAAa,CAAC;AAqLrB,qBAAa,kBAAmB,YAAW,mBAAmB;IAC5D,QAAQ,CAAC,EAAE,iBAAiB;IAEtB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAQhC,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,gBAAgB,CAAC;CA6D3F"}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whisper.cpp transcriber provider.
|
|
3
|
+
*
|
|
4
|
+
* Shells out to the `whisper-cpp` binary (install via `brew install whisper-cpp`).
|
|
5
|
+
* Extracts audio to 16kHz mono WAV via ffmpeg, runs whisper.cpp with JSON output,
|
|
6
|
+
* and parses word-level timestamps.
|
|
7
|
+
*
|
|
8
|
+
* whisper.cpp output format (--output-json-full):
|
|
9
|
+
* {
|
|
10
|
+
* "result": { "language": "en" },
|
|
11
|
+
* "transcription": [
|
|
12
|
+
* {
|
|
13
|
+
* "timestamps": { "from": "00:00:00,000", "to": "00:00:03,400" },
|
|
14
|
+
* "offsets": { "from": 0, "to": 3400 },
|
|
15
|
+
* "text": " Hello world.",
|
|
16
|
+
* "tokens": [
|
|
17
|
+
* { "offsets": { "from": 0, "to": 400 }, "text": " Hello", ... },
|
|
18
|
+
* ...
|
|
19
|
+
* ]
|
|
20
|
+
* }
|
|
21
|
+
* ]
|
|
22
|
+
* }
|
|
23
|
+
*
|
|
24
|
+
* Offsets are in milliseconds from the start of the source.
|
|
25
|
+
*/
|
|
26
|
+
import { execSync, spawnSync } from 'node:child_process';
|
|
27
|
+
import { existsSync, readFileSync, mkdtempSync, rmSync } from 'node:fs';
|
|
28
|
+
import { join } from 'node:path';
|
|
29
|
+
import { tmpdir } from 'node:os';
|
|
30
|
+
const MODEL_FILENAME = {
|
|
31
|
+
'tiny': 'ggml-tiny.bin',
|
|
32
|
+
'base': 'ggml-base.bin',
|
|
33
|
+
'small': 'ggml-small.bin',
|
|
34
|
+
'medium': 'ggml-medium.bin',
|
|
35
|
+
'large': 'ggml-large-v3.bin',
|
|
36
|
+
'tiny.en': 'ggml-tiny.en.bin',
|
|
37
|
+
'base.en': 'ggml-base.en.bin',
|
|
38
|
+
'small.en': 'ggml-small.en.bin',
|
|
39
|
+
'medium.en': 'ggml-medium.en.bin',
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Search the usual places for a whisper.cpp model file. In order:
|
|
43
|
+
* 1. $WHISPER_CPP_MODELS_DIR / <filename>
|
|
44
|
+
* 2. /opt/homebrew/share/whisper-cpp/<filename> (Apple Silicon brew)
|
|
45
|
+
* 3. /usr/local/share/whisper-cpp/<filename> (Intel brew)
|
|
46
|
+
* 4. ~/.cache/whisper.cpp/<filename>
|
|
47
|
+
*/
|
|
48
|
+
function resolveModelPath(modelSize) {
|
|
49
|
+
const filename = MODEL_FILENAME[modelSize];
|
|
50
|
+
const candidates = [
|
|
51
|
+
process.env.WHISPER_CPP_MODELS_DIR
|
|
52
|
+
? join(process.env.WHISPER_CPP_MODELS_DIR, filename)
|
|
53
|
+
: null,
|
|
54
|
+
`/opt/homebrew/share/whisper-cpp/${filename}`,
|
|
55
|
+
`/usr/local/share/whisper-cpp/${filename}`,
|
|
56
|
+
join(process.env.HOME || '', '.cache', 'whisper.cpp', filename),
|
|
57
|
+
].filter((x) => x !== null);
|
|
58
|
+
for (const path of candidates) {
|
|
59
|
+
if (existsSync(path))
|
|
60
|
+
return path;
|
|
61
|
+
}
|
|
62
|
+
throw new Error([
|
|
63
|
+
`whisper.cpp model '${filename}' not found. Searched:`,
|
|
64
|
+
...candidates.map((c) => ` - ${c}`),
|
|
65
|
+
'',
|
|
66
|
+
'Download via:',
|
|
67
|
+
` curl -L -o ~/.cache/whisper.cpp/${filename} \\`,
|
|
68
|
+
` https://huggingface.co/ggerganov/whisper.cpp/resolve/main/${filename}`,
|
|
69
|
+
'',
|
|
70
|
+
'Or set WHISPER_CPP_MODELS_DIR to a directory that contains the file.',
|
|
71
|
+
].join('\n'));
|
|
72
|
+
}
|
|
73
|
+
function resolveWhisperBinary() {
|
|
74
|
+
for (const candidate of ['whisper-cli', 'whisper-cpp', 'main']) {
|
|
75
|
+
const r = spawnSync('which', [candidate], { encoding: 'utf-8' });
|
|
76
|
+
if (r.status === 0 && r.stdout.trim())
|
|
77
|
+
return candidate;
|
|
78
|
+
}
|
|
79
|
+
throw new Error('whisper.cpp binary not found on PATH. Install with:\n' +
|
|
80
|
+
' brew install whisper-cpp\n' +
|
|
81
|
+
'\n' +
|
|
82
|
+
'This provides the `whisper-cli` binary (newer releases) or `whisper-cpp` (older).');
|
|
83
|
+
}
|
|
84
|
+
function probeDuration(file) {
|
|
85
|
+
const out = execSync(`ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "${file}"`, { encoding: 'utf-8' }).trim();
|
|
86
|
+
const d = parseFloat(out);
|
|
87
|
+
if (Number.isNaN(d))
|
|
88
|
+
throw new Error(`ffprobe could not read duration from ${file}`);
|
|
89
|
+
return d;
|
|
90
|
+
}
|
|
91
|
+
function extractWav(file, wavPath) {
|
|
92
|
+
execSync(`ffmpeg -y -hide_banner -loglevel error -i "${file}" -ar 16000 -ac 1 -c:a pcm_s16le "${wavPath}"`, { stdio: 'inherit' });
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Flatten whisper.cpp segment+token output into a flat word timing stream.
|
|
96
|
+
*
|
|
97
|
+
* whisper.cpp's "tokens" are BPE tokens, not words — they get merged into
|
|
98
|
+
* words by concatenating adjacent tokens until we hit whitespace. For most
|
|
99
|
+
* purposes the segment-level "text" split on whitespace is sufficient and
|
|
100
|
+
* accurate enough; we allocate times proportionally when tokens are missing.
|
|
101
|
+
*/
|
|
102
|
+
function segmentsToWords(segments) {
|
|
103
|
+
const words = [];
|
|
104
|
+
for (const seg of segments) {
|
|
105
|
+
const segStartSec = seg.offsets.from / 1000;
|
|
106
|
+
const segEndSec = seg.offsets.to / 1000;
|
|
107
|
+
const segDur = Math.max(0.001, segEndSec - segStartSec);
|
|
108
|
+
// Prefer token-level timestamps when available (whisper.cpp with
|
|
109
|
+
// --word-thold and --max-len 1 emits per-word tokens).
|
|
110
|
+
const tokenWords = [];
|
|
111
|
+
if (seg.tokens && seg.tokens.length > 0) {
|
|
112
|
+
// Merge tokens into words at whitespace boundaries.
|
|
113
|
+
let buf = null;
|
|
114
|
+
for (const tok of seg.tokens) {
|
|
115
|
+
const text = tok.text;
|
|
116
|
+
// whisper.cpp emits [_BEG_], [_TT_XX], <|...|> special tokens; skip them.
|
|
117
|
+
if (text.startsWith('[_') || text.startsWith('<|'))
|
|
118
|
+
continue;
|
|
119
|
+
const isWordStart = text.startsWith(' ') || buf === null;
|
|
120
|
+
const trimmed = text.trim();
|
|
121
|
+
if (!trimmed)
|
|
122
|
+
continue;
|
|
123
|
+
if (isWordStart) {
|
|
124
|
+
if (buf) {
|
|
125
|
+
tokenWords.push({
|
|
126
|
+
word: buf.text,
|
|
127
|
+
startSec: buf.from / 1000,
|
|
128
|
+
endSec: buf.to / 1000,
|
|
129
|
+
confidence: buf.p,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
buf = { text: trimmed, from: tok.offsets.from, to: tok.offsets.to, p: tok.p };
|
|
133
|
+
}
|
|
134
|
+
else if (buf) {
|
|
135
|
+
buf.text += trimmed;
|
|
136
|
+
buf.to = tok.offsets.to;
|
|
137
|
+
if (tok.p !== undefined && buf.p !== undefined) {
|
|
138
|
+
buf.p = Math.min(buf.p, tok.p);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (buf) {
|
|
143
|
+
tokenWords.push({
|
|
144
|
+
word: buf.text,
|
|
145
|
+
startSec: buf.from / 1000,
|
|
146
|
+
endSec: buf.to / 1000,
|
|
147
|
+
confidence: buf.p,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (tokenWords.length > 0) {
|
|
152
|
+
words.push(...tokenWords);
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
// Fallback: split segment text evenly across its duration.
|
|
156
|
+
const plainWords = seg.text
|
|
157
|
+
.split(/\s+/)
|
|
158
|
+
.map((w) => w.trim())
|
|
159
|
+
.filter((w) => w.length > 0);
|
|
160
|
+
if (plainWords.length === 0)
|
|
161
|
+
continue;
|
|
162
|
+
const per = segDur / plainWords.length;
|
|
163
|
+
for (let i = 0; i < plainWords.length; i++) {
|
|
164
|
+
words.push({
|
|
165
|
+
word: plainWords[i],
|
|
166
|
+
startSec: segStartSec + i * per,
|
|
167
|
+
endSec: segStartSec + (i + 1) * per,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return words;
|
|
172
|
+
}
|
|
173
|
+
export class WhisperCppProvider {
|
|
174
|
+
id = 'whisper-cpp';
|
|
175
|
+
async assertAvailable() {
|
|
176
|
+
resolveWhisperBinary();
|
|
177
|
+
const r = spawnSync('ffmpeg', ['-version'], { encoding: 'utf-8' });
|
|
178
|
+
if (r.status !== 0) {
|
|
179
|
+
throw new Error('ffmpeg not found on PATH. Install via `brew install ffmpeg`.');
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
async transcribe(file, options = {}) {
|
|
183
|
+
const modelSize = options.modelSize ?? 'base.en';
|
|
184
|
+
const language = options.language ?? 'auto';
|
|
185
|
+
const wordThold = options.wordThreshold ?? 0.01;
|
|
186
|
+
const threads = options.threads ?? Math.max(1, Math.min(8, Number(process.env.WHISPER_THREADS) || 4));
|
|
187
|
+
const binary = resolveWhisperBinary();
|
|
188
|
+
const modelPath = resolveModelPath(modelSize);
|
|
189
|
+
const durationSec = probeDuration(file);
|
|
190
|
+
const tmp = mkdtempSync(join(tmpdir(), 'whisper-cpp-'));
|
|
191
|
+
const wav = join(tmp, 'audio.wav');
|
|
192
|
+
const jsonBase = join(tmp, 'out');
|
|
193
|
+
const jsonPath = `${jsonBase}.json`;
|
|
194
|
+
try {
|
|
195
|
+
extractWav(file, wav);
|
|
196
|
+
const args = [
|
|
197
|
+
'--model', modelPath,
|
|
198
|
+
'--file', wav,
|
|
199
|
+
'--output-json-full',
|
|
200
|
+
'--output-file', jsonBase,
|
|
201
|
+
'--word-thold', String(wordThold),
|
|
202
|
+
'--max-len', '1',
|
|
203
|
+
'--threads', String(threads),
|
|
204
|
+
'--print-progress',
|
|
205
|
+
];
|
|
206
|
+
if (language !== 'auto') {
|
|
207
|
+
args.push('--language', language);
|
|
208
|
+
}
|
|
209
|
+
const r = spawnSync(binary, args, { stdio: 'inherit' });
|
|
210
|
+
if (r.status !== 0) {
|
|
211
|
+
throw new Error(`${binary} exited with code ${r.status}`);
|
|
212
|
+
}
|
|
213
|
+
if (!existsSync(jsonPath)) {
|
|
214
|
+
throw new Error(`whisper.cpp did not produce ${jsonPath}`);
|
|
215
|
+
}
|
|
216
|
+
const raw = readFileSync(jsonPath, 'utf-8');
|
|
217
|
+
const parsed = JSON.parse(raw);
|
|
218
|
+
const words = segmentsToWords(parsed.transcription ?? []);
|
|
219
|
+
const text = words.map((w) => w.word).join(' ');
|
|
220
|
+
const detectedLang = parsed.result?.language ?? (language === 'auto' ? 'en' : language);
|
|
221
|
+
return {
|
|
222
|
+
words,
|
|
223
|
+
text,
|
|
224
|
+
language: detectedLang,
|
|
225
|
+
durationSec,
|
|
226
|
+
transcriberLabel: `whisper-cpp:${modelSize}`,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
finally {
|
|
230
|
+
try {
|
|
231
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
232
|
+
}
|
|
233
|
+
catch {
|
|
234
|
+
// best-effort cleanup
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
//# sourceMappingURL=whisper-cpp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"whisper-cpp.js","sourceRoot":"","sources":["../../../src/editing/providers/whisper-cpp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EDL renderer.
|
|
3
|
+
*
|
|
4
|
+
* Turns an Edl into a single mp4 file via ffmpeg. Produces 30ms audio
|
|
5
|
+
* fades at every cut boundary (video-use's "no pops" rule). Supports
|
|
6
|
+
* hard cuts, crossfades, and fade-to-black transitions.
|
|
7
|
+
*
|
|
8
|
+
* Archive-first per workspace rule `.cursor/rules/shot-asset-safety.mdc`:
|
|
9
|
+
* any existing output file is renamed to `<stem>-v<N>.<ext>` before the
|
|
10
|
+
* new render lands.
|
|
11
|
+
*/
|
|
12
|
+
import type { Edl, Take } from './types.js';
|
|
13
|
+
export interface RenderOptions {
|
|
14
|
+
/** Optional destination for the output file. Default: `<editDir>/final-edit.mp4`. */
|
|
15
|
+
outputPath?: string;
|
|
16
|
+
/** Skip archiving the prior version. Default false (always archive). */
|
|
17
|
+
skipArchive?: boolean;
|
|
18
|
+
/** Dry-run — print the ffmpeg invocation without executing. */
|
|
19
|
+
dryRun?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Only re-render a specific clip by index. When set, the renderer writes
|
|
22
|
+
* a partial mp4 at `<outputStem>.clip-<N>.mp4` that the caller can splice
|
|
23
|
+
* via a secondary concat. This supports anti-pattern E4 (don't re-render
|
|
24
|
+
* the whole edit to fix one clip).
|
|
25
|
+
*/
|
|
26
|
+
onlyClipIndex?: number;
|
|
27
|
+
}
|
|
28
|
+
export interface RenderResult {
|
|
29
|
+
outputPath: string;
|
|
30
|
+
archivedPath: string | null;
|
|
31
|
+
filterGraph: string;
|
|
32
|
+
command: string;
|
|
33
|
+
durationEstSec: number;
|
|
34
|
+
}
|
|
35
|
+
export declare function renderEdl(edl: Edl, takes: Take[], options?: RenderOptions): RenderResult;
|
|
36
|
+
//# sourceMappingURL=render.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/editing/render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAWH,OAAO,KAAK,EAAE,GAAG,EAAW,IAAI,EAAE,MAAM,YAAY,CAAC;AAErD,MAAM,WAAW,aAAa;IAC5B,qFAAqF;IACrF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,wEAAwE;IACxE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,+DAA+D;IAC/D,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;CACxB;AAuKD,wBAAgB,SAAS,CACvB,GAAG,EAAE,GAAG,EACR,KAAK,EAAE,IAAI,EAAE,EACb,OAAO,GAAE,aAAkB,GAC1B,YAAY,CAqFd"}
|