vidistill 0.5.1 → 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 +188 -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
|
});
|
|
@@ -952,7 +963,7 @@ async function detectDuration(source) {
|
|
|
952
963
|
}
|
|
953
964
|
|
|
954
965
|
// src/core/pipeline.ts
|
|
955
|
-
import { log as
|
|
966
|
+
import { log as log5 } from "@clack/prompts";
|
|
956
967
|
|
|
957
968
|
// src/gemini/schemas.ts
|
|
958
969
|
import { Type } from "@google/genai";
|
|
@@ -1778,9 +1789,6 @@ function mergeTranscriptResults(pass1a, pass1b) {
|
|
|
1778
1789
|
};
|
|
1779
1790
|
}
|
|
1780
1791
|
|
|
1781
|
-
// src/core/transcript-consensus.ts
|
|
1782
|
-
import { log as log5 } from "@clack/prompts";
|
|
1783
|
-
|
|
1784
1792
|
// src/core/consensus.ts
|
|
1785
1793
|
function tokenize(content) {
|
|
1786
1794
|
const tokens = content.match(/[\p{L}\p{N}_]+/gu) ?? [];
|
|
@@ -2103,9 +2111,7 @@ async function runDiarizationConsensus(params) {
|
|
|
2103
2111
|
try {
|
|
2104
2112
|
const result = await runFn();
|
|
2105
2113
|
successfulRuns.push(result);
|
|
2106
|
-
} catch
|
|
2107
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
2108
|
-
log5.warn(`diarization consensus run ${i + 1}/${runs} failed: ${msg}`);
|
|
2114
|
+
} catch {
|
|
2109
2115
|
}
|
|
2110
2116
|
onProgress?.(i + 1, runs);
|
|
2111
2117
|
}
|
|
@@ -2156,9 +2162,7 @@ async function runTranscriptionConsensus(params) {
|
|
|
2156
2162
|
try {
|
|
2157
2163
|
const result = await runFn();
|
|
2158
2164
|
successfulRuns.push(result);
|
|
2159
|
-
} catch
|
|
2160
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
2161
|
-
log5.warn(`transcription consensus run ${i + 1}/${runs} failed: ${msg}`);
|
|
2165
|
+
} catch {
|
|
2162
2166
|
}
|
|
2163
2167
|
onProgress?.(i + 1, runs);
|
|
2164
2168
|
}
|
|
@@ -3003,7 +3007,7 @@ async function runPipeline(config) {
|
|
|
3003
3007
|
"pass0"
|
|
3004
3008
|
);
|
|
3005
3009
|
if (pass0Attempt.error !== null) {
|
|
3006
|
-
|
|
3010
|
+
log5.warn(pass0Attempt.error);
|
|
3007
3011
|
errors.push(pass0Attempt.error);
|
|
3008
3012
|
videoProfile = DEFAULT_PROFILE;
|
|
3009
3013
|
} else {
|
|
@@ -3050,7 +3054,7 @@ async function runPipeline(config) {
|
|
|
3050
3054
|
const pass1aResult = transcriptConsensusResult.result;
|
|
3051
3055
|
if (pass1aResult === null) {
|
|
3052
3056
|
const errMsg = `segment ${i} pass1a: all transcription consensus runs failed`;
|
|
3053
|
-
|
|
3057
|
+
log5.warn(errMsg);
|
|
3054
3058
|
errors.push(errMsg);
|
|
3055
3059
|
}
|
|
3056
3060
|
let pass1 = null;
|
|
@@ -3068,7 +3072,7 @@ async function runPipeline(config) {
|
|
|
3068
3072
|
});
|
|
3069
3073
|
if (pass1bResult === null) {
|
|
3070
3074
|
const errMsg = `segment ${i} pass1b: all diarization consensus runs failed`;
|
|
3071
|
-
|
|
3075
|
+
log5.warn(errMsg);
|
|
3072
3076
|
errors.push(errMsg);
|
|
3073
3077
|
pass1 = mergeTranscriptResults(pass1aResult, { speaker_assignments: [], speaker_summary: [] });
|
|
3074
3078
|
} else {
|
|
@@ -3100,7 +3104,7 @@ async function runPipeline(config) {
|
|
|
3100
3104
|
`segment ${i} pass2`
|
|
3101
3105
|
);
|
|
3102
3106
|
if (pass2Attempt.error !== null) {
|
|
3103
|
-
|
|
3107
|
+
log5.warn(pass2Attempt.error);
|
|
3104
3108
|
errors.push(pass2Attempt.error);
|
|
3105
3109
|
} else {
|
|
3106
3110
|
pass2 = pass2Attempt.result;
|
|
@@ -3133,7 +3137,7 @@ async function runPipeline(config) {
|
|
|
3133
3137
|
});
|
|
3134
3138
|
if (linkConsensusResult.runsCompleted === 0) {
|
|
3135
3139
|
const errMsg = `segment ${i} pass3c: all link consensus runs failed`;
|
|
3136
|
-
|
|
3140
|
+
log5.warn(errMsg);
|
|
3137
3141
|
errors.push(errMsg);
|
|
3138
3142
|
pass3c = null;
|
|
3139
3143
|
} else {
|
|
@@ -3162,7 +3166,7 @@ async function runPipeline(config) {
|
|
|
3162
3166
|
`segment ${i} pass3d`
|
|
3163
3167
|
);
|
|
3164
3168
|
if (pass3dAttempt.error !== null) {
|
|
3165
|
-
|
|
3169
|
+
log5.warn(pass3dAttempt.error);
|
|
3166
3170
|
errors.push(pass3dAttempt.error);
|
|
3167
3171
|
pass3d = null;
|
|
3168
3172
|
} else {
|
|
@@ -3220,7 +3224,7 @@ async function runPipeline(config) {
|
|
|
3220
3224
|
}
|
|
3221
3225
|
} catch (e) {
|
|
3222
3226
|
const msg = e instanceof Error ? e.message : String(e);
|
|
3223
|
-
|
|
3227
|
+
log5.warn(`speaker reconciliation failed, continuing with original labels: ${msg}`);
|
|
3224
3228
|
}
|
|
3225
3229
|
let peopleExtraction = null;
|
|
3226
3230
|
if (strategy.passes.includes("people")) {
|
|
@@ -3241,7 +3245,7 @@ async function runPipeline(config) {
|
|
|
3241
3245
|
"pass3b"
|
|
3242
3246
|
);
|
|
3243
3247
|
if (pass3bAttempt.error !== null) {
|
|
3244
|
-
|
|
3248
|
+
log5.warn(pass3bAttempt.error);
|
|
3245
3249
|
errors.push(pass3bAttempt.error);
|
|
3246
3250
|
} else {
|
|
3247
3251
|
peopleExtraction = pass3bAttempt.result;
|
|
@@ -3279,7 +3283,7 @@ async function runPipeline(config) {
|
|
|
3279
3283
|
});
|
|
3280
3284
|
if (consensusResult.runsCompleted === 0) {
|
|
3281
3285
|
const errMsg = "pass3a: all consensus runs failed";
|
|
3282
|
-
|
|
3286
|
+
log5.warn(errMsg);
|
|
3283
3287
|
errors.push(errMsg);
|
|
3284
3288
|
} else {
|
|
3285
3289
|
const validationResult = validateCodeReconstruction({
|
|
@@ -3318,7 +3322,7 @@ async function runPipeline(config) {
|
|
|
3318
3322
|
"synthesis"
|
|
3319
3323
|
);
|
|
3320
3324
|
if (synthAttempt.error !== null) {
|
|
3321
|
-
|
|
3325
|
+
log5.warn(synthAttempt.error);
|
|
3322
3326
|
errors.push(synthAttempt.error);
|
|
3323
3327
|
} else {
|
|
3324
3328
|
synthesisResult = synthAttempt.result ?? void 0;
|
|
@@ -3389,6 +3393,40 @@ function renderProcessingDetails(pipelineResult, speakerMapping) {
|
|
|
3389
3393
|
lines.push(`- **Segments processed:** ${pipelineResult.segments.length}`);
|
|
3390
3394
|
return lines.join("\n");
|
|
3391
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
|
+
}
|
|
3392
3430
|
function renderIncompletePasses(pipelineResult) {
|
|
3393
3431
|
const { errors, interrupted } = pipelineResult;
|
|
3394
3432
|
const hasErrors = errors.length > 0;
|
|
@@ -3434,7 +3472,7 @@ function writeGuide(params) {
|
|
|
3434
3472
|
"## Suggestions",
|
|
3435
3473
|
"",
|
|
3436
3474
|
renderSuggestions(synthesisResult, speakerMapping),
|
|
3437
|
-
|
|
3475
|
+
renderPrerequisites(synthesisResult?.prerequisites),
|
|
3438
3476
|
"## Processing Details",
|
|
3439
3477
|
"",
|
|
3440
3478
|
renderProcessingDetails(pipelineResult, speakerMapping),
|
|
@@ -3725,13 +3763,79 @@ function renderActionItems(items, speakerMapping) {
|
|
|
3725
3763
|
lines.push("");
|
|
3726
3764
|
return lines;
|
|
3727
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
|
+
}
|
|
3728
3826
|
function hasMeaningfulContent(s) {
|
|
3729
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;
|
|
3730
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
|
+
}
|
|
3731
3834
|
function writeNotes(params) {
|
|
3732
|
-
const { synthesisResult, speakerMapping } = params;
|
|
3835
|
+
const { synthesisResult, segments, speakerMapping } = params;
|
|
3733
3836
|
if (synthesisResult == null) return null;
|
|
3734
|
-
|
|
3837
|
+
const hasPass3d = segments != null && hasPass3dContent(segments);
|
|
3838
|
+
if (!hasMeaningfulContent(synthesisResult) && !hasPass3d) return null;
|
|
3735
3839
|
const sections = ["# Notes", ""];
|
|
3736
3840
|
if (synthesisResult.overview.length > 0) {
|
|
3737
3841
|
sections.push(replaceNamesInText(synthesisResult.overview, speakerMapping));
|
|
@@ -3742,6 +3846,11 @@ function writeNotes(params) {
|
|
|
3742
3846
|
sections.push(...renderTopics(synthesisResult.topics, speakerMapping));
|
|
3743
3847
|
sections.push(...renderQuestions(synthesisResult.questions_raised, speakerMapping));
|
|
3744
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
|
+
}
|
|
3745
3854
|
while (sections[sections.length - 1] === "") sections.pop();
|
|
3746
3855
|
return sections.join("\n");
|
|
3747
3856
|
}
|
|
@@ -4068,150 +4177,6 @@ function writeActionItems(params) {
|
|
|
4068
4177
|
return sections.join("\n");
|
|
4069
4178
|
}
|
|
4070
4179
|
|
|
4071
|
-
// src/output/insights.ts
|
|
4072
|
-
function collectEmotionalShifts(segments) {
|
|
4073
|
-
const shifts = [];
|
|
4074
|
-
for (const seg of segments) {
|
|
4075
|
-
if (seg.pass3d != null) {
|
|
4076
|
-
shifts.push(...seg.pass3d.emotional_shifts);
|
|
4077
|
-
}
|
|
4078
|
-
}
|
|
4079
|
-
return shifts;
|
|
4080
|
-
}
|
|
4081
|
-
function collectEmphasisPatterns(segments) {
|
|
4082
|
-
const patterns = [];
|
|
4083
|
-
for (const seg of segments) {
|
|
4084
|
-
if (seg.pass3d != null) {
|
|
4085
|
-
patterns.push(...seg.pass3d.emphasis_patterns);
|
|
4086
|
-
}
|
|
4087
|
-
}
|
|
4088
|
-
return patterns;
|
|
4089
|
-
}
|
|
4090
|
-
function collectImplicitQuestions(segments) {
|
|
4091
|
-
const questions = [];
|
|
4092
|
-
for (const seg of segments) {
|
|
4093
|
-
if (seg.pass3d != null) {
|
|
4094
|
-
questions.push(...seg.pass3d.questions_implicit);
|
|
4095
|
-
}
|
|
4096
|
-
}
|
|
4097
|
-
return questions;
|
|
4098
|
-
}
|
|
4099
|
-
function collectImplicitDecisions(segments) {
|
|
4100
|
-
const decisions = [];
|
|
4101
|
-
for (const seg of segments) {
|
|
4102
|
-
if (seg.pass3d != null) {
|
|
4103
|
-
decisions.push(...seg.pass3d.decisions_implicit);
|
|
4104
|
-
}
|
|
4105
|
-
}
|
|
4106
|
-
return decisions;
|
|
4107
|
-
}
|
|
4108
|
-
function renderEmotionalShifts(shifts, speakerMapping) {
|
|
4109
|
-
if (shifts.length === 0) return [];
|
|
4110
|
-
const lines = ["## Emotional Shifts", ""];
|
|
4111
|
-
for (const s of shifts) {
|
|
4112
|
-
lines.push(`- **[${s.timestamp}]** ${s.from_state} \u2192 ${s.to_state}`);
|
|
4113
|
-
if (s.trigger.length > 0) {
|
|
4114
|
-
lines.push(` _Trigger: ${replaceNamesInText(s.trigger, speakerMapping)}_`);
|
|
4115
|
-
}
|
|
4116
|
-
}
|
|
4117
|
-
lines.push("");
|
|
4118
|
-
return lines;
|
|
4119
|
-
}
|
|
4120
|
-
function renderEmphasisPatterns(patterns, speakerMapping) {
|
|
4121
|
-
if (patterns.length === 0) return [];
|
|
4122
|
-
const sorted = [...patterns].sort((a, b) => b.times_mentioned - a.times_mentioned);
|
|
4123
|
-
const lines = ["## Emphasis Patterns", ""];
|
|
4124
|
-
for (const p of sorted) {
|
|
4125
|
-
const ts = p.timestamps.length > 0 ? ` _(${p.timestamps.join(", ")})_` : "";
|
|
4126
|
-
lines.push(`### ${p.concept} (\xD7${p.times_mentioned})${ts}`);
|
|
4127
|
-
lines.push("");
|
|
4128
|
-
if (p.significance.length > 0) {
|
|
4129
|
-
lines.push(replaceNamesInText(p.significance, speakerMapping));
|
|
4130
|
-
lines.push("");
|
|
4131
|
-
}
|
|
4132
|
-
}
|
|
4133
|
-
return lines;
|
|
4134
|
-
}
|
|
4135
|
-
function renderImplicitQuestions(questions, speakerMapping) {
|
|
4136
|
-
if (questions.length === 0) return [];
|
|
4137
|
-
const lines = ["## Implicit Questions", ""];
|
|
4138
|
-
for (const q of questions) {
|
|
4139
|
-
lines.push(`- ${replaceNamesInText(q, speakerMapping)}`);
|
|
4140
|
-
}
|
|
4141
|
-
lines.push("");
|
|
4142
|
-
return lines;
|
|
4143
|
-
}
|
|
4144
|
-
function renderImplicitDecisions(decisions, speakerMapping) {
|
|
4145
|
-
if (decisions.length === 0) return [];
|
|
4146
|
-
const lines = ["## Implicit Decisions", ""];
|
|
4147
|
-
for (const d of decisions) {
|
|
4148
|
-
lines.push(`- ${replaceNamesInText(d, speakerMapping)}`);
|
|
4149
|
-
}
|
|
4150
|
-
lines.push("");
|
|
4151
|
-
return lines;
|
|
4152
|
-
}
|
|
4153
|
-
function writeInsights(params) {
|
|
4154
|
-
const { segments, speakerMapping } = params;
|
|
4155
|
-
const hasPass3d = segments.some((s) => s.pass3d != null);
|
|
4156
|
-
if (!hasPass3d) return null;
|
|
4157
|
-
const emotionalShifts = collectEmotionalShifts(segments);
|
|
4158
|
-
const emphasisPatterns = collectEmphasisPatterns(segments);
|
|
4159
|
-
const implicitQuestions = collectImplicitQuestions(segments);
|
|
4160
|
-
const implicitDecisions = collectImplicitDecisions(segments);
|
|
4161
|
-
if (emotionalShifts.length === 0 && emphasisPatterns.length === 0 && implicitQuestions.length === 0 && implicitDecisions.length === 0) {
|
|
4162
|
-
return null;
|
|
4163
|
-
}
|
|
4164
|
-
const sections = ["# Insights", ""];
|
|
4165
|
-
sections.push(...renderEmotionalShifts(emotionalShifts, speakerMapping));
|
|
4166
|
-
sections.push(...renderEmphasisPatterns(emphasisPatterns, speakerMapping));
|
|
4167
|
-
sections.push(...renderImplicitQuestions(implicitQuestions, speakerMapping));
|
|
4168
|
-
sections.push(...renderImplicitDecisions(implicitDecisions, speakerMapping));
|
|
4169
|
-
while (sections[sections.length - 1] === "") sections.pop();
|
|
4170
|
-
return sections.join("\n");
|
|
4171
|
-
}
|
|
4172
|
-
|
|
4173
|
-
// src/output/prereqs.ts
|
|
4174
|
-
var LEVEL_ORDER = ["advanced", "intermediate", "basic"];
|
|
4175
|
-
var LEVEL_LABELS = {
|
|
4176
|
-
advanced: "Advanced",
|
|
4177
|
-
intermediate: "Intermediate",
|
|
4178
|
-
basic: "Basic"
|
|
4179
|
-
};
|
|
4180
|
-
function renderLevelSection(level, concepts) {
|
|
4181
|
-
if (concepts.length === 0) return [];
|
|
4182
|
-
const lines = [`## ${LEVEL_LABELS[level]} Knowledge`, ""];
|
|
4183
|
-
for (const c of concepts) {
|
|
4184
|
-
lines.push(`### ${c.concept}`);
|
|
4185
|
-
lines.push("");
|
|
4186
|
-
lines.push(c.brief_explanation);
|
|
4187
|
-
lines.push("");
|
|
4188
|
-
lines.push(`_First assumed at: ${c.timestamp_first_assumed}_`);
|
|
4189
|
-
lines.push("");
|
|
4190
|
-
}
|
|
4191
|
-
return lines;
|
|
4192
|
-
}
|
|
4193
|
-
function writePrereqs(params) {
|
|
4194
|
-
const { prerequisites } = params;
|
|
4195
|
-
if (prerequisites == null || prerequisites.length === 0) return null;
|
|
4196
|
-
const grouped = /* @__PURE__ */ new Map();
|
|
4197
|
-
for (const level of LEVEL_ORDER) {
|
|
4198
|
-
grouped.set(level, []);
|
|
4199
|
-
}
|
|
4200
|
-
for (const c of prerequisites) {
|
|
4201
|
-
const bucket = grouped.get(c.assumed_knowledge_level);
|
|
4202
|
-
if (bucket != null) {
|
|
4203
|
-
bucket.push(c);
|
|
4204
|
-
}
|
|
4205
|
-
}
|
|
4206
|
-
const sections = ["# Prerequisites", ""];
|
|
4207
|
-
for (const level of LEVEL_ORDER) {
|
|
4208
|
-
const concepts = grouped.get(level) ?? [];
|
|
4209
|
-
sections.push(...renderLevelSection(level, concepts));
|
|
4210
|
-
}
|
|
4211
|
-
while (sections[sections.length - 1] === "") sections.pop();
|
|
4212
|
-
return sections.join("\n");
|
|
4213
|
-
}
|
|
4214
|
-
|
|
4215
4180
|
// src/output/timeline.ts
|
|
4216
4181
|
function toPercent(seconds, duration) {
|
|
4217
4182
|
if (duration <= 0) return "0";
|
|
@@ -4701,13 +4666,9 @@ function resolveFilesToGenerate(params) {
|
|
|
4701
4666
|
}
|
|
4702
4667
|
if (hasPass3d) {
|
|
4703
4668
|
optional.add("action-items.md");
|
|
4704
|
-
optional.add("insights.md");
|
|
4705
4669
|
}
|
|
4706
|
-
if (synthesisResult != null) optional.add("notes.md");
|
|
4670
|
+
if (synthesisResult != null || hasPass3d) optional.add("notes.md");
|
|
4707
4671
|
if (peopleExtraction != null) optional.add("people.md");
|
|
4708
|
-
if (synthesisResult?.prerequisites != null && Array.isArray(synthesisResult.prerequisites) && synthesisResult.prerequisites.length > 0) {
|
|
4709
|
-
optional.add("prereqs.md");
|
|
4710
|
-
}
|
|
4711
4672
|
const hasPass1 = segments.some((s) => s.pass1 != null);
|
|
4712
4673
|
if (hasPass1 || hasPass2) optional.add("timeline.html");
|
|
4713
4674
|
return optional;
|
|
@@ -4766,7 +4727,7 @@ async function generateOutput(params) {
|
|
|
4766
4727
|
}
|
|
4767
4728
|
if (filesToGenerate.has("notes.md")) {
|
|
4768
4729
|
try {
|
|
4769
|
-
const content = writeNotes({ synthesisResult: pipelineResult.synthesisResult, speakerMapping: expandedMapping });
|
|
4730
|
+
const content = writeNotes({ synthesisResult: pipelineResult.synthesisResult, segments: pipelineResult.segments, speakerMapping: expandedMapping });
|
|
4770
4731
|
if (content != null) {
|
|
4771
4732
|
await writeOutputFile("notes.md", content);
|
|
4772
4733
|
}
|
|
@@ -4818,26 +4779,6 @@ async function generateOutput(params) {
|
|
|
4818
4779
|
errors.push(`action-items.md: ${String(err)}`);
|
|
4819
4780
|
}
|
|
4820
4781
|
}
|
|
4821
|
-
if (filesToGenerate.has("insights.md")) {
|
|
4822
|
-
try {
|
|
4823
|
-
const content = writeInsights({ segments: pipelineResult.segments, speakerMapping: expandedMapping });
|
|
4824
|
-
if (content != null) {
|
|
4825
|
-
await writeOutputFile("insights.md", content);
|
|
4826
|
-
}
|
|
4827
|
-
} catch (err) {
|
|
4828
|
-
errors.push(`insights.md: ${String(err)}`);
|
|
4829
|
-
}
|
|
4830
|
-
}
|
|
4831
|
-
if (filesToGenerate.has("prereqs.md")) {
|
|
4832
|
-
try {
|
|
4833
|
-
const content = writePrereqs({ prerequisites: pipelineResult.synthesisResult?.prerequisites });
|
|
4834
|
-
if (content != null) {
|
|
4835
|
-
await writeOutputFile("prereqs.md", content);
|
|
4836
|
-
}
|
|
4837
|
-
} catch (err) {
|
|
4838
|
-
errors.push(`prereqs.md: ${String(err)}`);
|
|
4839
|
-
}
|
|
4840
|
-
}
|
|
4841
4782
|
if (filesToGenerate.has("timeline.html")) {
|
|
4842
4783
|
try {
|
|
4843
4784
|
const content = generateTimeline({ pipelineResult, duration, speakerMapping: expandedMapping });
|
|
@@ -4954,7 +4895,7 @@ async function reRenderWithSpeakerMapping(params) {
|
|
|
4954
4895
|
}
|
|
4955
4896
|
if (filesToReRender.has("notes.md")) {
|
|
4956
4897
|
try {
|
|
4957
|
-
const content = writeNotes({ synthesisResult: pipelineResult.synthesisResult, speakerMapping: expandedMapping });
|
|
4898
|
+
const content = writeNotes({ synthesisResult: pipelineResult.synthesisResult, segments: pipelineResult.segments, speakerMapping: expandedMapping });
|
|
4958
4899
|
if (content != null) await writeOutputFile("notes.md", content);
|
|
4959
4900
|
} catch (err) {
|
|
4960
4901
|
errors.push(`notes.md: ${String(err)}`);
|
|
@@ -4988,14 +4929,6 @@ async function reRenderWithSpeakerMapping(params) {
|
|
|
4988
4929
|
errors.push(`action-items.md: ${String(err)}`);
|
|
4989
4930
|
}
|
|
4990
4931
|
}
|
|
4991
|
-
if (filesToReRender.has("insights.md")) {
|
|
4992
|
-
try {
|
|
4993
|
-
const content = writeInsights({ segments: pipelineResult.segments, speakerMapping: expandedMapping });
|
|
4994
|
-
if (content != null) await writeOutputFile("insights.md", content);
|
|
4995
|
-
} catch (err) {
|
|
4996
|
-
errors.push(`insights.md: ${String(err)}`);
|
|
4997
|
-
}
|
|
4998
|
-
}
|
|
4999
4932
|
if (filesToReRender.has("timeline.html")) {
|
|
5000
4933
|
try {
|
|
5001
4934
|
const content = generateTimeline({ pipelineResult, duration, speakerMapping: expandedMapping });
|
|
@@ -5036,7 +4969,7 @@ async function reRenderWithSpeakerMapping(params) {
|
|
|
5036
4969
|
}
|
|
5037
4970
|
|
|
5038
4971
|
// src/core/shutdown.ts
|
|
5039
|
-
import { log as
|
|
4972
|
+
import { log as log6 } from "@clack/prompts";
|
|
5040
4973
|
function createShutdownHandler(params) {
|
|
5041
4974
|
const { client, uploadedFileNames } = params;
|
|
5042
4975
|
let shuttingDown = false;
|
|
@@ -5052,10 +4985,10 @@ function createShutdownHandler(params) {
|
|
|
5052
4985
|
}
|
|
5053
4986
|
shuttingDown = true;
|
|
5054
4987
|
if (hasProgress) {
|
|
5055
|
-
|
|
5056
|
-
|
|
4988
|
+
log6.warn(`Interrupted \u2014 progress saved (${progressCurrentStep}/${progressTotalSteps} steps)`);
|
|
4989
|
+
log6.info(`Resume: vidistill ${params.source} -o ${params.outputDir}/`);
|
|
5057
4990
|
} else {
|
|
5058
|
-
|
|
4991
|
+
log6.warn("Interrupted");
|
|
5059
4992
|
}
|
|
5060
4993
|
const forceExitHandler = () => {
|
|
5061
4994
|
process.exit(1);
|
|
@@ -5140,6 +5073,7 @@ async function runDistill(args) {
|
|
|
5140
5073
|
const apiKey = await resolveApiKey();
|
|
5141
5074
|
let rawInput = args.input ?? await promptVideoSource();
|
|
5142
5075
|
let context = args.context ?? await promptContext();
|
|
5076
|
+
let outputName = await promptOutputName();
|
|
5143
5077
|
const allFlagsProvided = args.input != null && args.context != null;
|
|
5144
5078
|
if (!allFlagsProvided) {
|
|
5145
5079
|
let confirmed = false;
|
|
@@ -5150,6 +5084,7 @@ async function runDistill(args) {
|
|
|
5150
5084
|
input: rawInput,
|
|
5151
5085
|
context,
|
|
5152
5086
|
output: args.output,
|
|
5087
|
+
outputName,
|
|
5153
5088
|
videoType: inputIsAudio ? "audio" : void 0,
|
|
5154
5089
|
lang: args.lang
|
|
5155
5090
|
});
|
|
@@ -5164,6 +5099,9 @@ async function runDistill(args) {
|
|
|
5164
5099
|
case "edit-context":
|
|
5165
5100
|
context = await promptContext();
|
|
5166
5101
|
break;
|
|
5102
|
+
case "edit-name":
|
|
5103
|
+
outputName = await promptOutputName();
|
|
5104
|
+
break;
|
|
5167
5105
|
case "cancel":
|
|
5168
5106
|
cancel2("Cancelled.");
|
|
5169
5107
|
process.exit(0);
|
|
@@ -5188,7 +5126,7 @@ async function runDistill(args) {
|
|
|
5188
5126
|
});
|
|
5189
5127
|
} catch {
|
|
5190
5128
|
duration = 600;
|
|
5191
|
-
|
|
5129
|
+
log7.warn("Could not detect video duration \u2014 defaulting to 10 minutes. Install yt-dlp for full-length processing: brew install yt-dlp");
|
|
5192
5130
|
}
|
|
5193
5131
|
if (result.uploadedFileName != null) {
|
|
5194
5132
|
uploadedFileNames = [result.uploadedFileName];
|
|
@@ -5208,6 +5146,9 @@ async function runDistill(args) {
|
|
|
5208
5146
|
}
|
|
5209
5147
|
videoTitle = basename3(resolved.value, extname2(resolved.value));
|
|
5210
5148
|
}
|
|
5149
|
+
if (outputName != null) {
|
|
5150
|
+
videoTitle = outputName;
|
|
5151
|
+
}
|
|
5211
5152
|
const model = MODELS.flash;
|
|
5212
5153
|
const outputDir = resolve(args.output);
|
|
5213
5154
|
const slug = slugify(videoTitle);
|
|
@@ -5265,32 +5206,32 @@ async function runDistill(args) {
|
|
|
5265
5206
|
const elapsedMins = Math.floor(elapsedSecs / 60);
|
|
5266
5207
|
const remainSecs = elapsedSecs % 60;
|
|
5267
5208
|
const elapsed = elapsedMins > 0 ? `${elapsedMins}m ${remainSecs}s` : `${remainSecs}s`;
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5209
|
+
log7.success(`Done in ${elapsed}`);
|
|
5210
|
+
log7.info(`Output: ${finalOutputDir}/`);
|
|
5211
|
+
log7.info(pc3.dim("Open guide.md for an overview"));
|
|
5271
5212
|
if (pipelineResult.peopleExtraction?.participants != null && pipelineResult.peopleExtraction.participants.length > 1) {
|
|
5272
|
-
|
|
5213
|
+
log7.info(pc3.dim("Tip: vidistill rename-speakers <dir> to assign real names"));
|
|
5273
5214
|
}
|
|
5274
5215
|
if (outputResult.errors.length > 0) {
|
|
5275
|
-
|
|
5216
|
+
log7.warn(`Output errors: ${pc3.yellow(String(outputResult.errors.length))}`);
|
|
5276
5217
|
for (const err of outputResult.errors) {
|
|
5277
|
-
|
|
5218
|
+
log7.warn(pc3.dim(` ${err}`));
|
|
5278
5219
|
}
|
|
5279
5220
|
}
|
|
5280
5221
|
}
|
|
5281
5222
|
|
|
5282
5223
|
// src/commands/mcp.ts
|
|
5283
|
-
import { log as
|
|
5224
|
+
import { log as log8 } from "@clack/prompts";
|
|
5284
5225
|
async function run(_args) {
|
|
5285
|
-
|
|
5226
|
+
log8.info("Not implemented yet.");
|
|
5286
5227
|
}
|
|
5287
5228
|
|
|
5288
5229
|
// src/commands/rename-speakers.ts
|
|
5289
5230
|
import { join as join5 } from "path";
|
|
5290
|
-
import { log as
|
|
5231
|
+
import { log as log10, text as text3, isCancel as isCancel3, cancel as cancel4 } from "@clack/prompts";
|
|
5291
5232
|
|
|
5292
5233
|
// src/cli/speaker-naming.ts
|
|
5293
|
-
import { log as
|
|
5234
|
+
import { log as log9, text as text2, confirm as confirm2, isCancel as isCancel2, cancel as cancel3 } from "@clack/prompts";
|
|
5294
5235
|
async function detectAndPromptMerges(mapping) {
|
|
5295
5236
|
const byName = /* @__PURE__ */ new Map();
|
|
5296
5237
|
for (const [label, name] of Object.entries(mapping)) {
|
|
@@ -5449,19 +5390,19 @@ async function runList(outputDir) {
|
|
|
5449
5390
|
const metadataPath = join5(outputDir, "metadata.json");
|
|
5450
5391
|
const metadata = await readJsonFile(metadataPath);
|
|
5451
5392
|
if (metadata == null) {
|
|
5452
|
-
|
|
5393
|
+
log10.error("Not a vidistill output directory");
|
|
5453
5394
|
return;
|
|
5454
5395
|
}
|
|
5455
5396
|
const rawDir = join5(outputDir, "raw");
|
|
5456
5397
|
const speakers = await collectSpeakersFromRaw(rawDir);
|
|
5457
5398
|
const speakerMapping = metadata.speakerMapping ?? {};
|
|
5458
5399
|
if (speakers.length === 0 && Object.keys(speakerMapping).length === 0) {
|
|
5459
|
-
|
|
5400
|
+
log10.info("No speakers found.");
|
|
5460
5401
|
return;
|
|
5461
5402
|
}
|
|
5462
5403
|
const groups = groupSpeakersByExistingMapping(speakers, speakerMapping);
|
|
5463
5404
|
if (groups.length === 0) {
|
|
5464
|
-
|
|
5405
|
+
log10.info("No speakers found.");
|
|
5465
5406
|
return;
|
|
5466
5407
|
}
|
|
5467
5408
|
const lines = groups.map((group, idx) => {
|
|
@@ -5470,17 +5411,17 @@ async function runList(outputDir) {
|
|
|
5470
5411
|
const labelsStr = group.labels.join(", ");
|
|
5471
5412
|
return `${String(num)}. ${displayName} (${labelsStr}, ${String(group.totalEntries)} entries)`;
|
|
5472
5413
|
});
|
|
5473
|
-
|
|
5414
|
+
log10.info(lines.join("\n"));
|
|
5474
5415
|
}
|
|
5475
5416
|
async function runRename(outputDir, oldName, newName) {
|
|
5476
5417
|
if (newName.trim().length === 0) {
|
|
5477
|
-
|
|
5418
|
+
log10.error("New name cannot be empty. Use the interactive prompt to clear a mapping.");
|
|
5478
5419
|
return;
|
|
5479
5420
|
}
|
|
5480
5421
|
const metadataPath = join5(outputDir, "metadata.json");
|
|
5481
5422
|
const metadata = await readJsonFile(metadataPath);
|
|
5482
5423
|
if (metadata == null) {
|
|
5483
|
-
|
|
5424
|
+
log10.error("Not a vidistill output directory");
|
|
5484
5425
|
return;
|
|
5485
5426
|
}
|
|
5486
5427
|
const speakerMapping = { ...metadata.speakerMapping ?? {} };
|
|
@@ -5501,18 +5442,18 @@ async function runRename(outputDir, oldName, newName) {
|
|
|
5501
5442
|
const currentNames = Object.values(speakerMapping);
|
|
5502
5443
|
const unmappedLabels = speakers.filter((s) => speakerMapping[s.label] == null).map((s) => s.label);
|
|
5503
5444
|
const allNames = [.../* @__PURE__ */ new Set([...currentNames, ...unmappedLabels])];
|
|
5504
|
-
|
|
5445
|
+
log10.error(`No speaker named "${oldName}" found. Current speakers: ${formatNameList(allNames)}`);
|
|
5505
5446
|
return;
|
|
5506
5447
|
}
|
|
5507
5448
|
if (matchingKeys.length > 1) {
|
|
5508
|
-
|
|
5449
|
+
log10.error(
|
|
5509
5450
|
`Multiple speakers named "${oldName}" (${matchingKeys.join(", ")}). Use SPEAKER_XX label to specify which one.`
|
|
5510
5451
|
);
|
|
5511
5452
|
return;
|
|
5512
5453
|
}
|
|
5513
5454
|
const key = matchingKeys[0];
|
|
5514
5455
|
speakerMapping[key] = newName;
|
|
5515
|
-
|
|
5456
|
+
log10.info("Re-rendering output files with updated speaker names...");
|
|
5516
5457
|
const result = await reRenderWithSpeakerMapping({
|
|
5517
5458
|
outputDir,
|
|
5518
5459
|
speakerMapping,
|
|
@@ -5520,16 +5461,16 @@ async function runRename(outputDir, oldName, newName) {
|
|
|
5520
5461
|
});
|
|
5521
5462
|
if (result.errors.length > 0) {
|
|
5522
5463
|
for (const err of result.errors) {
|
|
5523
|
-
|
|
5464
|
+
log10.error(err);
|
|
5524
5465
|
}
|
|
5525
5466
|
}
|
|
5526
|
-
|
|
5467
|
+
log10.info(`Done. ${String(result.filesGenerated.length)} file${result.filesGenerated.length === 1 ? "" : "s"} updated.`);
|
|
5527
5468
|
}
|
|
5528
5469
|
async function runMerge(outputDir, sourceName, targetName) {
|
|
5529
5470
|
const metadataPath = join5(outputDir, "metadata.json");
|
|
5530
5471
|
const metadata = await readJsonFile(metadataPath);
|
|
5531
5472
|
if (metadata == null) {
|
|
5532
|
-
|
|
5473
|
+
log10.error("Not a vidistill output directory");
|
|
5533
5474
|
return;
|
|
5534
5475
|
}
|
|
5535
5476
|
const speakerMapping = { ...metadata.speakerMapping ?? {} };
|
|
@@ -5558,19 +5499,19 @@ async function runMerge(outputDir, sourceName, targetName) {
|
|
|
5558
5499
|
const targetKeys = findKeys(targetName);
|
|
5559
5500
|
if (sourceKeys.length === 0) {
|
|
5560
5501
|
const currentNames = buildCurrentNames(speakers, speakerMapping);
|
|
5561
|
-
|
|
5502
|
+
log10.error(`No speaker named "${sourceName}" found. Current speakers: ${formatNameList(currentNames)}`);
|
|
5562
5503
|
return;
|
|
5563
5504
|
}
|
|
5564
5505
|
if (targetKeys.length === 0) {
|
|
5565
5506
|
const currentNames = buildCurrentNames(speakers, speakerMapping);
|
|
5566
|
-
|
|
5507
|
+
log10.error(`No speaker named "${targetName}" found. Current speakers: ${formatNameList(currentNames)}`);
|
|
5567
5508
|
return;
|
|
5568
5509
|
}
|
|
5569
5510
|
const resolvedTargetName = speakerMapping[targetKeys[0]] ?? targetName;
|
|
5570
5511
|
for (const key of sourceKeys) {
|
|
5571
5512
|
speakerMapping[key] = resolvedTargetName;
|
|
5572
5513
|
}
|
|
5573
|
-
|
|
5514
|
+
log10.info("Re-rendering output files with updated speaker names...");
|
|
5574
5515
|
const result = await reRenderWithSpeakerMapping({
|
|
5575
5516
|
outputDir,
|
|
5576
5517
|
speakerMapping,
|
|
@@ -5578,10 +5519,10 @@ async function runMerge(outputDir, sourceName, targetName) {
|
|
|
5578
5519
|
});
|
|
5579
5520
|
if (result.errors.length > 0) {
|
|
5580
5521
|
for (const err of result.errors) {
|
|
5581
|
-
|
|
5522
|
+
log10.error(err);
|
|
5582
5523
|
}
|
|
5583
5524
|
}
|
|
5584
|
-
|
|
5525
|
+
log10.info(`Done. ${String(result.filesGenerated.length)} file${result.filesGenerated.length === 1 ? "" : "s"} updated.`);
|
|
5585
5526
|
}
|
|
5586
5527
|
function buildCurrentNames(speakers, speakerMapping) {
|
|
5587
5528
|
const names = /* @__PURE__ */ new Set();
|
|
@@ -5598,11 +5539,11 @@ function buildCurrentNames(speakers, speakerMapping) {
|
|
|
5598
5539
|
async function run2(args) {
|
|
5599
5540
|
const { outputDir, list, rename, merge, error } = parseArgs(args);
|
|
5600
5541
|
if (error != null) {
|
|
5601
|
-
|
|
5542
|
+
log10.error(error);
|
|
5602
5543
|
return;
|
|
5603
5544
|
}
|
|
5604
5545
|
if (outputDir == null || outputDir.trim() === "") {
|
|
5605
|
-
|
|
5546
|
+
log10.error('Usage: vidistill rename-speakers <output-dir> [--list] [--rename "old" "new"] [--merge "source" "target"]');
|
|
5606
5547
|
return;
|
|
5607
5548
|
}
|
|
5608
5549
|
if (list) {
|
|
@@ -5620,22 +5561,22 @@ async function run2(args) {
|
|
|
5620
5561
|
const metadataPath = join5(outputDir, "metadata.json");
|
|
5621
5562
|
const metadata = await readJsonFile(metadataPath);
|
|
5622
5563
|
if (metadata == null) {
|
|
5623
|
-
|
|
5564
|
+
log10.error("Not a vidistill output directory");
|
|
5624
5565
|
return;
|
|
5625
5566
|
}
|
|
5626
5567
|
const rawDir = join5(outputDir, "raw");
|
|
5627
5568
|
const peopleExtraction = await readJsonFile(join5(rawDir, "pass3b-people.json"));
|
|
5628
5569
|
if (peopleExtraction == null) {
|
|
5629
|
-
|
|
5570
|
+
log10.info("No speakers detected in this video");
|
|
5630
5571
|
return;
|
|
5631
5572
|
}
|
|
5632
5573
|
const speakers = await collectSpeakersFromRaw(rawDir);
|
|
5633
5574
|
if (speakers.length === 0) {
|
|
5634
|
-
|
|
5575
|
+
log10.info("No speakers detected in this video");
|
|
5635
5576
|
return;
|
|
5636
5577
|
}
|
|
5637
5578
|
const existingMapping = metadata.speakerMapping ?? {};
|
|
5638
|
-
|
|
5579
|
+
log10.info(
|
|
5639
5580
|
`${String(speakers.length)} speaker${speakers.length === 1 ? "" : "s"} found. Enter names (or press Enter to keep current).`
|
|
5640
5581
|
);
|
|
5641
5582
|
const groups = groupSpeakersByExistingMapping(speakers, existingMapping);
|
|
@@ -5682,7 +5623,7 @@ async function run2(args) {
|
|
|
5682
5623
|
return;
|
|
5683
5624
|
}
|
|
5684
5625
|
const { mapping: finalMapping, declinedMerges } = mergeResult;
|
|
5685
|
-
|
|
5626
|
+
log10.info("Re-rendering output files with updated speaker names...");
|
|
5686
5627
|
const result = await reRenderWithSpeakerMapping({
|
|
5687
5628
|
outputDir,
|
|
5688
5629
|
speakerMapping: finalMapping,
|
|
@@ -5690,14 +5631,14 @@ async function run2(args) {
|
|
|
5690
5631
|
});
|
|
5691
5632
|
if (result.errors.length > 0) {
|
|
5692
5633
|
for (const err of result.errors) {
|
|
5693
|
-
|
|
5634
|
+
log10.error(err);
|
|
5694
5635
|
}
|
|
5695
5636
|
}
|
|
5696
|
-
|
|
5637
|
+
log10.info(`Done. ${String(result.filesGenerated.length)} file${result.filesGenerated.length === 1 ? "" : "s"} updated.`);
|
|
5697
5638
|
}
|
|
5698
5639
|
|
|
5699
5640
|
// src/cli/index.ts
|
|
5700
|
-
var version = "0.5.
|
|
5641
|
+
var version = "0.5.2";
|
|
5701
5642
|
var DEFAULT_OUTPUT = "./vidistill-output/";
|
|
5702
5643
|
var SUBCOMMANDS = {
|
|
5703
5644
|
mcp: run,
|
|
@@ -5750,11 +5691,11 @@ Commands: ${Object.keys(SUBCOMMANDS).join(", ")}`
|
|
|
5750
5691
|
lang: args.lang
|
|
5751
5692
|
});
|
|
5752
5693
|
} catch (err) {
|
|
5753
|
-
const { log:
|
|
5694
|
+
const { log: log11 } = await import("@clack/prompts");
|
|
5754
5695
|
const { default: pc4 } = await import("picocolors");
|
|
5755
5696
|
const raw = err instanceof Error ? err.message : String(err);
|
|
5756
5697
|
const message = raw.split("\n")[0].slice(0, 200);
|
|
5757
|
-
|
|
5698
|
+
log11.error(pc4.red(message));
|
|
5758
5699
|
process.exit(1);
|
|
5759
5700
|
}
|
|
5760
5701
|
}
|
package/package.json
CHANGED