video-context-mcp-server 0.11.0-beta
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 +432 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +152 -0
- package/dist/index.js.map +1 -0
- package/dist/services/audio/assemblyAiClient.d.ts +28 -0
- package/dist/services/audio/assemblyAiClient.d.ts.map +1 -0
- package/dist/services/audio/assemblyAiClient.js +40 -0
- package/dist/services/audio/assemblyAiClient.js.map +1 -0
- package/dist/services/audio/deepgramClient.d.ts +23 -0
- package/dist/services/audio/deepgramClient.d.ts.map +1 -0
- package/dist/services/audio/deepgramClient.js +50 -0
- package/dist/services/audio/deepgramClient.js.map +1 -0
- package/dist/services/audio/groqAudioClient.d.ts +18 -0
- package/dist/services/audio/groqAudioClient.d.ts.map +1 -0
- package/dist/services/audio/groqAudioClient.js +43 -0
- package/dist/services/audio/groqAudioClient.js.map +1 -0
- package/dist/services/audioRouter.d.ts +38 -0
- package/dist/services/audioRouter.d.ts.map +1 -0
- package/dist/services/audioRouter.js +81 -0
- package/dist/services/audioRouter.js.map +1 -0
- package/dist/services/ffmpeg.d.ts +54 -0
- package/dist/services/ffmpeg.d.ts.map +1 -0
- package/dist/services/ffmpeg.js +188 -0
- package/dist/services/ffmpeg.js.map +1 -0
- package/dist/services/geminiClient.d.ts +55 -0
- package/dist/services/geminiClient.d.ts.map +1 -0
- package/dist/services/geminiClient.js +143 -0
- package/dist/services/geminiClient.js.map +1 -0
- package/dist/services/glmClient.d.ts +50 -0
- package/dist/services/glmClient.d.ts.map +1 -0
- package/dist/services/glmClient.js +196 -0
- package/dist/services/glmClient.js.map +1 -0
- package/dist/services/kimiClient.d.ts +45 -0
- package/dist/services/kimiClient.d.ts.map +1 -0
- package/dist/services/kimiClient.js +152 -0
- package/dist/services/kimiClient.js.map +1 -0
- package/dist/services/providerRouter.d.ts +40 -0
- package/dist/services/providerRouter.d.ts.map +1 -0
- package/dist/services/providerRouter.js +64 -0
- package/dist/services/providerRouter.js.map +1 -0
- package/dist/tools/analyzeVideo.d.ts +18 -0
- package/dist/tools/analyzeVideo.d.ts.map +1 -0
- package/dist/tools/analyzeVideo.js +153 -0
- package/dist/tools/analyzeVideo.js.map +1 -0
- package/dist/tools/extractFrames.d.ts +22 -0
- package/dist/tools/extractFrames.d.ts.map +1 -0
- package/dist/tools/extractFrames.js +82 -0
- package/dist/tools/extractFrames.js.map +1 -0
- package/dist/tools/getVideoInfo.d.ts +18 -0
- package/dist/tools/getVideoInfo.d.ts.map +1 -0
- package/dist/tools/getVideoInfo.js +52 -0
- package/dist/tools/getVideoInfo.js.map +1 -0
- package/dist/tools/searchTimestamp.d.ts +25 -0
- package/dist/tools/searchTimestamp.d.ts.map +1 -0
- package/dist/tools/searchTimestamp.js +152 -0
- package/dist/tools/searchTimestamp.js.map +1 -0
- package/dist/tools/summarizeVideo.d.ts +18 -0
- package/dist/tools/summarizeVideo.d.ts.map +1 -0
- package/dist/tools/summarizeVideo.js +208 -0
- package/dist/tools/summarizeVideo.js.map +1 -0
- package/dist/tools/transcribeVideo.d.ts +18 -0
- package/dist/tools/transcribeVideo.d.ts.map +1 -0
- package/dist/tools/transcribeVideo.js +106 -0
- package/dist/tools/transcribeVideo.js.map +1 -0
- package/dist/utils/audioUtils.d.ts +62 -0
- package/dist/utils/audioUtils.d.ts.map +1 -0
- package/dist/utils/audioUtils.js +153 -0
- package/dist/utils/audioUtils.js.map +1 -0
- package/dist/utils/base64.d.ts +35 -0
- package/dist/utils/base64.d.ts.map +1 -0
- package/dist/utils/base64.js +50 -0
- package/dist/utils/base64.js.map +1 -0
- package/dist/utils/logger.d.ts +23 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +34 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/tempFiles.d.ts +20 -0
- package/dist/utils/tempFiles.d.ts.map +1 -0
- package/dist/utils/tempFiles.js +31 -0
- package/dist/utils/tempFiles.js.map +1 -0
- package/dist/utils/videoUtils.d.ts +19 -0
- package/dist/utils/videoUtils.d.ts.map +1 -0
- package/dist/utils/videoUtils.js +38 -0
- package/dist/utils/videoUtils.js.map +1 -0
- package/package.json +64 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: summarize_video
|
|
3
|
+
* Generate a structured summary of the video
|
|
4
|
+
*/
|
|
5
|
+
export declare const summarizeVideoTool: (params: any) => Promise<{
|
|
6
|
+
content: {
|
|
7
|
+
type: "text";
|
|
8
|
+
text: string;
|
|
9
|
+
}[];
|
|
10
|
+
isError?: undefined;
|
|
11
|
+
} | {
|
|
12
|
+
isError: boolean;
|
|
13
|
+
content: {
|
|
14
|
+
type: "text";
|
|
15
|
+
text: string;
|
|
16
|
+
}[];
|
|
17
|
+
}>;
|
|
18
|
+
//# sourceMappingURL=summarizeVideo.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summarizeVideo.d.ts","sourceRoot":"","sources":["../../src/tools/summarizeVideo.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA0CH,eAAO,MAAM,kBAAkB,GAAU,QAAQ,GAAG;;;;;;;;;;;;EA4NnD,CAAA"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: summarize_video
|
|
3
|
+
* Generate a structured summary of the video
|
|
4
|
+
*/
|
|
5
|
+
import * as fs from 'fs/promises';
|
|
6
|
+
import { createReadStream } from 'fs';
|
|
7
|
+
import { extractFramesEvenly, getVideoMetadata } from '../services/ffmpeg.js';
|
|
8
|
+
import { createGLMClient } from '../services/glmClient.js';
|
|
9
|
+
import { createKimiClient } from '../services/kimiClient.js';
|
|
10
|
+
import { createGeminiClient } from '../services/geminiClient.js';
|
|
11
|
+
import { getDefaultProvider, getMaxFrames, resolveProviderWithFallback, } from '../services/providerRouter.js';
|
|
12
|
+
import { bufferToBase64String } from '../utils/base64.js';
|
|
13
|
+
import { createTempDir, cleanupTempDir } from '../utils/tempFiles.js';
|
|
14
|
+
import { isRemoteUrl, normalizeVideoPath, downloadVideoToTemp, } from '../utils/videoUtils.js';
|
|
15
|
+
import { logProgress } from '../utils/logger.js';
|
|
16
|
+
import { getAudioEnhancementMode, tryExtractAndTranscribeAudio, buildPromptWithTranscript, } from '../utils/audioUtils.js';
|
|
17
|
+
const LONG_VIDEO_THRESHOLD_SEC = 5 * 60;
|
|
18
|
+
function buildSummaryPrompt() {
|
|
19
|
+
return [
|
|
20
|
+
'Provide a structured summary of this video.',
|
|
21
|
+
'Format your response with sections:',
|
|
22
|
+
'1) Overview',
|
|
23
|
+
'2) Key scenes',
|
|
24
|
+
'3) Timeline (with approximate timestamps)',
|
|
25
|
+
'4) Notable details',
|
|
26
|
+
].join('\n');
|
|
27
|
+
}
|
|
28
|
+
export const summarizeVideoTool = async (params) => {
|
|
29
|
+
let tempDir = null;
|
|
30
|
+
try {
|
|
31
|
+
const { videoPath } = params;
|
|
32
|
+
const provider = params.provider ?? getDefaultProvider();
|
|
33
|
+
const glmClient = createGLMClient();
|
|
34
|
+
const kimiClient = createKimiClient();
|
|
35
|
+
const geminiClient = createGeminiClient();
|
|
36
|
+
const prompt = buildSummaryPrompt();
|
|
37
|
+
let selectedProvider;
|
|
38
|
+
let fallbackNotice = '';
|
|
39
|
+
let summary;
|
|
40
|
+
if (isRemoteUrl(videoPath)) {
|
|
41
|
+
const resolved = resolveProviderWithFallback(provider, Boolean(kimiClient), Boolean(glmClient), Boolean(geminiClient));
|
|
42
|
+
selectedProvider = resolved.provider;
|
|
43
|
+
if (resolved.fallbackFrom) {
|
|
44
|
+
fallbackNotice = ` (fell back from ${resolved.fallbackFrom})`;
|
|
45
|
+
await logProgress(`Provider ${resolved.fallbackFrom} unavailable, using ${resolved.provider}`);
|
|
46
|
+
}
|
|
47
|
+
if (selectedProvider === 'gemini') {
|
|
48
|
+
if (!geminiClient) {
|
|
49
|
+
throw new Error('Gemini client is unavailable. Set GEMINI_API_KEY.');
|
|
50
|
+
}
|
|
51
|
+
tempDir = await createTempDir();
|
|
52
|
+
await logProgress('Downloading remote video...');
|
|
53
|
+
const localPath = await downloadVideoToTemp(videoPath, tempDir);
|
|
54
|
+
await logProgress('Uploading to Gemini...');
|
|
55
|
+
const uploadedVideo = await geminiClient.uploadVideo(localPath);
|
|
56
|
+
await logProgress('Waiting for Gemini to process video...');
|
|
57
|
+
await geminiClient.waitForFileProcessing(uploadedVideo.name);
|
|
58
|
+
await logProgress('Summarizing video...');
|
|
59
|
+
summary = await geminiClient.analyzeVideo(uploadedVideo, prompt);
|
|
60
|
+
}
|
|
61
|
+
else if (selectedProvider === 'glm') {
|
|
62
|
+
if (!glmClient) {
|
|
63
|
+
throw new Error('GLM provider is required for URL video summarization, but Z_AI_API_KEY is not configured.');
|
|
64
|
+
}
|
|
65
|
+
await logProgress('Summarizing remote video...');
|
|
66
|
+
summary = await glmClient.analyzeVideoUrl(videoPath, prompt);
|
|
67
|
+
}
|
|
68
|
+
else if (selectedProvider === 'kimi') {
|
|
69
|
+
if (!kimiClient) {
|
|
70
|
+
throw new Error('Kimi client is unavailable. Set MOONSHOT_API_KEY.');
|
|
71
|
+
}
|
|
72
|
+
tempDir = await createTempDir();
|
|
73
|
+
await logProgress('Downloading remote video...');
|
|
74
|
+
const localPath = await downloadVideoToTemp(videoPath, tempDir);
|
|
75
|
+
const remoteMetadata = await getVideoMetadata(localPath);
|
|
76
|
+
let enhancedPrompt = prompt;
|
|
77
|
+
if (getAudioEnhancementMode() !== 'false') {
|
|
78
|
+
const transcriptResult = await tryExtractAndTranscribeAudio(localPath, tempDir);
|
|
79
|
+
enhancedPrompt = buildPromptWithTranscript(prompt, transcriptResult);
|
|
80
|
+
}
|
|
81
|
+
const isLongRemoteVideo = remoteMetadata.duration > LONG_VIDEO_THRESHOLD_SEC;
|
|
82
|
+
if (isLongRemoteVideo) {
|
|
83
|
+
await logProgress('Extracting keyframes...');
|
|
84
|
+
const frames = await extractFramesEvenly(localPath, getMaxFrames());
|
|
85
|
+
const images = frames.map((frameBuffer) => ({
|
|
86
|
+
data: bufferToBase64String(frameBuffer),
|
|
87
|
+
mimeType: 'image/png',
|
|
88
|
+
}));
|
|
89
|
+
await logProgress('Summarizing video...');
|
|
90
|
+
summary = await kimiClient.analyzeImages(images, enhancedPrompt);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
await logProgress('Uploading to Kimi...');
|
|
94
|
+
const uploadedVideo = await kimiClient.uploadVideo(createReadStream(localPath));
|
|
95
|
+
await logProgress('Summarizing video...');
|
|
96
|
+
summary = await kimiClient.analyzeVideo(uploadedVideo.id, enhancedPrompt);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
throw new Error('No suitable provider found for remote URL summarization.');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
const resolvedPath = normalizeVideoPath(videoPath);
|
|
105
|
+
const metadata = await getVideoMetadata(resolvedPath);
|
|
106
|
+
const resolved = resolveProviderWithFallback(provider, Boolean(kimiClient), Boolean(glmClient), Boolean(geminiClient));
|
|
107
|
+
selectedProvider = resolved.provider;
|
|
108
|
+
if (resolved.fallbackFrom) {
|
|
109
|
+
fallbackNotice = ` (fell back from ${resolved.fallbackFrom})`;
|
|
110
|
+
await logProgress(`Provider ${resolved.fallbackFrom} unavailable, using ${resolved.provider}`);
|
|
111
|
+
}
|
|
112
|
+
// Inject audio transcript into prompt for GLM/Kimi (Gemini handles audio natively)
|
|
113
|
+
let enhancedPrompt = prompt;
|
|
114
|
+
if (selectedProvider !== 'gemini' &&
|
|
115
|
+
getAudioEnhancementMode() !== 'false') {
|
|
116
|
+
if (!tempDir)
|
|
117
|
+
tempDir = await createTempDir();
|
|
118
|
+
const result = await tryExtractAndTranscribeAudio(resolvedPath, tempDir);
|
|
119
|
+
enhancedPrompt = buildPromptWithTranscript(prompt, result);
|
|
120
|
+
}
|
|
121
|
+
if (selectedProvider === 'gemini') {
|
|
122
|
+
if (!geminiClient) {
|
|
123
|
+
throw new Error('Gemini client is unavailable. Set GEMINI_API_KEY.');
|
|
124
|
+
}
|
|
125
|
+
await logProgress('Uploading to Gemini...');
|
|
126
|
+
const uploadedVideo = await geminiClient.uploadVideo(resolvedPath);
|
|
127
|
+
await logProgress('Waiting for Gemini to process video...');
|
|
128
|
+
await geminiClient.waitForFileProcessing(uploadedVideo.name);
|
|
129
|
+
await logProgress('Summarizing video...');
|
|
130
|
+
summary = await geminiClient.analyzeVideo(uploadedVideo, prompt);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
const isLongVideo = metadata.duration > LONG_VIDEO_THRESHOLD_SEC;
|
|
134
|
+
if (isLongVideo) {
|
|
135
|
+
await logProgress('Extracting keyframes...');
|
|
136
|
+
const frames = await extractFramesEvenly(resolvedPath, getMaxFrames());
|
|
137
|
+
const images = frames.map((frameBuffer) => ({
|
|
138
|
+
data: bufferToBase64String(frameBuffer),
|
|
139
|
+
mimeType: 'image/png',
|
|
140
|
+
}));
|
|
141
|
+
if (selectedProvider === 'glm') {
|
|
142
|
+
if (!glmClient) {
|
|
143
|
+
throw new Error('GLM client is unavailable. Set Z_AI_API_KEY.');
|
|
144
|
+
}
|
|
145
|
+
await logProgress('Summarizing video...');
|
|
146
|
+
summary = await glmClient.analyzeImages(images, enhancedPrompt);
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
if (!kimiClient) {
|
|
150
|
+
throw new Error('Kimi client is unavailable. Set MOONSHOT_API_KEY.');
|
|
151
|
+
}
|
|
152
|
+
await logProgress('Summarizing video...');
|
|
153
|
+
summary = await kimiClient.analyzeImages(images, enhancedPrompt);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
if (selectedProvider === 'glm') {
|
|
158
|
+
if (!glmClient) {
|
|
159
|
+
throw new Error('GLM client is unavailable. Set Z_AI_API_KEY.');
|
|
160
|
+
}
|
|
161
|
+
await logProgress('Summarizing video...');
|
|
162
|
+
const videoBuffer = await fs.readFile(resolvedPath);
|
|
163
|
+
summary = await glmClient.analyzeVideoBase64(bufferToBase64String(videoBuffer), enhancedPrompt);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
if (!kimiClient) {
|
|
167
|
+
throw new Error('Kimi client is unavailable. Set MOONSHOT_API_KEY.');
|
|
168
|
+
}
|
|
169
|
+
await logProgress('Uploading to Kimi...');
|
|
170
|
+
const uploadedVideo = await kimiClient.uploadVideo(createReadStream(resolvedPath));
|
|
171
|
+
await logProgress('Summarizing video...');
|
|
172
|
+
summary = await kimiClient.analyzeVideo(uploadedVideo.id, enhancedPrompt);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (tempDir) {
|
|
178
|
+
await cleanupTempDir(tempDir);
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
content: [
|
|
182
|
+
{
|
|
183
|
+
type: 'text',
|
|
184
|
+
text: summary,
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
type: 'text',
|
|
188
|
+
text: `Provider used: ${selectedProvider}${fallbackNotice}`,
|
|
189
|
+
},
|
|
190
|
+
],
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
if (tempDir) {
|
|
195
|
+
await cleanupTempDir(tempDir).catch(() => { });
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
isError: true,
|
|
199
|
+
content: [
|
|
200
|
+
{
|
|
201
|
+
type: 'text',
|
|
202
|
+
text: `Error summarizing video: ${error instanceof Error ? error.message : String(error)}`,
|
|
203
|
+
},
|
|
204
|
+
],
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
//# sourceMappingURL=summarizeVideo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summarizeVideo.js","sourceRoot":"","sources":["../../src/tools/summarizeVideo.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: transcribe_video
|
|
3
|
+
* Extract audio from a video and transcribe it using a chosen audio provider
|
|
4
|
+
*/
|
|
5
|
+
export declare const transcribeVideoTool: (params: any) => Promise<{
|
|
6
|
+
content: {
|
|
7
|
+
type: "text";
|
|
8
|
+
text: string;
|
|
9
|
+
}[];
|
|
10
|
+
isError?: undefined;
|
|
11
|
+
} | {
|
|
12
|
+
isError: boolean;
|
|
13
|
+
content: {
|
|
14
|
+
type: "text";
|
|
15
|
+
text: string;
|
|
16
|
+
}[];
|
|
17
|
+
}>;
|
|
18
|
+
//# sourceMappingURL=transcribeVideo.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcribeVideo.d.ts","sourceRoot":"","sources":["../../src/tools/transcribeVideo.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA0BH,eAAO,MAAM,mBAAmB,GAAU,QAAQ,GAAG;;;;;;;;;;;;EAuGpD,CAAA"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: transcribe_video
|
|
3
|
+
* Extract audio from a video and transcribe it using a chosen audio provider
|
|
4
|
+
*/
|
|
5
|
+
import * as path from 'path';
|
|
6
|
+
import { extractAudio } from '../services/ffmpeg.js';
|
|
7
|
+
import { DeepgramClient } from '../services/audio/deepgramClient.js';
|
|
8
|
+
import { AssemblyAiClient } from '../services/audio/assemblyAiClient.js';
|
|
9
|
+
import { GroqAudioClient } from '../services/audio/groqAudioClient.js';
|
|
10
|
+
import { createGeminiClient } from '../services/geminiClient.js';
|
|
11
|
+
import { resolveAudioProvider, getAudioProviderKeys, } from '../services/audioRouter.js';
|
|
12
|
+
import { createTempDir, cleanupTempDir, generateTempFilename, } from '../utils/tempFiles.js';
|
|
13
|
+
import { isRemoteUrl, normalizeVideoPath, downloadVideoToTemp, } from '../utils/videoUtils.js';
|
|
14
|
+
import { logProgress } from '../utils/logger.js';
|
|
15
|
+
export const transcribeVideoTool = async (params) => {
|
|
16
|
+
let tempDir = null;
|
|
17
|
+
try {
|
|
18
|
+
const { videoPath, language, diarize = false, translate = false } = params;
|
|
19
|
+
const provider = params.provider;
|
|
20
|
+
const keys = getAudioProviderKeys();
|
|
21
|
+
const { provider: selectedProvider, fallbackFrom } = resolveAudioProvider(provider, keys);
|
|
22
|
+
if (fallbackFrom) {
|
|
23
|
+
await logProgress(`Audio provider ${fallbackFrom} unavailable, using ${selectedProvider}`);
|
|
24
|
+
}
|
|
25
|
+
tempDir = await createTempDir();
|
|
26
|
+
// Resolve local path (download if remote)
|
|
27
|
+
let localVideoPath;
|
|
28
|
+
if (isRemoteUrl(videoPath)) {
|
|
29
|
+
await logProgress('Downloading remote video...');
|
|
30
|
+
localVideoPath = await downloadVideoToTemp(videoPath, tempDir);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
localVideoPath = normalizeVideoPath(videoPath);
|
|
34
|
+
}
|
|
35
|
+
// Extract audio to temp .m4a
|
|
36
|
+
await logProgress(`Extracting audio (${selectedProvider})...`);
|
|
37
|
+
const audioPath = path.join(tempDir, generateTempFilename('.m4a'));
|
|
38
|
+
await extractAudio(localVideoPath, audioPath);
|
|
39
|
+
// Transcribe via selected provider
|
|
40
|
+
await logProgress('Transcribing...');
|
|
41
|
+
let transcript;
|
|
42
|
+
switch (selectedProvider) {
|
|
43
|
+
case 'deepgram': {
|
|
44
|
+
const apiKey = process.env.DEEPGRAM_API_KEY;
|
|
45
|
+
const client = new DeepgramClient(apiKey);
|
|
46
|
+
const result = await client.transcribe(audioPath, { language, diarize });
|
|
47
|
+
transcript = result.text;
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
case 'assemblyai': {
|
|
51
|
+
const apiKey = process.env.ASSEMBLYAI_API_KEY;
|
|
52
|
+
const client = new AssemblyAiClient(apiKey);
|
|
53
|
+
const result = await client.transcribe(audioPath, { language, diarize });
|
|
54
|
+
transcript = result.text;
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
case 'groq': {
|
|
58
|
+
const apiKey = process.env.GROQ_API_KEY;
|
|
59
|
+
const client = new GroqAudioClient(apiKey);
|
|
60
|
+
const result = await client.transcribe(audioPath, {
|
|
61
|
+
language,
|
|
62
|
+
translate,
|
|
63
|
+
});
|
|
64
|
+
transcript = result.text;
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
case 'gemini': {
|
|
68
|
+
const geminiClient = createGeminiClient();
|
|
69
|
+
if (!geminiClient) {
|
|
70
|
+
throw new Error('Gemini provider selected but GEMINI_API_KEY is not configured.');
|
|
71
|
+
}
|
|
72
|
+
await logProgress('Uploading audio to Gemini...');
|
|
73
|
+
const uploadedFile = await geminiClient.uploadVideo(audioPath, 'audio/mp4');
|
|
74
|
+
await logProgress('Waiting for Gemini to process audio...');
|
|
75
|
+
await geminiClient.waitForFileProcessing(uploadedFile.name);
|
|
76
|
+
transcript = await geminiClient.transcribeAudio(uploadedFile);
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
content: [
|
|
82
|
+
{
|
|
83
|
+
type: 'text',
|
|
84
|
+
text: `[Transcript — provider: ${selectedProvider}${fallbackFrom ? ` (fell back from ${fallbackFrom})` : ''}${diarize ? ', diarization enabled' : ''}${translate ? ', translated to English' : ''}]\n\n${transcript}`,
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
return {
|
|
91
|
+
isError: true,
|
|
92
|
+
content: [
|
|
93
|
+
{
|
|
94
|
+
type: 'text',
|
|
95
|
+
text: `transcribe_video failed: ${error?.message ?? String(error)}`,
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
finally {
|
|
101
|
+
if (tempDir) {
|
|
102
|
+
await cleanupTempDir(tempDir);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
//# sourceMappingURL=transcribeVideo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcribeVideo.js","sourceRoot":"","sources":["../../src/tools/transcribeVideo.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audio injection utility for video analysis tools.
|
|
3
|
+
*
|
|
4
|
+
* AUDIO_ENHANCE_VIDEO_ANALYSIS controls whether audio transcripts are injected
|
|
5
|
+
* into GLM/Kimi prompts for analyze_video and summarize_video.
|
|
6
|
+
*
|
|
7
|
+
* Supported values:
|
|
8
|
+
* "auto" (default) — transcribe only when the video has a detected audio track
|
|
9
|
+
* "true" — always attempt transcription regardless of audio track presence
|
|
10
|
+
* "false" — disabled entirely
|
|
11
|
+
*/
|
|
12
|
+
export type AudioEnhancementMode = 'auto' | 'true' | 'false';
|
|
13
|
+
export type TranscriptResult = {
|
|
14
|
+
text: string;
|
|
15
|
+
confidence: 'high' | 'medium' | 'low';
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Reads and validates AUDIO_ENHANCE_VIDEO_ANALYSIS env var.
|
|
19
|
+
* Returns 'auto' if unset or unrecognised (the default behaviour).
|
|
20
|
+
*/
|
|
21
|
+
export declare function getAudioEnhancementMode(): AudioEnhancementMode;
|
|
22
|
+
/**
|
|
23
|
+
* @deprecated Use `getAudioEnhancementMode() !== 'false'` instead.
|
|
24
|
+
*/
|
|
25
|
+
export declare function isAudioEnhancementEnabled(): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Computes a transcript confidence label based on word density.
|
|
28
|
+
*
|
|
29
|
+
* Thresholds (words per minute):
|
|
30
|
+
* >= 80 → high (typical speech rate)
|
|
31
|
+
* 20–79 → medium (sparse or halting speech)
|
|
32
|
+
* < 20 → low (near-silent — may mislead the model)
|
|
33
|
+
*
|
|
34
|
+
* @param wordCount - Number of words in the transcript
|
|
35
|
+
* @param durationSec - Video duration in seconds
|
|
36
|
+
*/
|
|
37
|
+
export declare function computeTranscriptConfidence(wordCount: number, durationSec: number): 'high' | 'medium' | 'low';
|
|
38
|
+
/**
|
|
39
|
+
* Attempts to extract + transcribe audio from a local video file.
|
|
40
|
+
*
|
|
41
|
+
* - In "auto" mode (default): skips silently when the video has no detected
|
|
42
|
+
* audio track, avoiding unnecessary work on silent screen recordings etc.
|
|
43
|
+
* - In "true" mode: always attempts transcription regardless of audio track.
|
|
44
|
+
* - In "false" mode: returns null immediately (no work done).
|
|
45
|
+
*
|
|
46
|
+
* Returns a TranscriptResult (text + confidence label) on success, or null if
|
|
47
|
+
* enhancement is disabled / no audio track (auto) / no provider / error.
|
|
48
|
+
*
|
|
49
|
+
* @param localVideoPath - Resolved local path to the video file
|
|
50
|
+
* @param tempDir - Temp directory for the extracted audio file
|
|
51
|
+
*/
|
|
52
|
+
export declare function tryExtractAndTranscribeAudio(localVideoPath: string, tempDir: string): Promise<TranscriptResult | null>;
|
|
53
|
+
/**
|
|
54
|
+
* Wraps a prompt with an annotated transcript header.
|
|
55
|
+
* If result is null the original prompt is returned unchanged.
|
|
56
|
+
*
|
|
57
|
+
* The header includes a confidence note for medium/low quality transcripts so
|
|
58
|
+
* the AI model can weight the transcript appropriately rather than treating
|
|
59
|
+
* uncertain content as authoritative.
|
|
60
|
+
*/
|
|
61
|
+
export declare function buildPromptWithTranscript(prompt: string, result: TranscriptResult | null): string;
|
|
62
|
+
//# sourceMappingURL=audioUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audioUtils.d.ts","sourceRoot":"","sources":["../../src/utils/audioUtils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAgBH,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;AAE5D,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAA;CACtC,CAAA;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,IAAI,oBAAoB,CAI9D;AAED;;GAEG;AACH,wBAAgB,yBAAyB,IAAI,OAAO,CAEnD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,2BAA2B,CACzC,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,GAClB,MAAM,GAAG,QAAQ,GAAG,KAAK,CAM3B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,4BAA4B,CAChD,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAkElC;AAED;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,gBAAgB,GAAG,IAAI,GAC9B,MAAM,CASR"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audio injection utility for video analysis tools.
|
|
3
|
+
*
|
|
4
|
+
* AUDIO_ENHANCE_VIDEO_ANALYSIS controls whether audio transcripts are injected
|
|
5
|
+
* into GLM/Kimi prompts for analyze_video and summarize_video.
|
|
6
|
+
*
|
|
7
|
+
* Supported values:
|
|
8
|
+
* "auto" (default) — transcribe only when the video has a detected audio track
|
|
9
|
+
* "true" — always attempt transcription regardless of audio track presence
|
|
10
|
+
* "false" — disabled entirely
|
|
11
|
+
*/
|
|
12
|
+
import * as path from 'path';
|
|
13
|
+
import { extractAudio, getVideoMetadata } from '../services/ffmpeg.js';
|
|
14
|
+
import { DeepgramClient } from '../services/audio/deepgramClient.js';
|
|
15
|
+
import { AssemblyAiClient } from '../services/audio/assemblyAiClient.js';
|
|
16
|
+
import { GroqAudioClient } from '../services/audio/groqAudioClient.js';
|
|
17
|
+
import { createGeminiClient } from '../services/geminiClient.js';
|
|
18
|
+
import { resolveAudioProvider, getAudioProviderKeys, } from '../services/audioRouter.js';
|
|
19
|
+
import { generateTempFilename } from './tempFiles.js';
|
|
20
|
+
import { logProgress } from './logger.js';
|
|
21
|
+
/**
|
|
22
|
+
* Reads and validates AUDIO_ENHANCE_VIDEO_ANALYSIS env var.
|
|
23
|
+
* Returns 'auto' if unset or unrecognised (the default behaviour).
|
|
24
|
+
*/
|
|
25
|
+
export function getAudioEnhancementMode() {
|
|
26
|
+
const val = process.env.AUDIO_ENHANCE_VIDEO_ANALYSIS;
|
|
27
|
+
if (val === 'true' || val === 'false' || val === 'auto')
|
|
28
|
+
return val;
|
|
29
|
+
return 'auto';
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* @deprecated Use `getAudioEnhancementMode() !== 'false'` instead.
|
|
33
|
+
*/
|
|
34
|
+
export function isAudioEnhancementEnabled() {
|
|
35
|
+
return getAudioEnhancementMode() !== 'false';
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Computes a transcript confidence label based on word density.
|
|
39
|
+
*
|
|
40
|
+
* Thresholds (words per minute):
|
|
41
|
+
* >= 80 → high (typical speech rate)
|
|
42
|
+
* 20–79 → medium (sparse or halting speech)
|
|
43
|
+
* < 20 → low (near-silent — may mislead the model)
|
|
44
|
+
*
|
|
45
|
+
* @param wordCount - Number of words in the transcript
|
|
46
|
+
* @param durationSec - Video duration in seconds
|
|
47
|
+
*/
|
|
48
|
+
export function computeTranscriptConfidence(wordCount, durationSec) {
|
|
49
|
+
if (durationSec <= 0)
|
|
50
|
+
return wordCount > 10 ? 'medium' : 'low';
|
|
51
|
+
const wordsPerMinute = wordCount / (durationSec / 60);
|
|
52
|
+
if (wordsPerMinute >= 80)
|
|
53
|
+
return 'high';
|
|
54
|
+
if (wordsPerMinute >= 20)
|
|
55
|
+
return 'medium';
|
|
56
|
+
return 'low';
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Attempts to extract + transcribe audio from a local video file.
|
|
60
|
+
*
|
|
61
|
+
* - In "auto" mode (default): skips silently when the video has no detected
|
|
62
|
+
* audio track, avoiding unnecessary work on silent screen recordings etc.
|
|
63
|
+
* - In "true" mode: always attempts transcription regardless of audio track.
|
|
64
|
+
* - In "false" mode: returns null immediately (no work done).
|
|
65
|
+
*
|
|
66
|
+
* Returns a TranscriptResult (text + confidence label) on success, or null if
|
|
67
|
+
* enhancement is disabled / no audio track (auto) / no provider / error.
|
|
68
|
+
*
|
|
69
|
+
* @param localVideoPath - Resolved local path to the video file
|
|
70
|
+
* @param tempDir - Temp directory for the extracted audio file
|
|
71
|
+
*/
|
|
72
|
+
export async function tryExtractAndTranscribeAudio(localVideoPath, tempDir) {
|
|
73
|
+
const mode = getAudioEnhancementMode();
|
|
74
|
+
if (mode === 'false')
|
|
75
|
+
return null;
|
|
76
|
+
try {
|
|
77
|
+
// Probe metadata to detect audio track presence and get duration for confidence
|
|
78
|
+
const metadata = await getVideoMetadata(localVideoPath);
|
|
79
|
+
if (mode === 'auto' && !metadata.hasAudio) {
|
|
80
|
+
// Silent skip — no audio track detected in the video
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
const keys = getAudioProviderKeys();
|
|
84
|
+
let selectedProvider;
|
|
85
|
+
try {
|
|
86
|
+
const resolved = resolveAudioProvider(undefined, keys);
|
|
87
|
+
selectedProvider = resolved.provider;
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return null; // No audio provider configured — skip gracefully
|
|
91
|
+
}
|
|
92
|
+
await logProgress('Extracting audio for transcript...');
|
|
93
|
+
const audioPath = path.join(tempDir, generateTempFilename('.m4a'));
|
|
94
|
+
await extractAudio(localVideoPath, audioPath);
|
|
95
|
+
await logProgress('Transcribing audio...');
|
|
96
|
+
let text;
|
|
97
|
+
switch (selectedProvider) {
|
|
98
|
+
case 'deepgram': {
|
|
99
|
+
const client = new DeepgramClient(process.env.DEEPGRAM_API_KEY);
|
|
100
|
+
const result = await client.transcribe(audioPath);
|
|
101
|
+
text = result.text;
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
case 'assemblyai': {
|
|
105
|
+
const client = new AssemblyAiClient(process.env.ASSEMBLYAI_API_KEY);
|
|
106
|
+
const result = await client.transcribe(audioPath);
|
|
107
|
+
text = result.text;
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
case 'groq': {
|
|
111
|
+
const client = new GroqAudioClient(process.env.GROQ_API_KEY);
|
|
112
|
+
const result = await client.transcribe(audioPath);
|
|
113
|
+
text = result.text;
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
case 'gemini': {
|
|
117
|
+
const geminiClient = createGeminiClient();
|
|
118
|
+
if (!geminiClient)
|
|
119
|
+
return null;
|
|
120
|
+
const uploadedFile = await geminiClient.uploadVideo(audioPath);
|
|
121
|
+
await geminiClient.waitForFileProcessing(uploadedFile.name);
|
|
122
|
+
text = await geminiClient.transcribeAudio(uploadedFile);
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const wordCount = text.trim().split(/\s+/).filter(Boolean).length;
|
|
127
|
+
const confidence = computeTranscriptConfidence(wordCount, metadata.duration);
|
|
128
|
+
return { text: text, confidence };
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
// Audio extraction/transcription is best-effort — never fail the parent tool
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Wraps a prompt with an annotated transcript header.
|
|
137
|
+
* If result is null the original prompt is returned unchanged.
|
|
138
|
+
*
|
|
139
|
+
* The header includes a confidence note for medium/low quality transcripts so
|
|
140
|
+
* the AI model can weight the transcript appropriately rather than treating
|
|
141
|
+
* uncertain content as authoritative.
|
|
142
|
+
*/
|
|
143
|
+
export function buildPromptWithTranscript(prompt, result) {
|
|
144
|
+
if (!result)
|
|
145
|
+
return prompt;
|
|
146
|
+
const confidenceNote = result.confidence === 'high'
|
|
147
|
+
? ''
|
|
148
|
+
: result.confidence === 'medium'
|
|
149
|
+
? ' [confidence: medium]'
|
|
150
|
+
: ' [confidence: low — sparse speech detected]';
|
|
151
|
+
return `--- AUDIO TRANSCRIPT${confidenceNote} ---\n${result.text}\n--- END TRANSCRIPT ---\n\n${prompt}`;
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=audioUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audioUtils.js","sourceRoot":"","sources":["../../src/utils/audioUtils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base64 encoding utilities for images and videos
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Convert a Buffer to a base64 data URI
|
|
6
|
+
* @param buffer - The data buffer
|
|
7
|
+
* @param mimeType - MIME type (e.g., "image/png", "video/mp4")
|
|
8
|
+
* @returns Base64 data URI string
|
|
9
|
+
*/
|
|
10
|
+
export declare function bufferToBase64(buffer: Buffer, mimeType: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* Convert a Buffer to base64 without the data URI prefix
|
|
13
|
+
* @param buffer - The data buffer
|
|
14
|
+
* @returns Base64 string
|
|
15
|
+
*/
|
|
16
|
+
export declare function bufferToBase64String(buffer: Buffer): string;
|
|
17
|
+
/**
|
|
18
|
+
* Extract the MIME type from a data URI
|
|
19
|
+
* @param dataUri - Data URI string (e.g., "data:image/png;base64,abc123...")
|
|
20
|
+
* @returns MIME type (e.g., "image/png")
|
|
21
|
+
*/
|
|
22
|
+
export declare function getMimeTypeFromDataUri(dataUri: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* Extract the base64 content from a data URI
|
|
25
|
+
* @param dataUri - Data URI string (e.g., "data:image/png;base64,abc123...")
|
|
26
|
+
* @returns Base64 string without prefix
|
|
27
|
+
*/
|
|
28
|
+
export declare function getBase64FromDataUri(dataUri: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* Convert base64 string to Buffer
|
|
31
|
+
* @param base64 - Base64 string (with or without data URI prefix)
|
|
32
|
+
* @returns Buffer
|
|
33
|
+
*/
|
|
34
|
+
export declare function base64ToBuffer(base64: string): Buffer;
|
|
35
|
+
//# sourceMappingURL=base64.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base64.d.ts","sourceRoot":"","sources":["../../src/utils/base64.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGvE;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAG9D;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAG5D;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAIrD"}
|