vidistill 0.2.4 → 0.2.5

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.
Files changed (2) hide show
  1. package/dist/index.js +374 -27
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,9 +1,4 @@
1
1
  #!/usr/bin/env node
2
- var __glob = (map) => (path) => {
3
- var fn = map[path];
4
- if (fn) return fn();
5
- throw new Error("Module not found in bundle: " + path);
6
- };
7
2
 
8
3
  // src/cli/index.ts
9
4
  import { createRequire as createRequire2 } from "module";
@@ -878,7 +873,7 @@ var _require = createRequire(import.meta.url);
878
873
  var ffmpeg = _require("fluent-ffmpeg");
879
874
  var BYTES_PER_SECOND = 5e5;
880
875
  function ffprobeAsync(filePath) {
881
- return new Promise((resolve2, reject) => {
876
+ return new Promise((resolve3, reject) => {
882
877
  ffmpeg.ffprobe(filePath, (err, data) => {
883
878
  if (err) {
884
879
  reject(err);
@@ -889,7 +884,7 @@ function ffprobeAsync(filePath) {
889
884
  reject(new Error("ffprobe returned no duration"));
890
885
  return;
891
886
  }
892
- resolve2(duration);
887
+ resolve3(duration);
893
888
  });
894
889
  });
895
890
  }
@@ -917,8 +912,8 @@ async function detectDuration(source) {
917
912
  let bytes;
918
913
  if (source.filePath !== void 0) {
919
914
  try {
920
- const { statSync: statSync2 } = await import("fs");
921
- bytes = statSync2(source.filePath).size;
915
+ const { statSync: statSync3 } = await import("fs");
916
+ bytes = statSync3(source.filePath).size;
922
917
  } catch {
923
918
  }
924
919
  }
@@ -2175,10 +2170,10 @@ async function runCodeConsensus(params) {
2175
2170
  };
2176
2171
  }
2177
2172
  const voteMap = /* @__PURE__ */ new Map();
2178
- for (const run of successfulRuns) {
2179
- if (run.files.length === 0) continue;
2173
+ for (const run7 of successfulRuns) {
2174
+ if (run7.files.length === 0) continue;
2180
2175
  const seenInRun = /* @__PURE__ */ new Set();
2181
- for (const file of run.files) {
2176
+ for (const file of run7.files) {
2182
2177
  const normalized = normalizeFilename(file.filename);
2183
2178
  if (seenInRun.has(normalized)) continue;
2184
2179
  seenInRun.add(normalized);
@@ -2586,10 +2581,10 @@ async function runPipeline(config) {
2586
2581
  { onWait }
2587
2582
  ),
2588
2583
  pass2Results,
2589
- onProgress: (run, total) => {
2590
- onProgress?.({ phase: "pass3a", segment: run - 1, totalSegments: total, status: "running", totalSteps });
2584
+ onProgress: (run7, total) => {
2585
+ onProgress?.({ phase: "pass3a", segment: run7 - 1, totalSegments: total, status: "running", totalSteps });
2591
2586
  currentStep++;
2592
- onProgress?.({ phase: "pass3a", segment: run - 1, totalSegments: total, status: "done", currentStep, totalSteps });
2587
+ onProgress?.({ phase: "pass3a", segment: run7 - 1, totalSegments: total, status: "done", currentStep, totalSteps });
2593
2588
  }
2594
2589
  });
2595
2590
  if (consensusResult.runsCompleted === 0) {
@@ -3852,19 +3847,375 @@ async function runDistill(args) {
3852
3847
  }
3853
3848
  }
3854
3849
 
3855
- // import("../commands/**/*.js") in src/cli/index.ts
3856
- var globImport_commands_js = __glob({});
3850
+ // src/commands/extract.ts
3851
+ import { log as log9 } from "@clack/prompts";
3852
+ import { existsSync as existsSync4, statSync as statSync2, readdirSync, readFileSync } from "fs";
3853
+ import { resolve as resolve2, basename as basename4 } from "path";
3854
+ var VALID_TYPES = ["code", "links", "people", "transcript", "commands"];
3855
+ function isValidType(t) {
3856
+ return VALID_TYPES.includes(t);
3857
+ }
3858
+ function readJson(filePath) {
3859
+ try {
3860
+ const parsed = JSON.parse(readFileSync(filePath, "utf8"));
3861
+ if (parsed == null || typeof parsed !== "object") return null;
3862
+ return parsed;
3863
+ } catch {
3864
+ return null;
3865
+ }
3866
+ }
3867
+ function listMatchingFiles(dir, pattern) {
3868
+ try {
3869
+ return readdirSync(dir).filter((f) => pattern.test(f)).sort((a, b) => {
3870
+ const segIndexA = parseInt(a.match(/seg(\d+)\.json$/)?.[1] ?? "0", 10);
3871
+ const segIndexB = parseInt(b.match(/seg(\d+)\.json$/)?.[1] ?? "0", 10);
3872
+ return segIndexA - segIndexB;
3873
+ }).map((f) => `${dir}/${f}`);
3874
+ } catch {
3875
+ return [];
3876
+ }
3877
+ }
3878
+ function extractTranscript(rawDir) {
3879
+ const files = listMatchingFiles(rawDir, /^pass1-seg\d+\.json$/);
3880
+ if (files.length === 0) {
3881
+ log9.info("No transcript data found (pass1-seg*.json missing)");
3882
+ return;
3883
+ }
3884
+ for (const filePath of files) {
3885
+ const data = readJson(filePath);
3886
+ if (data == null) continue;
3887
+ for (const entry of data.transcript_entries ?? []) {
3888
+ const speaker = entry.speaker ? `[${entry.speaker}] ` : "";
3889
+ console.log(`${entry.timestamp} ${speaker}${entry.text}`);
3890
+ }
3891
+ }
3892
+ }
3893
+ function extractCode(rawDir) {
3894
+ const filePath = `${rawDir}/pass3a.json`;
3895
+ if (!existsSync4(filePath)) {
3896
+ log9.info("No code data found (pass3a.json missing)");
3897
+ return;
3898
+ }
3899
+ const data = readJson(filePath);
3900
+ if (data == null || !Array.isArray(data.files) || data.files.length === 0) {
3901
+ log9.info("No code files found in pass3a.json");
3902
+ return;
3903
+ }
3904
+ for (const file of data.files) {
3905
+ console.log(`
3906
+ ${"=".repeat(60)}`);
3907
+ console.log(`// ${file.filename}`);
3908
+ console.log("=".repeat(60));
3909
+ console.log(file.final_content ?? "");
3910
+ }
3911
+ }
3912
+ function extractLinks(rawDir) {
3913
+ const files = listMatchingFiles(rawDir, /^pass3c-seg\d+\.json$/);
3914
+ if (files.length === 0) {
3915
+ log9.info("No link data found (pass3c-seg*.json missing)");
3916
+ return;
3917
+ }
3918
+ let found = 0;
3919
+ for (const filePath of files) {
3920
+ const data = readJson(filePath);
3921
+ if (data == null) continue;
3922
+ for (const link of data.links ?? []) {
3923
+ console.log(`${link.url}`);
3924
+ if (link.context) {
3925
+ console.log(` Context: ${link.context}`);
3926
+ }
3927
+ if (link.timestamp) {
3928
+ console.log(` At: ${link.timestamp}`);
3929
+ }
3930
+ console.log("");
3931
+ found++;
3932
+ }
3933
+ }
3934
+ if (found === 0) {
3935
+ log9.info("No links found in pass3c segments");
3936
+ }
3937
+ }
3938
+ function extractPeople(rawDir) {
3939
+ const filePath = `${rawDir}/pass3b-people.json`;
3940
+ if (!existsSync4(filePath)) {
3941
+ log9.info("No people data found (pass3b-people.json missing)");
3942
+ return;
3943
+ }
3944
+ const data = readJson(filePath);
3945
+ if (data == null || !Array.isArray(data.participants) || data.participants.length === 0) {
3946
+ log9.info("No participants found in pass3b-people.json");
3947
+ return;
3948
+ }
3949
+ for (const p of data.participants) {
3950
+ console.log(`
3951
+ Name: ${p.name ?? "Unknown"}`);
3952
+ if (p.role) console.log(`Role: ${p.role}`);
3953
+ if (p.organization) console.log(`Organization: ${p.organization}`);
3954
+ if (Array.isArray(p.contributions) && p.contributions.length > 0) {
3955
+ console.log("Contributions:");
3956
+ for (const c of p.contributions) {
3957
+ console.log(` - ${c}`);
3958
+ }
3959
+ }
3960
+ }
3961
+ }
3962
+ function extractCommands(rawDir) {
3963
+ const files = listMatchingFiles(rawDir, /^pass2-seg\d+\.json$/);
3964
+ if (files.length === 0) {
3965
+ log9.info("No visual data found (pass2-seg*.json missing)");
3966
+ return;
3967
+ }
3968
+ let found = 0;
3969
+ for (const filePath of files) {
3970
+ const data = readJson(filePath);
3971
+ if (data == null) continue;
3972
+ for (const block of data.code_blocks ?? []) {
3973
+ if (block.screen_type?.toLowerCase().includes("terminal")) {
3974
+ console.log(`# ${block.timestamp}`);
3975
+ console.log(block.content ?? "");
3976
+ console.log("");
3977
+ found++;
3978
+ }
3979
+ }
3980
+ }
3981
+ if (found === 0) {
3982
+ log9.info("No terminal commands found in pass2 segments");
3983
+ }
3984
+ }
3985
+ function strategyForType(type) {
3986
+ switch (type) {
3987
+ case "transcript":
3988
+ return { passes: ["transcript"], resolution: "medium", segmentMinutes: 10 };
3989
+ case "code":
3990
+ return { passes: ["transcript", "visual", "code"], resolution: "medium", segmentMinutes: 10 };
3991
+ case "links":
3992
+ return { passes: ["transcript", "visual", "chat"], resolution: "medium", segmentMinutes: 10 };
3993
+ case "people":
3994
+ return { passes: ["transcript", "people"], resolution: "medium", segmentMinutes: 10 };
3995
+ case "commands":
3996
+ return { passes: ["transcript", "visual"], resolution: "medium", segmentMinutes: 10 };
3997
+ }
3998
+ }
3999
+ function printVideoModeResults(type, result) {
4000
+ switch (type) {
4001
+ case "transcript":
4002
+ for (const seg of result.segments) {
4003
+ for (const entry of seg.pass1?.transcript_entries ?? []) {
4004
+ const speaker = entry.speaker ? `[${entry.speaker}] ` : "";
4005
+ console.log(`${entry.timestamp} ${speaker}${entry.text}`);
4006
+ }
4007
+ }
4008
+ break;
4009
+ case "code":
4010
+ if (result.codeReconstruction == null || result.codeReconstruction.files.length === 0) {
4011
+ log9.info("No code files extracted");
4012
+ } else {
4013
+ for (const file of result.codeReconstruction.files) {
4014
+ console.log(`
4015
+ ${"=".repeat(60)}`);
4016
+ console.log(`// ${file.filename}`);
4017
+ console.log("=".repeat(60));
4018
+ console.log(file.final_content ?? "");
4019
+ }
4020
+ }
4021
+ break;
4022
+ case "links":
4023
+ {
4024
+ let found = 0;
4025
+ for (const seg of result.segments) {
4026
+ for (const link of seg.pass3c?.links ?? []) {
4027
+ console.log(`${link.url}`);
4028
+ if (link.context) console.log(` Context: ${link.context}`);
4029
+ if (link.timestamp) console.log(` At: ${link.timestamp}`);
4030
+ console.log("");
4031
+ found++;
4032
+ }
4033
+ }
4034
+ if (found === 0) log9.info("No links found");
4035
+ }
4036
+ break;
4037
+ case "people":
4038
+ if (result.peopleExtraction == null || result.peopleExtraction.participants.length === 0) {
4039
+ log9.info("No participants found");
4040
+ } else {
4041
+ for (const p of result.peopleExtraction.participants) {
4042
+ console.log(`
4043
+ Name: ${p.name ?? "Unknown"}`);
4044
+ if (p.role) console.log(`Role: ${p.role}`);
4045
+ if (p.organization) console.log(`Organization: ${p.organization}`);
4046
+ if (Array.isArray(p.contributions) && p.contributions.length > 0) {
4047
+ console.log("Contributions:");
4048
+ for (const c of p.contributions) console.log(` - ${c}`);
4049
+ }
4050
+ }
4051
+ }
4052
+ break;
4053
+ case "commands":
4054
+ {
4055
+ let found = 0;
4056
+ for (const seg of result.segments) {
4057
+ for (const block of seg.pass2?.code_blocks ?? []) {
4058
+ if (block.screen_type?.toLowerCase().includes("terminal")) {
4059
+ console.log(`# ${block.timestamp}`);
4060
+ console.log(block.content ?? "");
4061
+ console.log("");
4062
+ found++;
4063
+ }
4064
+ }
4065
+ }
4066
+ if (found === 0) log9.info("No terminal commands found");
4067
+ }
4068
+ break;
4069
+ }
4070
+ }
4071
+ async function run(args) {
4072
+ let lang;
4073
+ const filteredArgs = [];
4074
+ for (let i = 0; i < args.length; i++) {
4075
+ if (args[i] === "--lang" && args[i + 1] != null) {
4076
+ lang = args[++i];
4077
+ } else {
4078
+ filteredArgs.push(args[i]);
4079
+ }
4080
+ }
4081
+ const [typeArg, sourceArg] = filteredArgs;
4082
+ if (typeArg == null) {
4083
+ log9.error(`Usage: vidistill extract <type> <source>`);
4084
+ log9.error(`Valid types: ${VALID_TYPES.join(", ")}`);
4085
+ process.exit(1);
4086
+ }
4087
+ if (!isValidType(typeArg)) {
4088
+ log9.error(`Unknown extraction type: "${typeArg}"`);
4089
+ log9.error(`Valid types: ${VALID_TYPES.join(", ")}`);
4090
+ process.exit(1);
4091
+ }
4092
+ if (sourceArg == null) {
4093
+ log9.error("Missing source: provide a vidistill output directory or a video file path");
4094
+ process.exit(1);
4095
+ }
4096
+ const sourcePath = resolve2(sourceArg);
4097
+ const type = typeArg;
4098
+ const sourceExists = existsSync4(sourcePath);
4099
+ if (!sourceExists) {
4100
+ log9.error(`Source not found: ${sourcePath}`);
4101
+ process.exit(1);
4102
+ }
4103
+ const isDir = statSync2(sourcePath).isDirectory();
4104
+ if (isDir) {
4105
+ const metadataPath = `${sourcePath}/metadata.json`;
4106
+ if (!existsSync4(metadataPath)) {
4107
+ log9.error(`Not a vidistill output directory (metadata.json not found): ${sourcePath}`);
4108
+ process.exit(1);
4109
+ }
4110
+ const rawDir = `${sourcePath}/raw`;
4111
+ switch (type) {
4112
+ case "transcript":
4113
+ extractTranscript(rawDir);
4114
+ break;
4115
+ case "code":
4116
+ extractCode(rawDir);
4117
+ break;
4118
+ case "links":
4119
+ extractLinks(rawDir);
4120
+ break;
4121
+ case "people":
4122
+ extractPeople(rawDir);
4123
+ break;
4124
+ case "commands":
4125
+ extractCommands(rawDir);
4126
+ break;
4127
+ }
4128
+ return;
4129
+ }
4130
+ const apiKey = await resolveApiKey();
4131
+ const client = new GeminiClient(apiKey);
4132
+ log9.info(`Uploading ${basename4(sourcePath)}...`);
4133
+ const localResult = await handleLocalFile(sourcePath, client);
4134
+ const duration = await detectDuration({
4135
+ filePath: sourcePath,
4136
+ geminiDuration: localResult.duration
4137
+ });
4138
+ const overrideStrategy = strategyForType(type);
4139
+ const rateLimiter = new RateLimiter();
4140
+ const progress2 = createProgressDisplay();
4141
+ const estSegments = Math.max(1, Math.ceil(duration / (overrideStrategy.segmentMinutes * 60)));
4142
+ log9.info(`Extracting ${type} (${estSegments} segment${estSegments !== 1 ? "s" : ""})...`);
4143
+ const pipelineResult = await runPipeline({
4144
+ client,
4145
+ fileUri: localResult.fileUri,
4146
+ mimeType: localResult.mimeType,
4147
+ duration,
4148
+ model: MODELS.flash,
4149
+ lang,
4150
+ rateLimiter,
4151
+ overrideStrategy,
4152
+ onProgress: (status) => {
4153
+ progress2.update(status);
4154
+ },
4155
+ onWait: (delayMs) => progress2.onWait(delayMs)
4156
+ });
4157
+ progress2.complete(pipelineResult, 0);
4158
+ if (localResult.uploadedFileName != null) {
4159
+ try {
4160
+ await client.deleteFile(localResult.uploadedFileName);
4161
+ } catch {
4162
+ }
4163
+ }
4164
+ printVideoModeResults(type, pipelineResult);
4165
+ if (pipelineResult.errors.length > 0) {
4166
+ log9.info(`Completed with ${pipelineResult.errors.length} error(s)`);
4167
+ }
4168
+ }
4169
+
4170
+ // src/commands/ask.ts
4171
+ import { log as log10 } from "@clack/prompts";
4172
+ async function run2(_args) {
4173
+ log10.info("Not implemented yet.");
4174
+ }
4175
+
4176
+ // src/commands/search.ts
4177
+ import { log as log11 } from "@clack/prompts";
4178
+ async function run3(_args) {
4179
+ log11.info("Not implemented yet.");
4180
+ }
4181
+
4182
+ // src/commands/mcp.ts
4183
+ import { log as log12 } from "@clack/prompts";
4184
+ async function run4(_args) {
4185
+ log12.info("Not implemented yet.");
4186
+ }
4187
+
4188
+ // src/commands/watch.ts
4189
+ import { log as log13 } from "@clack/prompts";
4190
+ async function run5(_args) {
4191
+ log13.info("Not implemented yet.");
4192
+ }
4193
+
4194
+ // src/commands/rename-speakers.ts
4195
+ import { log as log14 } from "@clack/prompts";
4196
+ async function run6(_args) {
4197
+ log14.info("Not implemented yet.");
4198
+ }
3857
4199
 
3858
4200
  // src/cli/index.ts
3859
4201
  var _require2 = createRequire2(import.meta.url);
3860
4202
  var { version } = _require2("../package.json");
3861
4203
  var DEFAULT_OUTPUT = "./vidistill-output/";
3862
- var SUBCOMMANDS = /* @__PURE__ */ new Set(["ask", "search", "extract", "mcp", "watch", "rename-speakers"]);
4204
+ var SUBCOMMANDS = {
4205
+ ask: run2,
4206
+ search: run3,
4207
+ extract: run,
4208
+ mcp: run4,
4209
+ watch: run5,
4210
+ "rename-speakers": run6
4211
+ };
3863
4212
  var main = defineCommand({
3864
4213
  meta: {
3865
4214
  name: "vidistill",
3866
4215
  version,
3867
- description: "Video Intelligence Distiller \u2014 turn video into structured notes\n\nCommands: ask, search, extract, mcp, watch, rename-speakers"
4216
+ description: `Video Intelligence Distiller \u2014 turn video into structured notes
4217
+
4218
+ Commands: ${Object.keys(SUBCOMMANDS).join(", ")}`
3868
4219
  },
3869
4220
  args: {
3870
4221
  input: {
@@ -3893,12 +4244,8 @@ var main = defineCommand({
3893
4244
  showLogo();
3894
4245
  showIntro();
3895
4246
  const name = args.input;
3896
- if (name != null && SUBCOMMANDS.has(name)) {
3897
- const mod = await globImport_commands_js(`../commands/${name}.js`);
3898
- if (typeof mod.run !== "function") {
3899
- throw new Error(`Subcommand "${name}" does not export a run function`);
3900
- }
3901
- await mod.run(process.argv.slice(3));
4247
+ if (name != null && name in SUBCOMMANDS) {
4248
+ await SUBCOMMANDS[name](process.argv.slice(3));
3902
4249
  return;
3903
4250
  }
3904
4251
  try {
@@ -3909,11 +4256,11 @@ var main = defineCommand({
3909
4256
  lang: args.lang
3910
4257
  });
3911
4258
  } catch (err) {
3912
- const { log: log9 } = await import("@clack/prompts");
4259
+ const { log: log15 } = await import("@clack/prompts");
3913
4260
  const { default: pc5 } = await import("picocolors");
3914
4261
  const raw = err instanceof Error ? err.message : String(err);
3915
4262
  const message = raw.split("\n")[0].slice(0, 200);
3916
- log9.error(pc5.red(message));
4263
+ log15.error(pc5.red(message));
3917
4264
  process.exit(1);
3918
4265
  }
3919
4266
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vidistill",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "Video intelligence distiller — extract structured notes, transcripts, and insights from any video using Gemini",
5
5
  "type": "module",
6
6
  "license": "MIT",