vidistill 0.5.0 → 0.5.2
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/README.md +1 -3
- package/dist/index.js +208 -247
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -69,16 +69,14 @@ vidistill-output/my-video/
|
|
|
69
69
|
├── guide.md # overview and navigation
|
|
70
70
|
├── transcript.md # full timestamped transcript
|
|
71
71
|
├── combined.md # transcript + visual notes merged
|
|
72
|
-
├── notes.md #
|
|
72
|
+
├── notes.md # notes, implicit questions/decisions, recurring themes
|
|
73
73
|
├── code/ # extracted and reconstructed source files
|
|
74
74
|
│ ├── *.ext # individual source files
|
|
75
75
|
│ └── code-timeline.md # code evolution timeline
|
|
76
76
|
├── people.md # speakers and participants
|
|
77
77
|
├── chat.md # chat messages and links
|
|
78
78
|
├── action-items.md # tasks and follow-ups
|
|
79
|
-
├── insights.md # implicit signals and analysis
|
|
80
79
|
├── links.md # all URLs mentioned
|
|
81
|
-
├── prereqs.md # prerequisite knowledge (when detected)
|
|
82
80
|
├── timeline.html # interactive visual timeline
|
|
83
81
|
├── metadata.json # processing metadata
|
|
84
82
|
└── raw/ # raw pass outputs
|
package/dist/index.js
CHANGED
|
@@ -263,6 +263,7 @@ function showConfigBox(config) {
|
|
|
263
263
|
const lines = [
|
|
264
264
|
`Video: ${config.input}`,
|
|
265
265
|
`Context: ${config.context ?? "(none)"}`,
|
|
266
|
+
`Name: ${config.outputName ?? "(auto-detect)"}`,
|
|
266
267
|
`Output: ${config.output}`
|
|
267
268
|
];
|
|
268
269
|
if (config.videoType === "audio") {
|
|
@@ -276,7 +277,7 @@ function showConfigBox(config) {
|
|
|
276
277
|
}
|
|
277
278
|
|
|
278
279
|
// src/commands/distill.ts
|
|
279
|
-
import { log as
|
|
280
|
+
import { log as log7, cancel as cancel2 } from "@clack/prompts";
|
|
280
281
|
import pc3 from "picocolors";
|
|
281
282
|
import { basename as basename3, extname as extname2, resolve, join as join4 } from "path";
|
|
282
283
|
import { existsSync as existsSync3, openSync as openSync2, readSync as readSync2, closeSync as closeSync2 } from "fs";
|
|
@@ -312,6 +313,15 @@ async function promptContext() {
|
|
|
312
313
|
const result = value.trim();
|
|
313
314
|
return result.length > 0 ? result : void 0;
|
|
314
315
|
}
|
|
316
|
+
async function promptOutputName() {
|
|
317
|
+
const value = await text({
|
|
318
|
+
message: "Output folder name",
|
|
319
|
+
placeholder: "(press Enter to auto-detect from source)"
|
|
320
|
+
});
|
|
321
|
+
handleCancel(value);
|
|
322
|
+
const result = value.trim();
|
|
323
|
+
return result.length > 0 ? result : void 0;
|
|
324
|
+
}
|
|
315
325
|
async function promptApiKey() {
|
|
316
326
|
const value = await password({
|
|
317
327
|
message: "Gemini API key",
|
|
@@ -339,6 +349,7 @@ async function promptConfirmation() {
|
|
|
339
349
|
{ value: "start", label: "Start processing" },
|
|
340
350
|
{ value: "edit-video", label: "Edit video source" },
|
|
341
351
|
{ value: "edit-context", label: "Edit context" },
|
|
352
|
+
{ value: "edit-name", label: "Edit output name" },
|
|
342
353
|
{ value: "cancel", label: "Cancel" }
|
|
343
354
|
]
|
|
344
355
|
});
|
|
@@ -657,8 +668,27 @@ function fetchYtDlpDuration(url) {
|
|
|
657
668
|
});
|
|
658
669
|
});
|
|
659
670
|
}
|
|
671
|
+
async function fetchYouTubePageDuration(url) {
|
|
672
|
+
try {
|
|
673
|
+
const normalized = normalizeYouTubeUrl(url);
|
|
674
|
+
if (!normalized) return void 0;
|
|
675
|
+
const res = await fetch(normalized, {
|
|
676
|
+
headers: { "Accept-Language": "en-US,en;q=0.9" }
|
|
677
|
+
});
|
|
678
|
+
if (!res.ok) return void 0;
|
|
679
|
+
const html = await res.text();
|
|
680
|
+
const match = html.match(/"lengthSeconds"\s*:\s*"(\d+)"/);
|
|
681
|
+
if (match?.[1]) {
|
|
682
|
+
const seconds = parseInt(match[1], 10);
|
|
683
|
+
if (seconds > 0) return seconds;
|
|
684
|
+
}
|
|
685
|
+
return void 0;
|
|
686
|
+
} catch {
|
|
687
|
+
return void 0;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
660
690
|
async function handleYouTube(url, _client) {
|
|
661
|
-
const duration = await fetchYtDlpDuration(url);
|
|
691
|
+
const duration = await fetchYtDlpDuration(url) ?? await fetchYouTubePageDuration(url);
|
|
662
692
|
return { fileUri: url, mimeType: "video/mp4", source: "direct", duration };
|
|
663
693
|
}
|
|
664
694
|
|
|
@@ -933,7 +963,7 @@ async function detectDuration(source) {
|
|
|
933
963
|
}
|
|
934
964
|
|
|
935
965
|
// src/core/pipeline.ts
|
|
936
|
-
import { log as
|
|
966
|
+
import { log as log5 } from "@clack/prompts";
|
|
937
967
|
|
|
938
968
|
// src/gemini/schemas.ts
|
|
939
969
|
import { Type } from "@google/genai";
|
|
@@ -1759,9 +1789,6 @@ function mergeTranscriptResults(pass1a, pass1b) {
|
|
|
1759
1789
|
};
|
|
1760
1790
|
}
|
|
1761
1791
|
|
|
1762
|
-
// src/core/transcript-consensus.ts
|
|
1763
|
-
import { log as log5 } from "@clack/prompts";
|
|
1764
|
-
|
|
1765
1792
|
// src/core/consensus.ts
|
|
1766
1793
|
function tokenize(content) {
|
|
1767
1794
|
const tokens = content.match(/[\p{L}\p{N}_]+/gu) ?? [];
|
|
@@ -2084,9 +2111,7 @@ async function runDiarizationConsensus(params) {
|
|
|
2084
2111
|
try {
|
|
2085
2112
|
const result = await runFn();
|
|
2086
2113
|
successfulRuns.push(result);
|
|
2087
|
-
} catch
|
|
2088
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
2089
|
-
log5.warn(`diarization consensus run ${i + 1}/${runs} failed: ${msg}`);
|
|
2114
|
+
} catch {
|
|
2090
2115
|
}
|
|
2091
2116
|
onProgress?.(i + 1, runs);
|
|
2092
2117
|
}
|
|
@@ -2137,9 +2162,7 @@ async function runTranscriptionConsensus(params) {
|
|
|
2137
2162
|
try {
|
|
2138
2163
|
const result = await runFn();
|
|
2139
2164
|
successfulRuns.push(result);
|
|
2140
|
-
} catch
|
|
2141
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
2142
|
-
log5.warn(`transcription consensus run ${i + 1}/${runs} failed: ${msg}`);
|
|
2165
|
+
} catch {
|
|
2143
2166
|
}
|
|
2144
2167
|
onProgress?.(i + 1, runs);
|
|
2145
2168
|
}
|
|
@@ -2984,7 +3007,7 @@ async function runPipeline(config) {
|
|
|
2984
3007
|
"pass0"
|
|
2985
3008
|
);
|
|
2986
3009
|
if (pass0Attempt.error !== null) {
|
|
2987
|
-
|
|
3010
|
+
log5.warn(pass0Attempt.error);
|
|
2988
3011
|
errors.push(pass0Attempt.error);
|
|
2989
3012
|
videoProfile = DEFAULT_PROFILE;
|
|
2990
3013
|
} else {
|
|
@@ -3031,7 +3054,7 @@ async function runPipeline(config) {
|
|
|
3031
3054
|
const pass1aResult = transcriptConsensusResult.result;
|
|
3032
3055
|
if (pass1aResult === null) {
|
|
3033
3056
|
const errMsg = `segment ${i} pass1a: all transcription consensus runs failed`;
|
|
3034
|
-
|
|
3057
|
+
log5.warn(errMsg);
|
|
3035
3058
|
errors.push(errMsg);
|
|
3036
3059
|
}
|
|
3037
3060
|
let pass1 = null;
|
|
@@ -3049,7 +3072,7 @@ async function runPipeline(config) {
|
|
|
3049
3072
|
});
|
|
3050
3073
|
if (pass1bResult === null) {
|
|
3051
3074
|
const errMsg = `segment ${i} pass1b: all diarization consensus runs failed`;
|
|
3052
|
-
|
|
3075
|
+
log5.warn(errMsg);
|
|
3053
3076
|
errors.push(errMsg);
|
|
3054
3077
|
pass1 = mergeTranscriptResults(pass1aResult, { speaker_assignments: [], speaker_summary: [] });
|
|
3055
3078
|
} else {
|
|
@@ -3081,7 +3104,7 @@ async function runPipeline(config) {
|
|
|
3081
3104
|
`segment ${i} pass2`
|
|
3082
3105
|
);
|
|
3083
3106
|
if (pass2Attempt.error !== null) {
|
|
3084
|
-
|
|
3107
|
+
log5.warn(pass2Attempt.error);
|
|
3085
3108
|
errors.push(pass2Attempt.error);
|
|
3086
3109
|
} else {
|
|
3087
3110
|
pass2 = pass2Attempt.result;
|
|
@@ -3114,7 +3137,7 @@ async function runPipeline(config) {
|
|
|
3114
3137
|
});
|
|
3115
3138
|
if (linkConsensusResult.runsCompleted === 0) {
|
|
3116
3139
|
const errMsg = `segment ${i} pass3c: all link consensus runs failed`;
|
|
3117
|
-
|
|
3140
|
+
log5.warn(errMsg);
|
|
3118
3141
|
errors.push(errMsg);
|
|
3119
3142
|
pass3c = null;
|
|
3120
3143
|
} else {
|
|
@@ -3143,7 +3166,7 @@ async function runPipeline(config) {
|
|
|
3143
3166
|
`segment ${i} pass3d`
|
|
3144
3167
|
);
|
|
3145
3168
|
if (pass3dAttempt.error !== null) {
|
|
3146
|
-
|
|
3169
|
+
log5.warn(pass3dAttempt.error);
|
|
3147
3170
|
errors.push(pass3dAttempt.error);
|
|
3148
3171
|
pass3d = null;
|
|
3149
3172
|
} else {
|
|
@@ -3201,7 +3224,7 @@ async function runPipeline(config) {
|
|
|
3201
3224
|
}
|
|
3202
3225
|
} catch (e) {
|
|
3203
3226
|
const msg = e instanceof Error ? e.message : String(e);
|
|
3204
|
-
|
|
3227
|
+
log5.warn(`speaker reconciliation failed, continuing with original labels: ${msg}`);
|
|
3205
3228
|
}
|
|
3206
3229
|
let peopleExtraction = null;
|
|
3207
3230
|
if (strategy.passes.includes("people")) {
|
|
@@ -3222,7 +3245,7 @@ async function runPipeline(config) {
|
|
|
3222
3245
|
"pass3b"
|
|
3223
3246
|
);
|
|
3224
3247
|
if (pass3bAttempt.error !== null) {
|
|
3225
|
-
|
|
3248
|
+
log5.warn(pass3bAttempt.error);
|
|
3226
3249
|
errors.push(pass3bAttempt.error);
|
|
3227
3250
|
} else {
|
|
3228
3251
|
peopleExtraction = pass3bAttempt.result;
|
|
@@ -3260,7 +3283,7 @@ async function runPipeline(config) {
|
|
|
3260
3283
|
});
|
|
3261
3284
|
if (consensusResult.runsCompleted === 0) {
|
|
3262
3285
|
const errMsg = "pass3a: all consensus runs failed";
|
|
3263
|
-
|
|
3286
|
+
log5.warn(errMsg);
|
|
3264
3287
|
errors.push(errMsg);
|
|
3265
3288
|
} else {
|
|
3266
3289
|
const validationResult = validateCodeReconstruction({
|
|
@@ -3299,7 +3322,7 @@ async function runPipeline(config) {
|
|
|
3299
3322
|
"synthesis"
|
|
3300
3323
|
);
|
|
3301
3324
|
if (synthAttempt.error !== null) {
|
|
3302
|
-
|
|
3325
|
+
log5.warn(synthAttempt.error);
|
|
3303
3326
|
errors.push(synthAttempt.error);
|
|
3304
3327
|
} else {
|
|
3305
3328
|
synthesisResult = synthAttempt.result ?? void 0;
|
|
@@ -3370,6 +3393,40 @@ function renderProcessingDetails(pipelineResult, speakerMapping) {
|
|
|
3370
3393
|
lines.push(`- **Segments processed:** ${pipelineResult.segments.length}`);
|
|
3371
3394
|
return lines.join("\n");
|
|
3372
3395
|
}
|
|
3396
|
+
var PREREQ_LEVEL_ORDER = ["advanced", "intermediate", "basic"];
|
|
3397
|
+
var PREREQ_LEVEL_LABELS = {
|
|
3398
|
+
advanced: "Advanced",
|
|
3399
|
+
intermediate: "Intermediate",
|
|
3400
|
+
basic: "Basic"
|
|
3401
|
+
};
|
|
3402
|
+
function renderPrerequisites(prerequisites) {
|
|
3403
|
+
if (prerequisites == null || prerequisites.length === 0) return "";
|
|
3404
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
3405
|
+
for (const level of PREREQ_LEVEL_ORDER) {
|
|
3406
|
+
grouped.set(level, []);
|
|
3407
|
+
}
|
|
3408
|
+
for (const c of prerequisites) {
|
|
3409
|
+
const bucket = grouped.get(c.assumed_knowledge_level);
|
|
3410
|
+
if (bucket != null) {
|
|
3411
|
+
bucket.push(c);
|
|
3412
|
+
}
|
|
3413
|
+
}
|
|
3414
|
+
const lines = ["", "## Prerequisites", ""];
|
|
3415
|
+
for (const level of PREREQ_LEVEL_ORDER) {
|
|
3416
|
+
const concepts = grouped.get(level) ?? [];
|
|
3417
|
+
if (concepts.length === 0) continue;
|
|
3418
|
+
lines.push(`### ${PREREQ_LEVEL_LABELS[level]} Knowledge`, "");
|
|
3419
|
+
for (const c of concepts) {
|
|
3420
|
+
lines.push(`**${c.concept}**`);
|
|
3421
|
+
lines.push("");
|
|
3422
|
+
lines.push(c.brief_explanation);
|
|
3423
|
+
lines.push("");
|
|
3424
|
+
lines.push(`_First assumed at: ${c.timestamp_first_assumed}_`);
|
|
3425
|
+
lines.push("");
|
|
3426
|
+
}
|
|
3427
|
+
}
|
|
3428
|
+
return lines.join("\n");
|
|
3429
|
+
}
|
|
3373
3430
|
function renderIncompletePasses(pipelineResult) {
|
|
3374
3431
|
const { errors, interrupted } = pipelineResult;
|
|
3375
3432
|
const hasErrors = errors.length > 0;
|
|
@@ -3415,7 +3472,7 @@ function writeGuide(params) {
|
|
|
3415
3472
|
"## Suggestions",
|
|
3416
3473
|
"",
|
|
3417
3474
|
renderSuggestions(synthesisResult, speakerMapping),
|
|
3418
|
-
|
|
3475
|
+
renderPrerequisites(synthesisResult?.prerequisites),
|
|
3419
3476
|
"## Processing Details",
|
|
3420
3477
|
"",
|
|
3421
3478
|
renderProcessingDetails(pipelineResult, speakerMapping),
|
|
@@ -3706,13 +3763,79 @@ function renderActionItems(items, speakerMapping) {
|
|
|
3706
3763
|
lines.push("");
|
|
3707
3764
|
return lines;
|
|
3708
3765
|
}
|
|
3766
|
+
function collectImplicitQuestions(segments) {
|
|
3767
|
+
const questions = [];
|
|
3768
|
+
for (const seg of segments) {
|
|
3769
|
+
if (seg.pass3d != null) {
|
|
3770
|
+
questions.push(...seg.pass3d.questions_implicit);
|
|
3771
|
+
}
|
|
3772
|
+
}
|
|
3773
|
+
return questions;
|
|
3774
|
+
}
|
|
3775
|
+
function collectImplicitDecisions(segments) {
|
|
3776
|
+
const decisions = [];
|
|
3777
|
+
for (const seg of segments) {
|
|
3778
|
+
if (seg.pass3d != null) {
|
|
3779
|
+
decisions.push(...seg.pass3d.decisions_implicit);
|
|
3780
|
+
}
|
|
3781
|
+
}
|
|
3782
|
+
return decisions;
|
|
3783
|
+
}
|
|
3784
|
+
function collectEmphasisPatterns(segments) {
|
|
3785
|
+
const patterns = [];
|
|
3786
|
+
for (const seg of segments) {
|
|
3787
|
+
if (seg.pass3d != null) {
|
|
3788
|
+
patterns.push(...seg.pass3d.emphasis_patterns);
|
|
3789
|
+
}
|
|
3790
|
+
}
|
|
3791
|
+
return patterns;
|
|
3792
|
+
}
|
|
3793
|
+
function renderImplicitQuestions(questions, speakerMapping) {
|
|
3794
|
+
if (questions.length === 0) return [];
|
|
3795
|
+
const lines = ["## Implicit Questions", ""];
|
|
3796
|
+
for (const q of questions) {
|
|
3797
|
+
lines.push(`- ${replaceNamesInText(q, speakerMapping)}`);
|
|
3798
|
+
}
|
|
3799
|
+
lines.push("");
|
|
3800
|
+
return lines;
|
|
3801
|
+
}
|
|
3802
|
+
function renderImplicitDecisions(decisions, speakerMapping) {
|
|
3803
|
+
if (decisions.length === 0) return [];
|
|
3804
|
+
const lines = ["## Implicit Decisions", ""];
|
|
3805
|
+
for (const d of decisions) {
|
|
3806
|
+
lines.push(`- ${replaceNamesInText(d, speakerMapping)}`);
|
|
3807
|
+
}
|
|
3808
|
+
lines.push("");
|
|
3809
|
+
return lines;
|
|
3810
|
+
}
|
|
3811
|
+
function renderRecurringThemes(patterns, speakerMapping) {
|
|
3812
|
+
if (patterns.length === 0) return [];
|
|
3813
|
+
const sorted = [...patterns].sort((a, b) => b.times_mentioned - a.times_mentioned);
|
|
3814
|
+
const lines = ["## Recurring Themes", ""];
|
|
3815
|
+
for (const p of sorted) {
|
|
3816
|
+
const ts = p.timestamps.length > 0 ? ` _(${p.timestamps.join(", ")})_` : "";
|
|
3817
|
+
lines.push(`### ${p.concept} (\xD7${p.times_mentioned})${ts}`);
|
|
3818
|
+
lines.push("");
|
|
3819
|
+
if (p.significance.length > 0) {
|
|
3820
|
+
lines.push(replaceNamesInText(p.significance, speakerMapping));
|
|
3821
|
+
lines.push("");
|
|
3822
|
+
}
|
|
3823
|
+
}
|
|
3824
|
+
return lines;
|
|
3825
|
+
}
|
|
3709
3826
|
function hasMeaningfulContent(s) {
|
|
3710
3827
|
return s.key_concepts.length > 0 || s.key_decisions.length > 0 || s.topics.length > 0 || s.questions_raised.length > 0 || s.action_items.length > 0;
|
|
3711
3828
|
}
|
|
3829
|
+
function hasPass3dContent(segments) {
|
|
3830
|
+
return segments.some(
|
|
3831
|
+
(s) => s.pass3d != null && (s.pass3d.questions_implicit.length > 0 || s.pass3d.decisions_implicit.length > 0 || s.pass3d.emphasis_patterns.length > 0)
|
|
3832
|
+
);
|
|
3833
|
+
}
|
|
3712
3834
|
function writeNotes(params) {
|
|
3713
|
-
const { synthesisResult, speakerMapping } = params;
|
|
3835
|
+
const { synthesisResult, segments, speakerMapping } = params;
|
|
3714
3836
|
if (synthesisResult == null) return null;
|
|
3715
|
-
|
|
3837
|
+
const hasPass3d = segments != null && hasPass3dContent(segments);
|
|
3838
|
+
if (!hasMeaningfulContent(synthesisResult) && !hasPass3d) return null;
|
|
3716
3839
|
const sections = ["# Notes", ""];
|
|
3717
3840
|
if (synthesisResult.overview.length > 0) {
|
|
3718
3841
|
sections.push(replaceNamesInText(synthesisResult.overview, speakerMapping));
|
|
@@ -3723,6 +3846,11 @@ function writeNotes(params) {
|
|
|
3723
3846
|
sections.push(...renderTopics(synthesisResult.topics, speakerMapping));
|
|
3724
3847
|
sections.push(...renderQuestions(synthesisResult.questions_raised, speakerMapping));
|
|
3725
3848
|
sections.push(...renderActionItems(synthesisResult.action_items, speakerMapping));
|
|
3849
|
+
if (segments != null) {
|
|
3850
|
+
sections.push(...renderImplicitQuestions(collectImplicitQuestions(segments), speakerMapping));
|
|
3851
|
+
sections.push(...renderImplicitDecisions(collectImplicitDecisions(segments), speakerMapping));
|
|
3852
|
+
sections.push(...renderRecurringThemes(collectEmphasisPatterns(segments), speakerMapping));
|
|
3853
|
+
}
|
|
3726
3854
|
while (sections[sections.length - 1] === "") sections.pop();
|
|
3727
3855
|
return sections.join("\n");
|
|
3728
3856
|
}
|
|
@@ -4049,150 +4177,6 @@ function writeActionItems(params) {
|
|
|
4049
4177
|
return sections.join("\n");
|
|
4050
4178
|
}
|
|
4051
4179
|
|
|
4052
|
-
// src/output/insights.ts
|
|
4053
|
-
function collectEmotionalShifts(segments) {
|
|
4054
|
-
const shifts = [];
|
|
4055
|
-
for (const seg of segments) {
|
|
4056
|
-
if (seg.pass3d != null) {
|
|
4057
|
-
shifts.push(...seg.pass3d.emotional_shifts);
|
|
4058
|
-
}
|
|
4059
|
-
}
|
|
4060
|
-
return shifts;
|
|
4061
|
-
}
|
|
4062
|
-
function collectEmphasisPatterns(segments) {
|
|
4063
|
-
const patterns = [];
|
|
4064
|
-
for (const seg of segments) {
|
|
4065
|
-
if (seg.pass3d != null) {
|
|
4066
|
-
patterns.push(...seg.pass3d.emphasis_patterns);
|
|
4067
|
-
}
|
|
4068
|
-
}
|
|
4069
|
-
return patterns;
|
|
4070
|
-
}
|
|
4071
|
-
function collectImplicitQuestions(segments) {
|
|
4072
|
-
const questions = [];
|
|
4073
|
-
for (const seg of segments) {
|
|
4074
|
-
if (seg.pass3d != null) {
|
|
4075
|
-
questions.push(...seg.pass3d.questions_implicit);
|
|
4076
|
-
}
|
|
4077
|
-
}
|
|
4078
|
-
return questions;
|
|
4079
|
-
}
|
|
4080
|
-
function collectImplicitDecisions(segments) {
|
|
4081
|
-
const decisions = [];
|
|
4082
|
-
for (const seg of segments) {
|
|
4083
|
-
if (seg.pass3d != null) {
|
|
4084
|
-
decisions.push(...seg.pass3d.decisions_implicit);
|
|
4085
|
-
}
|
|
4086
|
-
}
|
|
4087
|
-
return decisions;
|
|
4088
|
-
}
|
|
4089
|
-
function renderEmotionalShifts(shifts, speakerMapping) {
|
|
4090
|
-
if (shifts.length === 0) return [];
|
|
4091
|
-
const lines = ["## Emotional Shifts", ""];
|
|
4092
|
-
for (const s of shifts) {
|
|
4093
|
-
lines.push(`- **[${s.timestamp}]** ${s.from_state} \u2192 ${s.to_state}`);
|
|
4094
|
-
if (s.trigger.length > 0) {
|
|
4095
|
-
lines.push(` _Trigger: ${replaceNamesInText(s.trigger, speakerMapping)}_`);
|
|
4096
|
-
}
|
|
4097
|
-
}
|
|
4098
|
-
lines.push("");
|
|
4099
|
-
return lines;
|
|
4100
|
-
}
|
|
4101
|
-
function renderEmphasisPatterns(patterns, speakerMapping) {
|
|
4102
|
-
if (patterns.length === 0) return [];
|
|
4103
|
-
const sorted = [...patterns].sort((a, b) => b.times_mentioned - a.times_mentioned);
|
|
4104
|
-
const lines = ["## Emphasis Patterns", ""];
|
|
4105
|
-
for (const p of sorted) {
|
|
4106
|
-
const ts = p.timestamps.length > 0 ? ` _(${p.timestamps.join(", ")})_` : "";
|
|
4107
|
-
lines.push(`### ${p.concept} (\xD7${p.times_mentioned})${ts}`);
|
|
4108
|
-
lines.push("");
|
|
4109
|
-
if (p.significance.length > 0) {
|
|
4110
|
-
lines.push(replaceNamesInText(p.significance, speakerMapping));
|
|
4111
|
-
lines.push("");
|
|
4112
|
-
}
|
|
4113
|
-
}
|
|
4114
|
-
return lines;
|
|
4115
|
-
}
|
|
4116
|
-
function renderImplicitQuestions(questions, speakerMapping) {
|
|
4117
|
-
if (questions.length === 0) return [];
|
|
4118
|
-
const lines = ["## Implicit Questions", ""];
|
|
4119
|
-
for (const q of questions) {
|
|
4120
|
-
lines.push(`- ${replaceNamesInText(q, speakerMapping)}`);
|
|
4121
|
-
}
|
|
4122
|
-
lines.push("");
|
|
4123
|
-
return lines;
|
|
4124
|
-
}
|
|
4125
|
-
function renderImplicitDecisions(decisions, speakerMapping) {
|
|
4126
|
-
if (decisions.length === 0) return [];
|
|
4127
|
-
const lines = ["## Implicit Decisions", ""];
|
|
4128
|
-
for (const d of decisions) {
|
|
4129
|
-
lines.push(`- ${replaceNamesInText(d, speakerMapping)}`);
|
|
4130
|
-
}
|
|
4131
|
-
lines.push("");
|
|
4132
|
-
return lines;
|
|
4133
|
-
}
|
|
4134
|
-
function writeInsights(params) {
|
|
4135
|
-
const { segments, speakerMapping } = params;
|
|
4136
|
-
const hasPass3d = segments.some((s) => s.pass3d != null);
|
|
4137
|
-
if (!hasPass3d) return null;
|
|
4138
|
-
const emotionalShifts = collectEmotionalShifts(segments);
|
|
4139
|
-
const emphasisPatterns = collectEmphasisPatterns(segments);
|
|
4140
|
-
const implicitQuestions = collectImplicitQuestions(segments);
|
|
4141
|
-
const implicitDecisions = collectImplicitDecisions(segments);
|
|
4142
|
-
if (emotionalShifts.length === 0 && emphasisPatterns.length === 0 && implicitQuestions.length === 0 && implicitDecisions.length === 0) {
|
|
4143
|
-
return null;
|
|
4144
|
-
}
|
|
4145
|
-
const sections = ["# Insights", ""];
|
|
4146
|
-
sections.push(...renderEmotionalShifts(emotionalShifts, speakerMapping));
|
|
4147
|
-
sections.push(...renderEmphasisPatterns(emphasisPatterns, speakerMapping));
|
|
4148
|
-
sections.push(...renderImplicitQuestions(implicitQuestions, speakerMapping));
|
|
4149
|
-
sections.push(...renderImplicitDecisions(implicitDecisions, speakerMapping));
|
|
4150
|
-
while (sections[sections.length - 1] === "") sections.pop();
|
|
4151
|
-
return sections.join("\n");
|
|
4152
|
-
}
|
|
4153
|
-
|
|
4154
|
-
// src/output/prereqs.ts
|
|
4155
|
-
var LEVEL_ORDER = ["advanced", "intermediate", "basic"];
|
|
4156
|
-
var LEVEL_LABELS = {
|
|
4157
|
-
advanced: "Advanced",
|
|
4158
|
-
intermediate: "Intermediate",
|
|
4159
|
-
basic: "Basic"
|
|
4160
|
-
};
|
|
4161
|
-
function renderLevelSection(level, concepts) {
|
|
4162
|
-
if (concepts.length === 0) return [];
|
|
4163
|
-
const lines = [`## ${LEVEL_LABELS[level]} Knowledge`, ""];
|
|
4164
|
-
for (const c of concepts) {
|
|
4165
|
-
lines.push(`### ${c.concept}`);
|
|
4166
|
-
lines.push("");
|
|
4167
|
-
lines.push(c.brief_explanation);
|
|
4168
|
-
lines.push("");
|
|
4169
|
-
lines.push(`_First assumed at: ${c.timestamp_first_assumed}_`);
|
|
4170
|
-
lines.push("");
|
|
4171
|
-
}
|
|
4172
|
-
return lines;
|
|
4173
|
-
}
|
|
4174
|
-
function writePrereqs(params) {
|
|
4175
|
-
const { prerequisites } = params;
|
|
4176
|
-
if (prerequisites == null || prerequisites.length === 0) return null;
|
|
4177
|
-
const grouped = /* @__PURE__ */ new Map();
|
|
4178
|
-
for (const level of LEVEL_ORDER) {
|
|
4179
|
-
grouped.set(level, []);
|
|
4180
|
-
}
|
|
4181
|
-
for (const c of prerequisites) {
|
|
4182
|
-
const bucket = grouped.get(c.assumed_knowledge_level);
|
|
4183
|
-
if (bucket != null) {
|
|
4184
|
-
bucket.push(c);
|
|
4185
|
-
}
|
|
4186
|
-
}
|
|
4187
|
-
const sections = ["# Prerequisites", ""];
|
|
4188
|
-
for (const level of LEVEL_ORDER) {
|
|
4189
|
-
const concepts = grouped.get(level) ?? [];
|
|
4190
|
-
sections.push(...renderLevelSection(level, concepts));
|
|
4191
|
-
}
|
|
4192
|
-
while (sections[sections.length - 1] === "") sections.pop();
|
|
4193
|
-
return sections.join("\n");
|
|
4194
|
-
}
|
|
4195
|
-
|
|
4196
4180
|
// src/output/timeline.ts
|
|
4197
4181
|
function toPercent(seconds, duration) {
|
|
4198
4182
|
if (duration <= 0) return "0";
|
|
@@ -4682,13 +4666,9 @@ function resolveFilesToGenerate(params) {
|
|
|
4682
4666
|
}
|
|
4683
4667
|
if (hasPass3d) {
|
|
4684
4668
|
optional.add("action-items.md");
|
|
4685
|
-
optional.add("insights.md");
|
|
4686
4669
|
}
|
|
4687
|
-
if (synthesisResult != null) optional.add("notes.md");
|
|
4670
|
+
if (synthesisResult != null || hasPass3d) optional.add("notes.md");
|
|
4688
4671
|
if (peopleExtraction != null) optional.add("people.md");
|
|
4689
|
-
if (synthesisResult?.prerequisites != null && Array.isArray(synthesisResult.prerequisites) && synthesisResult.prerequisites.length > 0) {
|
|
4690
|
-
optional.add("prereqs.md");
|
|
4691
|
-
}
|
|
4692
4672
|
const hasPass1 = segments.some((s) => s.pass1 != null);
|
|
4693
4673
|
if (hasPass1 || hasPass2) optional.add("timeline.html");
|
|
4694
4674
|
return optional;
|
|
@@ -4747,7 +4727,7 @@ async function generateOutput(params) {
|
|
|
4747
4727
|
}
|
|
4748
4728
|
if (filesToGenerate.has("notes.md")) {
|
|
4749
4729
|
try {
|
|
4750
|
-
const content = writeNotes({ synthesisResult: pipelineResult.synthesisResult, speakerMapping: expandedMapping });
|
|
4730
|
+
const content = writeNotes({ synthesisResult: pipelineResult.synthesisResult, segments: pipelineResult.segments, speakerMapping: expandedMapping });
|
|
4751
4731
|
if (content != null) {
|
|
4752
4732
|
await writeOutputFile("notes.md", content);
|
|
4753
4733
|
}
|
|
@@ -4799,26 +4779,6 @@ async function generateOutput(params) {
|
|
|
4799
4779
|
errors.push(`action-items.md: ${String(err)}`);
|
|
4800
4780
|
}
|
|
4801
4781
|
}
|
|
4802
|
-
if (filesToGenerate.has("insights.md")) {
|
|
4803
|
-
try {
|
|
4804
|
-
const content = writeInsights({ segments: pipelineResult.segments, speakerMapping: expandedMapping });
|
|
4805
|
-
if (content != null) {
|
|
4806
|
-
await writeOutputFile("insights.md", content);
|
|
4807
|
-
}
|
|
4808
|
-
} catch (err) {
|
|
4809
|
-
errors.push(`insights.md: ${String(err)}`);
|
|
4810
|
-
}
|
|
4811
|
-
}
|
|
4812
|
-
if (filesToGenerate.has("prereqs.md")) {
|
|
4813
|
-
try {
|
|
4814
|
-
const content = writePrereqs({ prerequisites: pipelineResult.synthesisResult?.prerequisites });
|
|
4815
|
-
if (content != null) {
|
|
4816
|
-
await writeOutputFile("prereqs.md", content);
|
|
4817
|
-
}
|
|
4818
|
-
} catch (err) {
|
|
4819
|
-
errors.push(`prereqs.md: ${String(err)}`);
|
|
4820
|
-
}
|
|
4821
|
-
}
|
|
4822
4782
|
if (filesToGenerate.has("timeline.html")) {
|
|
4823
4783
|
try {
|
|
4824
4784
|
const content = generateTimeline({ pipelineResult, duration, speakerMapping: expandedMapping });
|
|
@@ -4935,7 +4895,7 @@ async function reRenderWithSpeakerMapping(params) {
|
|
|
4935
4895
|
}
|
|
4936
4896
|
if (filesToReRender.has("notes.md")) {
|
|
4937
4897
|
try {
|
|
4938
|
-
const content = writeNotes({ synthesisResult: pipelineResult.synthesisResult, speakerMapping: expandedMapping });
|
|
4898
|
+
const content = writeNotes({ synthesisResult: pipelineResult.synthesisResult, segments: pipelineResult.segments, speakerMapping: expandedMapping });
|
|
4939
4899
|
if (content != null) await writeOutputFile("notes.md", content);
|
|
4940
4900
|
} catch (err) {
|
|
4941
4901
|
errors.push(`notes.md: ${String(err)}`);
|
|
@@ -4969,14 +4929,6 @@ async function reRenderWithSpeakerMapping(params) {
|
|
|
4969
4929
|
errors.push(`action-items.md: ${String(err)}`);
|
|
4970
4930
|
}
|
|
4971
4931
|
}
|
|
4972
|
-
if (filesToReRender.has("insights.md")) {
|
|
4973
|
-
try {
|
|
4974
|
-
const content = writeInsights({ segments: pipelineResult.segments, speakerMapping: expandedMapping });
|
|
4975
|
-
if (content != null) await writeOutputFile("insights.md", content);
|
|
4976
|
-
} catch (err) {
|
|
4977
|
-
errors.push(`insights.md: ${String(err)}`);
|
|
4978
|
-
}
|
|
4979
|
-
}
|
|
4980
4932
|
if (filesToReRender.has("timeline.html")) {
|
|
4981
4933
|
try {
|
|
4982
4934
|
const content = generateTimeline({ pipelineResult, duration, speakerMapping: expandedMapping });
|
|
@@ -5017,7 +4969,7 @@ async function reRenderWithSpeakerMapping(params) {
|
|
|
5017
4969
|
}
|
|
5018
4970
|
|
|
5019
4971
|
// src/core/shutdown.ts
|
|
5020
|
-
import { log as
|
|
4972
|
+
import { log as log6 } from "@clack/prompts";
|
|
5021
4973
|
function createShutdownHandler(params) {
|
|
5022
4974
|
const { client, uploadedFileNames } = params;
|
|
5023
4975
|
let shuttingDown = false;
|
|
@@ -5033,10 +4985,10 @@ function createShutdownHandler(params) {
|
|
|
5033
4985
|
}
|
|
5034
4986
|
shuttingDown = true;
|
|
5035
4987
|
if (hasProgress) {
|
|
5036
|
-
|
|
5037
|
-
|
|
4988
|
+
log6.warn(`Interrupted \u2014 progress saved (${progressCurrentStep}/${progressTotalSteps} steps)`);
|
|
4989
|
+
log6.info(`Resume: vidistill ${params.source} -o ${params.outputDir}/`);
|
|
5038
4990
|
} else {
|
|
5039
|
-
|
|
4991
|
+
log6.warn("Interrupted");
|
|
5040
4992
|
}
|
|
5041
4993
|
const forceExitHandler = () => {
|
|
5042
4994
|
process.exit(1);
|
|
@@ -5121,6 +5073,7 @@ async function runDistill(args) {
|
|
|
5121
5073
|
const apiKey = await resolveApiKey();
|
|
5122
5074
|
let rawInput = args.input ?? await promptVideoSource();
|
|
5123
5075
|
let context = args.context ?? await promptContext();
|
|
5076
|
+
let outputName = await promptOutputName();
|
|
5124
5077
|
const allFlagsProvided = args.input != null && args.context != null;
|
|
5125
5078
|
if (!allFlagsProvided) {
|
|
5126
5079
|
let confirmed = false;
|
|
@@ -5131,6 +5084,7 @@ async function runDistill(args) {
|
|
|
5131
5084
|
input: rawInput,
|
|
5132
5085
|
context,
|
|
5133
5086
|
output: args.output,
|
|
5087
|
+
outputName,
|
|
5134
5088
|
videoType: inputIsAudio ? "audio" : void 0,
|
|
5135
5089
|
lang: args.lang
|
|
5136
5090
|
});
|
|
@@ -5145,6 +5099,9 @@ async function runDistill(args) {
|
|
|
5145
5099
|
case "edit-context":
|
|
5146
5100
|
context = await promptContext();
|
|
5147
5101
|
break;
|
|
5102
|
+
case "edit-name":
|
|
5103
|
+
outputName = await promptOutputName();
|
|
5104
|
+
break;
|
|
5148
5105
|
case "cancel":
|
|
5149
5106
|
cancel2("Cancelled.");
|
|
5150
5107
|
process.exit(0);
|
|
@@ -5169,6 +5126,7 @@ async function runDistill(args) {
|
|
|
5169
5126
|
});
|
|
5170
5127
|
} catch {
|
|
5171
5128
|
duration = 600;
|
|
5129
|
+
log7.warn("Could not detect video duration \u2014 defaulting to 10 minutes. Install yt-dlp for full-length processing: brew install yt-dlp");
|
|
5172
5130
|
}
|
|
5173
5131
|
if (result.uploadedFileName != null) {
|
|
5174
5132
|
uploadedFileNames = [result.uploadedFileName];
|
|
@@ -5188,6 +5146,9 @@ async function runDistill(args) {
|
|
|
5188
5146
|
}
|
|
5189
5147
|
videoTitle = basename3(resolved.value, extname2(resolved.value));
|
|
5190
5148
|
}
|
|
5149
|
+
if (outputName != null) {
|
|
5150
|
+
videoTitle = outputName;
|
|
5151
|
+
}
|
|
5191
5152
|
const model = MODELS.flash;
|
|
5192
5153
|
const outputDir = resolve(args.output);
|
|
5193
5154
|
const slug = slugify(videoTitle);
|
|
@@ -5245,32 +5206,32 @@ async function runDistill(args) {
|
|
|
5245
5206
|
const elapsedMins = Math.floor(elapsedSecs / 60);
|
|
5246
5207
|
const remainSecs = elapsedSecs % 60;
|
|
5247
5208
|
const elapsed = elapsedMins > 0 ? `${elapsedMins}m ${remainSecs}s` : `${remainSecs}s`;
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5209
|
+
log7.success(`Done in ${elapsed}`);
|
|
5210
|
+
log7.info(`Output: ${finalOutputDir}/`);
|
|
5211
|
+
log7.info(pc3.dim("Open guide.md for an overview"));
|
|
5251
5212
|
if (pipelineResult.peopleExtraction?.participants != null && pipelineResult.peopleExtraction.participants.length > 1) {
|
|
5252
|
-
|
|
5213
|
+
log7.info(pc3.dim("Tip: vidistill rename-speakers <dir> to assign real names"));
|
|
5253
5214
|
}
|
|
5254
5215
|
if (outputResult.errors.length > 0) {
|
|
5255
|
-
|
|
5216
|
+
log7.warn(`Output errors: ${pc3.yellow(String(outputResult.errors.length))}`);
|
|
5256
5217
|
for (const err of outputResult.errors) {
|
|
5257
|
-
|
|
5218
|
+
log7.warn(pc3.dim(` ${err}`));
|
|
5258
5219
|
}
|
|
5259
5220
|
}
|
|
5260
5221
|
}
|
|
5261
5222
|
|
|
5262
5223
|
// src/commands/mcp.ts
|
|
5263
|
-
import { log as
|
|
5224
|
+
import { log as log8 } from "@clack/prompts";
|
|
5264
5225
|
async function run(_args) {
|
|
5265
|
-
|
|
5226
|
+
log8.info("Not implemented yet.");
|
|
5266
5227
|
}
|
|
5267
5228
|
|
|
5268
5229
|
// src/commands/rename-speakers.ts
|
|
5269
5230
|
import { join as join5 } from "path";
|
|
5270
|
-
import { log as
|
|
5231
|
+
import { log as log10, text as text3, isCancel as isCancel3, cancel as cancel4 } from "@clack/prompts";
|
|
5271
5232
|
|
|
5272
5233
|
// src/cli/speaker-naming.ts
|
|
5273
|
-
import { log as
|
|
5234
|
+
import { log as log9, text as text2, confirm as confirm2, isCancel as isCancel2, cancel as cancel3 } from "@clack/prompts";
|
|
5274
5235
|
async function detectAndPromptMerges(mapping) {
|
|
5275
5236
|
const byName = /* @__PURE__ */ new Map();
|
|
5276
5237
|
for (const [label, name] of Object.entries(mapping)) {
|
|
@@ -5429,19 +5390,19 @@ async function runList(outputDir) {
|
|
|
5429
5390
|
const metadataPath = join5(outputDir, "metadata.json");
|
|
5430
5391
|
const metadata = await readJsonFile(metadataPath);
|
|
5431
5392
|
if (metadata == null) {
|
|
5432
|
-
|
|
5393
|
+
log10.error("Not a vidistill output directory");
|
|
5433
5394
|
return;
|
|
5434
5395
|
}
|
|
5435
5396
|
const rawDir = join5(outputDir, "raw");
|
|
5436
5397
|
const speakers = await collectSpeakersFromRaw(rawDir);
|
|
5437
5398
|
const speakerMapping = metadata.speakerMapping ?? {};
|
|
5438
5399
|
if (speakers.length === 0 && Object.keys(speakerMapping).length === 0) {
|
|
5439
|
-
|
|
5400
|
+
log10.info("No speakers found.");
|
|
5440
5401
|
return;
|
|
5441
5402
|
}
|
|
5442
5403
|
const groups = groupSpeakersByExistingMapping(speakers, speakerMapping);
|
|
5443
5404
|
if (groups.length === 0) {
|
|
5444
|
-
|
|
5405
|
+
log10.info("No speakers found.");
|
|
5445
5406
|
return;
|
|
5446
5407
|
}
|
|
5447
5408
|
const lines = groups.map((group, idx) => {
|
|
@@ -5450,17 +5411,17 @@ async function runList(outputDir) {
|
|
|
5450
5411
|
const labelsStr = group.labels.join(", ");
|
|
5451
5412
|
return `${String(num)}. ${displayName} (${labelsStr}, ${String(group.totalEntries)} entries)`;
|
|
5452
5413
|
});
|
|
5453
|
-
|
|
5414
|
+
log10.info(lines.join("\n"));
|
|
5454
5415
|
}
|
|
5455
5416
|
async function runRename(outputDir, oldName, newName) {
|
|
5456
5417
|
if (newName.trim().length === 0) {
|
|
5457
|
-
|
|
5418
|
+
log10.error("New name cannot be empty. Use the interactive prompt to clear a mapping.");
|
|
5458
5419
|
return;
|
|
5459
5420
|
}
|
|
5460
5421
|
const metadataPath = join5(outputDir, "metadata.json");
|
|
5461
5422
|
const metadata = await readJsonFile(metadataPath);
|
|
5462
5423
|
if (metadata == null) {
|
|
5463
|
-
|
|
5424
|
+
log10.error("Not a vidistill output directory");
|
|
5464
5425
|
return;
|
|
5465
5426
|
}
|
|
5466
5427
|
const speakerMapping = { ...metadata.speakerMapping ?? {} };
|
|
@@ -5481,18 +5442,18 @@ async function runRename(outputDir, oldName, newName) {
|
|
|
5481
5442
|
const currentNames = Object.values(speakerMapping);
|
|
5482
5443
|
const unmappedLabels = speakers.filter((s) => speakerMapping[s.label] == null).map((s) => s.label);
|
|
5483
5444
|
const allNames = [.../* @__PURE__ */ new Set([...currentNames, ...unmappedLabels])];
|
|
5484
|
-
|
|
5445
|
+
log10.error(`No speaker named "${oldName}" found. Current speakers: ${formatNameList(allNames)}`);
|
|
5485
5446
|
return;
|
|
5486
5447
|
}
|
|
5487
5448
|
if (matchingKeys.length > 1) {
|
|
5488
|
-
|
|
5449
|
+
log10.error(
|
|
5489
5450
|
`Multiple speakers named "${oldName}" (${matchingKeys.join(", ")}). Use SPEAKER_XX label to specify which one.`
|
|
5490
5451
|
);
|
|
5491
5452
|
return;
|
|
5492
5453
|
}
|
|
5493
5454
|
const key = matchingKeys[0];
|
|
5494
5455
|
speakerMapping[key] = newName;
|
|
5495
|
-
|
|
5456
|
+
log10.info("Re-rendering output files with updated speaker names...");
|
|
5496
5457
|
const result = await reRenderWithSpeakerMapping({
|
|
5497
5458
|
outputDir,
|
|
5498
5459
|
speakerMapping,
|
|
@@ -5500,16 +5461,16 @@ async function runRename(outputDir, oldName, newName) {
|
|
|
5500
5461
|
});
|
|
5501
5462
|
if (result.errors.length > 0) {
|
|
5502
5463
|
for (const err of result.errors) {
|
|
5503
|
-
|
|
5464
|
+
log10.error(err);
|
|
5504
5465
|
}
|
|
5505
5466
|
}
|
|
5506
|
-
|
|
5467
|
+
log10.info(`Done. ${String(result.filesGenerated.length)} file${result.filesGenerated.length === 1 ? "" : "s"} updated.`);
|
|
5507
5468
|
}
|
|
5508
5469
|
async function runMerge(outputDir, sourceName, targetName) {
|
|
5509
5470
|
const metadataPath = join5(outputDir, "metadata.json");
|
|
5510
5471
|
const metadata = await readJsonFile(metadataPath);
|
|
5511
5472
|
if (metadata == null) {
|
|
5512
|
-
|
|
5473
|
+
log10.error("Not a vidistill output directory");
|
|
5513
5474
|
return;
|
|
5514
5475
|
}
|
|
5515
5476
|
const speakerMapping = { ...metadata.speakerMapping ?? {} };
|
|
@@ -5538,19 +5499,19 @@ async function runMerge(outputDir, sourceName, targetName) {
|
|
|
5538
5499
|
const targetKeys = findKeys(targetName);
|
|
5539
5500
|
if (sourceKeys.length === 0) {
|
|
5540
5501
|
const currentNames = buildCurrentNames(speakers, speakerMapping);
|
|
5541
|
-
|
|
5502
|
+
log10.error(`No speaker named "${sourceName}" found. Current speakers: ${formatNameList(currentNames)}`);
|
|
5542
5503
|
return;
|
|
5543
5504
|
}
|
|
5544
5505
|
if (targetKeys.length === 0) {
|
|
5545
5506
|
const currentNames = buildCurrentNames(speakers, speakerMapping);
|
|
5546
|
-
|
|
5507
|
+
log10.error(`No speaker named "${targetName}" found. Current speakers: ${formatNameList(currentNames)}`);
|
|
5547
5508
|
return;
|
|
5548
5509
|
}
|
|
5549
5510
|
const resolvedTargetName = speakerMapping[targetKeys[0]] ?? targetName;
|
|
5550
5511
|
for (const key of sourceKeys) {
|
|
5551
5512
|
speakerMapping[key] = resolvedTargetName;
|
|
5552
5513
|
}
|
|
5553
|
-
|
|
5514
|
+
log10.info("Re-rendering output files with updated speaker names...");
|
|
5554
5515
|
const result = await reRenderWithSpeakerMapping({
|
|
5555
5516
|
outputDir,
|
|
5556
5517
|
speakerMapping,
|
|
@@ -5558,10 +5519,10 @@ async function runMerge(outputDir, sourceName, targetName) {
|
|
|
5558
5519
|
});
|
|
5559
5520
|
if (result.errors.length > 0) {
|
|
5560
5521
|
for (const err of result.errors) {
|
|
5561
|
-
|
|
5522
|
+
log10.error(err);
|
|
5562
5523
|
}
|
|
5563
5524
|
}
|
|
5564
|
-
|
|
5525
|
+
log10.info(`Done. ${String(result.filesGenerated.length)} file${result.filesGenerated.length === 1 ? "" : "s"} updated.`);
|
|
5565
5526
|
}
|
|
5566
5527
|
function buildCurrentNames(speakers, speakerMapping) {
|
|
5567
5528
|
const names = /* @__PURE__ */ new Set();
|
|
@@ -5578,11 +5539,11 @@ function buildCurrentNames(speakers, speakerMapping) {
|
|
|
5578
5539
|
async function run2(args) {
|
|
5579
5540
|
const { outputDir, list, rename, merge, error } = parseArgs(args);
|
|
5580
5541
|
if (error != null) {
|
|
5581
|
-
|
|
5542
|
+
log10.error(error);
|
|
5582
5543
|
return;
|
|
5583
5544
|
}
|
|
5584
5545
|
if (outputDir == null || outputDir.trim() === "") {
|
|
5585
|
-
|
|
5546
|
+
log10.error('Usage: vidistill rename-speakers <output-dir> [--list] [--rename "old" "new"] [--merge "source" "target"]');
|
|
5586
5547
|
return;
|
|
5587
5548
|
}
|
|
5588
5549
|
if (list) {
|
|
@@ -5600,22 +5561,22 @@ async function run2(args) {
|
|
|
5600
5561
|
const metadataPath = join5(outputDir, "metadata.json");
|
|
5601
5562
|
const metadata = await readJsonFile(metadataPath);
|
|
5602
5563
|
if (metadata == null) {
|
|
5603
|
-
|
|
5564
|
+
log10.error("Not a vidistill output directory");
|
|
5604
5565
|
return;
|
|
5605
5566
|
}
|
|
5606
5567
|
const rawDir = join5(outputDir, "raw");
|
|
5607
5568
|
const peopleExtraction = await readJsonFile(join5(rawDir, "pass3b-people.json"));
|
|
5608
5569
|
if (peopleExtraction == null) {
|
|
5609
|
-
|
|
5570
|
+
log10.info("No speakers detected in this video");
|
|
5610
5571
|
return;
|
|
5611
5572
|
}
|
|
5612
5573
|
const speakers = await collectSpeakersFromRaw(rawDir);
|
|
5613
5574
|
if (speakers.length === 0) {
|
|
5614
|
-
|
|
5575
|
+
log10.info("No speakers detected in this video");
|
|
5615
5576
|
return;
|
|
5616
5577
|
}
|
|
5617
5578
|
const existingMapping = metadata.speakerMapping ?? {};
|
|
5618
|
-
|
|
5579
|
+
log10.info(
|
|
5619
5580
|
`${String(speakers.length)} speaker${speakers.length === 1 ? "" : "s"} found. Enter names (or press Enter to keep current).`
|
|
5620
5581
|
);
|
|
5621
5582
|
const groups = groupSpeakersByExistingMapping(speakers, existingMapping);
|
|
@@ -5662,7 +5623,7 @@ async function run2(args) {
|
|
|
5662
5623
|
return;
|
|
5663
5624
|
}
|
|
5664
5625
|
const { mapping: finalMapping, declinedMerges } = mergeResult;
|
|
5665
|
-
|
|
5626
|
+
log10.info("Re-rendering output files with updated speaker names...");
|
|
5666
5627
|
const result = await reRenderWithSpeakerMapping({
|
|
5667
5628
|
outputDir,
|
|
5668
5629
|
speakerMapping: finalMapping,
|
|
@@ -5670,14 +5631,14 @@ async function run2(args) {
|
|
|
5670
5631
|
});
|
|
5671
5632
|
if (result.errors.length > 0) {
|
|
5672
5633
|
for (const err of result.errors) {
|
|
5673
|
-
|
|
5634
|
+
log10.error(err);
|
|
5674
5635
|
}
|
|
5675
5636
|
}
|
|
5676
|
-
|
|
5637
|
+
log10.info(`Done. ${String(result.filesGenerated.length)} file${result.filesGenerated.length === 1 ? "" : "s"} updated.`);
|
|
5677
5638
|
}
|
|
5678
5639
|
|
|
5679
5640
|
// src/cli/index.ts
|
|
5680
|
-
var version = "0.5.
|
|
5641
|
+
var version = "0.5.2";
|
|
5681
5642
|
var DEFAULT_OUTPUT = "./vidistill-output/";
|
|
5682
5643
|
var SUBCOMMANDS = {
|
|
5683
5644
|
mcp: run,
|
|
@@ -5730,11 +5691,11 @@ Commands: ${Object.keys(SUBCOMMANDS).join(", ")}`
|
|
|
5730
5691
|
lang: args.lang
|
|
5731
5692
|
});
|
|
5732
5693
|
} catch (err) {
|
|
5733
|
-
const { log:
|
|
5694
|
+
const { log: log11 } = await import("@clack/prompts");
|
|
5734
5695
|
const { default: pc4 } = await import("picocolors");
|
|
5735
5696
|
const raw = err instanceof Error ? err.message : String(err);
|
|
5736
5697
|
const message = raw.split("\n")[0].slice(0, 200);
|
|
5737
|
-
|
|
5698
|
+
log11.error(pc4.red(message));
|
|
5738
5699
|
process.exit(1);
|
|
5739
5700
|
}
|
|
5740
5701
|
}
|
package/package.json
CHANGED