@mux/ai 0.1.0 → 0.1.1

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 (45) hide show
  1. package/dist/audio-translation.d.ts +21 -0
  2. package/dist/audio-translation.d.ts.map +1 -0
  3. package/dist/audio-translation.js +229 -0
  4. package/dist/audio-translation.js.map +1 -0
  5. package/dist/burned-in-captions.d.ts +19 -0
  6. package/dist/burned-in-captions.d.ts.map +1 -0
  7. package/dist/burned-in-captions.js +243 -0
  8. package/dist/burned-in-captions.js.map +1 -0
  9. package/dist/chapters.d.ts +18 -0
  10. package/dist/chapters.d.ts.map +1 -0
  11. package/dist/chapters.js +255 -0
  12. package/dist/chapters.js.map +1 -0
  13. package/dist/index.d.ts +9 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +9 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/moderation.d.ts +39 -0
  18. package/dist/moderation.d.ts.map +1 -0
  19. package/dist/moderation.js +341 -0
  20. package/dist/moderation.js.map +1 -0
  21. package/dist/summarization.d.ts +26 -0
  22. package/dist/summarization.d.ts.map +1 -0
  23. package/dist/summarization.js +337 -0
  24. package/dist/summarization.js.map +1 -0
  25. package/dist/translation.d.ts +22 -0
  26. package/dist/translation.d.ts.map +1 -0
  27. package/dist/translation.js +196 -0
  28. package/dist/translation.js.map +1 -0
  29. package/dist/types.d.ts +12 -0
  30. package/dist/types.d.ts.map +1 -0
  31. package/dist/types.js +2 -0
  32. package/dist/types.js.map +1 -0
  33. package/dist/utils/image-download.d.ts +65 -0
  34. package/dist/utils/image-download.d.ts.map +1 -0
  35. package/dist/utils/image-download.js +150 -0
  36. package/dist/utils/image-download.js.map +1 -0
  37. package/dist/utils/storyboard-processor.d.ts +40 -0
  38. package/dist/utils/storyboard-processor.d.ts.map +1 -0
  39. package/dist/utils/storyboard-processor.js +202 -0
  40. package/dist/utils/storyboard-processor.js.map +1 -0
  41. package/dist/utils/vtt-parser.d.ts +8 -0
  42. package/dist/utils/vtt-parser.d.ts.map +1 -0
  43. package/dist/utils/vtt-parser.js +43 -0
  44. package/dist/utils/vtt-parser.js.map +1 -0
  45. package/package.json +9 -1
@@ -0,0 +1,21 @@
1
+ import { MuxAIOptions } from './types';
2
+ export interface AudioTranslationResult {
3
+ assetId: string;
4
+ targetLanguageCode: string;
5
+ dubbingId: string;
6
+ uploadedTrackId?: string;
7
+ presignedUrl?: string;
8
+ }
9
+ export interface AudioTranslationOptions extends MuxAIOptions {
10
+ provider?: 'elevenlabs';
11
+ numSpeakers?: number;
12
+ s3Endpoint?: string;
13
+ s3Region?: string;
14
+ s3Bucket?: string;
15
+ s3AccessKeyId?: string;
16
+ s3SecretAccessKey?: string;
17
+ uploadToMux?: boolean;
18
+ elevenLabsApiKey?: string;
19
+ }
20
+ export declare function translateAudio(assetId: string, toLanguageCode: string, options?: AudioTranslationOptions): Promise<AudioTranslationResult>;
21
+ //# sourceMappingURL=audio-translation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audio-translation.d.ts","sourceRoot":"","sources":["../src/audio-translation.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,uBAAwB,SAAQ,YAAY;IAC3D,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAsB,cAAc,CAClC,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,MAAM,EACtB,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,sBAAsB,CAAC,CA4RjC"}
@@ -0,0 +1,229 @@
1
+ import Mux from '@mux/mux-node';
2
+ // Using direct HTTP requests instead of SDK for better compatibility
3
+ import { S3Client } from '@aws-sdk/client-s3';
4
+ import { Upload } from '@aws-sdk/lib-storage';
5
+ import { GetObjectCommand } from '@aws-sdk/client-s3';
6
+ import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
7
+ export async function translateAudio(assetId, toLanguageCode, options = {}) {
8
+ // Uses the default audio track on your asset, language is auto-detected by ElevenLabs
9
+ const { provider = 'elevenlabs', numSpeakers = 0, // 0 = auto-detect
10
+ muxTokenId, muxTokenSecret, elevenLabsApiKey, uploadToMux = true, ...config } = options;
11
+ if (provider !== 'elevenlabs') {
12
+ throw new Error('Only ElevenLabs provider is currently supported for audio translation');
13
+ }
14
+ // Validate required credentials
15
+ const muxId = muxTokenId || process.env.MUX_TOKEN_ID;
16
+ const muxSecret = muxTokenSecret || process.env.MUX_TOKEN_SECRET;
17
+ const elevenLabsKey = elevenLabsApiKey || process.env.ELEVENLABS_API_KEY;
18
+ // S3 configuration
19
+ const s3Endpoint = options.s3Endpoint || process.env.S3_ENDPOINT;
20
+ const s3Region = options.s3Region || process.env.S3_REGION || 'auto';
21
+ const s3Bucket = options.s3Bucket || process.env.S3_BUCKET;
22
+ const s3AccessKeyId = options.s3AccessKeyId || process.env.S3_ACCESS_KEY_ID;
23
+ const s3SecretAccessKey = options.s3SecretAccessKey || process.env.S3_SECRET_ACCESS_KEY;
24
+ if (!muxId || !muxSecret) {
25
+ throw new Error('Mux credentials are required. Provide muxTokenId and muxTokenSecret in options or set MUX_TOKEN_ID and MUX_TOKEN_SECRET environment variables.');
26
+ }
27
+ if (!elevenLabsKey) {
28
+ throw new Error('ElevenLabs API key is required. Provide elevenLabsApiKey in options or set ELEVENLABS_API_KEY environment variable.');
29
+ }
30
+ if (uploadToMux && (!s3Endpoint || !s3Bucket || !s3AccessKeyId || !s3SecretAccessKey)) {
31
+ throw new Error('S3 configuration is required for uploading to Mux. Provide s3Endpoint, s3Bucket, s3AccessKeyId, and s3SecretAccessKey in options or set S3_ENDPOINT, S3_BUCKET, S3_ACCESS_KEY_ID, and S3_SECRET_ACCESS_KEY environment variables.');
32
+ }
33
+ // Initialize clients
34
+ const mux = new Mux({
35
+ tokenId: muxId,
36
+ tokenSecret: muxSecret,
37
+ });
38
+ // Fetch asset data from Mux
39
+ console.log(`🎬 Fetching Mux asset: ${assetId}`);
40
+ let assetData;
41
+ try {
42
+ const asset = await mux.video.assets.retrieve(assetId);
43
+ assetData = asset;
44
+ }
45
+ catch (error) {
46
+ throw new Error(`Failed to fetch asset from Mux: ${error instanceof Error ? error.message : 'Unknown error'}`);
47
+ }
48
+ // Check for audio-only static rendition
49
+ console.log('🔍 Checking for audio-only static rendition...');
50
+ if (!assetData.static_renditions || !assetData.static_renditions.files) {
51
+ throw new Error('No static renditions found for this asset');
52
+ }
53
+ const staticRenditionFiles = assetData.static_renditions.files;
54
+ if (staticRenditionFiles.length === 0) {
55
+ throw new Error('No static rendition files found for this asset');
56
+ }
57
+ const audioRendition = staticRenditionFiles.find((rendition) => rendition.name === 'audio.m4a' && rendition.status === 'ready');
58
+ if (!audioRendition) {
59
+ throw new Error('No ready audio-only static rendition found for this asset. Please ensure the asset has an audio.m4a static rendition.');
60
+ }
61
+ const audioUrl = `https://stream.mux.com/${assetData.playback_ids?.[0]?.id}/audio.m4a`;
62
+ console.log(`✅ Found audio rendition: ${audioUrl}`);
63
+ // Create dubbing job in ElevenLabs
64
+ console.log(`🎙️ Creating ElevenLabs dubbing job (auto-detect → ${toLanguageCode})`);
65
+ let dubbingId;
66
+ try {
67
+ // Fetch audio file and create dubbing job
68
+ const audioResponse = await fetch(audioUrl);
69
+ if (!audioResponse.ok) {
70
+ throw new Error(`Failed to fetch audio file: ${audioResponse.statusText}`);
71
+ }
72
+ const audioBuffer = await audioResponse.arrayBuffer();
73
+ const audioBlob = new Blob([audioBuffer], { type: 'audio/mp4' });
74
+ const audioFile = audioBlob; // ElevenLabs accepts Blob
75
+ // Create dubbing job using direct HTTP request
76
+ const formData = new FormData();
77
+ formData.append('file', audioFile);
78
+ formData.append('target_lang', toLanguageCode);
79
+ // Note: source_lang is omitted to enable automatic language detection
80
+ formData.append('num_speakers', numSpeakers.toString());
81
+ formData.append('name', `Mux Asset ${assetId} - auto to ${toLanguageCode}`);
82
+ const dubbingResponse = await fetch('https://api.elevenlabs.io/v1/dubbing', {
83
+ method: 'POST',
84
+ headers: {
85
+ 'xi-api-key': elevenLabsKey
86
+ },
87
+ body: formData
88
+ });
89
+ if (!dubbingResponse.ok) {
90
+ throw new Error(`ElevenLabs API error: ${dubbingResponse.statusText}`);
91
+ }
92
+ const dubbingData = await dubbingResponse.json();
93
+ dubbingId = dubbingData.dubbing_id;
94
+ console.log(`✅ Dubbing job created: ${dubbingId}`);
95
+ console.log(`⏱️ Expected duration: ${dubbingData.expected_duration_sec}s`);
96
+ }
97
+ catch (error) {
98
+ throw new Error(`Failed to create ElevenLabs dubbing job: ${error instanceof Error ? error.message : 'Unknown error'}`);
99
+ }
100
+ // Poll for completion
101
+ console.log('⏳ Waiting for dubbing to complete...');
102
+ let dubbingStatus = 'dubbing';
103
+ let pollAttempts = 0;
104
+ const maxPollAttempts = 180; // 30 minutes at 10s intervals
105
+ while (dubbingStatus === 'dubbing' && pollAttempts < maxPollAttempts) {
106
+ await new Promise(resolve => setTimeout(resolve, 10000)); // Wait 10 seconds
107
+ pollAttempts++;
108
+ try {
109
+ const statusResponse = await fetch(`https://api.elevenlabs.io/v1/dubbing/${dubbingId}`, {
110
+ headers: {
111
+ 'xi-api-key': elevenLabsKey
112
+ }
113
+ });
114
+ if (!statusResponse.ok) {
115
+ throw new Error(`Status check failed: ${statusResponse.statusText}`);
116
+ }
117
+ const statusData = await statusResponse.json();
118
+ dubbingStatus = statusData.status;
119
+ console.log(`📊 Status check ${pollAttempts}: ${dubbingStatus}`);
120
+ if (dubbingStatus === 'failed') {
121
+ throw new Error('ElevenLabs dubbing job failed');
122
+ }
123
+ }
124
+ catch (error) {
125
+ throw new Error(`Failed to check dubbing status: ${error instanceof Error ? error.message : 'Unknown error'}`);
126
+ }
127
+ }
128
+ if (dubbingStatus !== 'dubbed') {
129
+ throw new Error(`Dubbing job timed out or failed. Final status: ${dubbingStatus}`);
130
+ }
131
+ console.log('✅ Dubbing completed successfully!');
132
+ // If uploadToMux is false, just return the dubbing info
133
+ if (!uploadToMux) {
134
+ return {
135
+ assetId,
136
+ targetLanguageCode: toLanguageCode,
137
+ dubbingId
138
+ };
139
+ }
140
+ // Download dubbed audio from ElevenLabs
141
+ console.log('📥 Downloading dubbed audio from ElevenLabs...');
142
+ let dubbedAudioBuffer;
143
+ try {
144
+ // Get dubbed audio using fetch (since the SDK method might not be available)
145
+ const audioUrl = `https://api.elevenlabs.io/v1/dubbing/${dubbingId}/audio/${toLanguageCode}`;
146
+ const audioResponse = await fetch(audioUrl, {
147
+ headers: {
148
+ 'xi-api-key': elevenLabsKey
149
+ }
150
+ });
151
+ if (!audioResponse.ok) {
152
+ throw new Error(`Failed to fetch dubbed audio: ${audioResponse.statusText}`);
153
+ }
154
+ dubbedAudioBuffer = await audioResponse.arrayBuffer();
155
+ console.log(`✅ Downloaded dubbed audio (${dubbedAudioBuffer.byteLength} bytes)`);
156
+ }
157
+ catch (error) {
158
+ throw new Error(`Failed to download dubbed audio: ${error instanceof Error ? error.message : 'Unknown error'}`);
159
+ }
160
+ // Upload to S3-compatible storage
161
+ console.log('📤 Uploading dubbed audio to S3-compatible storage...');
162
+ const s3Client = new S3Client({
163
+ region: s3Region,
164
+ endpoint: s3Endpoint,
165
+ credentials: {
166
+ accessKeyId: s3AccessKeyId,
167
+ secretAccessKey: s3SecretAccessKey
168
+ },
169
+ forcePathStyle: true
170
+ });
171
+ // Create unique key for the audio file
172
+ const audioKey = `audio-translations/${assetId}/auto-to-${toLanguageCode}-${Date.now()}.m4a`;
173
+ let presignedUrl;
174
+ try {
175
+ // Upload audio to S3
176
+ const upload = new Upload({
177
+ client: s3Client,
178
+ params: {
179
+ Bucket: s3Bucket,
180
+ Key: audioKey,
181
+ Body: new Uint8Array(dubbedAudioBuffer),
182
+ ContentType: 'audio/mp4'
183
+ }
184
+ });
185
+ await upload.done();
186
+ console.log(`✅ Audio uploaded successfully to: ${audioKey}`);
187
+ // Generate presigned URL (valid for 1 hour)
188
+ const getObjectCommand = new GetObjectCommand({
189
+ Bucket: s3Bucket,
190
+ Key: audioKey
191
+ });
192
+ presignedUrl = await getSignedUrl(s3Client, getObjectCommand, {
193
+ expiresIn: 3600 // 1 hour
194
+ });
195
+ console.log(`🔗 Generated presigned URL (expires in 1 hour)`);
196
+ }
197
+ catch (error) {
198
+ throw new Error(`Failed to upload audio to S3: ${error instanceof Error ? error.message : 'Unknown error'}`);
199
+ }
200
+ // Add translated audio track to Mux asset
201
+ console.log('🎬 Adding translated audio track to Mux asset...');
202
+ let uploadedTrackId;
203
+ try {
204
+ const languageName = new Intl.DisplayNames(['en'], { type: 'language' }).of(toLanguageCode) || toLanguageCode.toUpperCase();
205
+ const trackName = `${languageName} (auto-dubbed)`;
206
+ const trackResponse = await mux.video.assets.createTrack(assetId, {
207
+ type: 'audio',
208
+ language_code: toLanguageCode,
209
+ name: trackName,
210
+ url: presignedUrl
211
+ });
212
+ uploadedTrackId = trackResponse.id;
213
+ console.log(`✅ Audio track added to Mux asset with ID: ${uploadedTrackId}`);
214
+ console.log(`🎵 Track name: "${trackName}"`);
215
+ }
216
+ catch (error) {
217
+ console.warn(`⚠️ Failed to add audio track to Mux asset: ${error instanceof Error ? error.message : 'Unknown error'}`);
218
+ console.log('🔗 You can manually add the track using this presigned URL:');
219
+ console.log(presignedUrl);
220
+ }
221
+ return {
222
+ assetId,
223
+ targetLanguageCode: toLanguageCode,
224
+ dubbingId,
225
+ uploadedTrackId,
226
+ presignedUrl
227
+ };
228
+ }
229
+ //# sourceMappingURL=audio-translation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audio-translation.js","sourceRoot":"","sources":["../src/audio-translation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import { MuxAIOptions } from './types';
2
+ import { ImageDownloadOptions } from './utils/image-download';
3
+ export interface BurnedInCaptionsResult {
4
+ assetId: string;
5
+ hasBurnedInCaptions: boolean;
6
+ confidence: number;
7
+ detectedLanguage: string | null;
8
+ storyboardUrl?: string;
9
+ }
10
+ export interface BurnedInCaptionsOptions extends MuxAIOptions {
11
+ provider?: 'openai' | 'anthropic';
12
+ model?: string;
13
+ /** Method for submitting storyboard to AI providers (default: 'url') */
14
+ imageSubmissionMode?: 'url' | 'base64';
15
+ /** Options for image download when using base64 submission mode */
16
+ imageDownloadOptions?: ImageDownloadOptions;
17
+ }
18
+ export declare function hasBurnedInCaptions(assetId: string, options?: BurnedInCaptionsOptions): Promise<BurnedInCaptionsResult>;
19
+ //# sourceMappingURL=burned-in-captions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"burned-in-captions.d.ts","sourceRoot":"","sources":["../src/burned-in-captions.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAQ9D,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,uBAAwB,SAAQ,YAAY;IAC3D,QAAQ,CAAC,EAAE,QAAQ,GAAG,WAAW,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,mBAAmB,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;IACvC,mEAAmE;IACnE,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;CAC7C;AAkGD,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,sBAAsB,CAAC,CAgLjC"}
@@ -0,0 +1,243 @@
1
+ import { z } from 'zod';
2
+ import { zodTextFormat } from 'openai/helpers/zod';
3
+ import { getAssetInfo, processStoryboardWithAnthropic } from './utils/storyboard-processor';
4
+ const burnedInCaptionsSchema = z.object({
5
+ hasBurnedInCaptions: z.boolean(),
6
+ confidence: z.number().min(0).max(1),
7
+ detectedLanguage: z.string().nullable()
8
+ });
9
+ const DEFAULT_SYSTEM_PROMPT = `You are an expert at analyzing video frames to detect burned-in captions (also called open captions or hardcoded subtitles). These are text overlays that are permanently embedded in the video image, common on TikTok, Instagram Reels, and other social media platforms.
10
+
11
+ CRITICAL: Burned-in captions must appear consistently across MOST frames in the storyboard. Text appearing in only 1-2 frames at the end is typically marketing copy, taglines, or end-cards - NOT burned-in captions.
12
+
13
+ Analyze the provided video storyboard by:
14
+ 1. COUNT how many frames contain text overlays vs. how many don't
15
+ 2. Check if text appears in consistent positions across multiple frames
16
+ 3. Verify text changes content between frames (indicating dialogue/narration)
17
+ 4. Ensure text has caption-style formatting (contrasting colors, readable fonts)
18
+
19
+ ONLY classify as burned-in captions if:
20
+ - Text appears in multiple frames (not just 1-2 end frames)
21
+ - Text positioning is consistent across those frames
22
+ - Content suggests dialogue, narration, or subtitles (not marketing)
23
+ - Formatting looks like captions (not graphics/logos)
24
+
25
+ DO NOT classify as burned-in captions:
26
+ - Marketing taglines appearing only in final 1-2 frames
27
+ - Single words or phrases that don't change between frames
28
+ - Graphics, logos, watermarks, or UI elements
29
+ - Text that's part of the original scene content
30
+ - End-cards with calls-to-action or brand messaging
31
+
32
+ If you detect burned-in captions, try to identify the language of the text.`;
33
+ const ANTHROPIC_SYSTEM_PROMPT = `You are an expert at analyzing video frames to detect burned-in captions (also called open captions or hardcoded subtitles). These are text overlays permanently embedded in video images, common on social media platforms.
34
+
35
+ Key principles:
36
+ 1. Burned-in captions appear across multiple frames throughout the video timeline
37
+ 2. End-cards and marketing text appear only in final frames
38
+ 3. Captions have consistent positioning and caption-style formatting
39
+ 4. Caption text typically changes between frames (dialogue/narration)
40
+
41
+ Analysis approach:
42
+ - Look for text overlays distributed across different parts of the timeline
43
+ - Distinguish between dialogue captions vs. marketing end-cards
44
+ - Consider text positioning, formatting, and content patterns`;
45
+ const DEFAULT_USER_PROMPT = `Analyze this video storyboard for burned-in captions. Follow this systematic approach:
46
+
47
+ STEP 1: Count the frames
48
+ - How many total frames are shown in this storyboard?
49
+ - How many frames contain any text overlays?
50
+ - What percentage of frames contain text?
51
+
52
+ STEP 2: Analyze text consistency
53
+ - If text is present, does it appear in the same position across multiple frames?
54
+ - Does the text content change between frames (suggesting dialogue)?
55
+ - Or is it the same text in just 1-2 frames (suggesting marketing/end-card)?
56
+
57
+ STEP 3: Classification
58
+ - Are there burned-in captions (text overlaid that appears to be subtitles/captions)?
59
+ - How confident are you (0.0 to 1.0)? Be decisive and accurate:
60
+ * If clear dialogue/caption text across multiple frames → 0.8+ confidence, TRUE
61
+ * If ONLY marketing text in final frames → 0.0 confidence, FALSE
62
+ * If truly uncertain → 0.3-0.5 confidence
63
+ - If captions are present, what language?
64
+
65
+ REMEMBER: Marketing taglines in final frames = NOT captions (0.0 confidence, FALSE). Dialogue text across timeline = captions (0.8+ confidence, TRUE).
66
+
67
+ Respond with your analysis.`;
68
+ const ANTHROPIC_USER_PROMPT = `Analyze this storyboard for burned-in captions:
69
+
70
+ 1. Examine each frame from left to right (timeline order)
71
+ 2. Note which frames have text overlays and their positions
72
+ 3. Determine the pattern:
73
+ - Text scattered across timeline = likely captions
74
+ - Text only in final 1-2 frames = likely end-card/marketing
75
+
76
+ Classification rules:
77
+ - If text appears in 3+ frames distributed throughout timeline → burned-in captions
78
+ - If text appears only in final frames → NOT burned-in captions
79
+ - Look for dialogue-style content vs. marketing taglines
80
+
81
+ Analyze and classify with confidence level.`;
82
+ const ANTHROPIC_JSON_PROMPT = `Apply the frame analysis above.
83
+
84
+ Key rule: Text appearing only in final 2-3 frames = NOT captions. Text distributed throughout timeline = captions.
85
+
86
+ Respond ONLY with valid JSON:
87
+ {
88
+ "hasBurnedInCaptions": true/false,
89
+ "confidence": 0.85,
90
+ "detectedLanguage": "English" (or null if no captions or language unclear)
91
+ }
92
+
93
+ Do not include any text before or after the JSON. The JSON must be valid and parseable.`;
94
+ export async function hasBurnedInCaptions(assetId, options = {}) {
95
+ const { provider = 'openai', model, imageSubmissionMode = 'url', imageDownloadOptions, muxTokenId, muxTokenSecret, openaiApiKey, anthropicApiKey, ...config } = options;
96
+ // Set default models based on provider
97
+ const defaultModel = provider === 'anthropic' ? 'claude-3-5-haiku-20241022' : 'gpt-4o-mini';
98
+ const finalModel = model || defaultModel;
99
+ // Validate required credentials
100
+ const openaiKey = openaiApiKey || process.env.OPENAI_API_KEY;
101
+ const anthropicKey = anthropicApiKey || process.env.ANTHROPIC_API_KEY;
102
+ if (provider === 'openai' && !openaiKey) {
103
+ throw new Error('OpenAI API key is required for OpenAI provider. Provide openaiApiKey in options or set OPENAI_API_KEY environment variable.');
104
+ }
105
+ if (provider === 'anthropic' && !anthropicKey) {
106
+ throw new Error('Anthropic API key is required for Anthropic provider. Provide anthropicApiKey in options or set ANTHROPIC_API_KEY environment variable.');
107
+ }
108
+ // Get asset information
109
+ const storyboardOptions = {
110
+ muxTokenId,
111
+ muxTokenSecret,
112
+ openaiApiKey,
113
+ anthropicApiKey,
114
+ imageSubmissionMode,
115
+ imageDownloadOptions
116
+ };
117
+ const assetInfo = await getAssetInfo(assetId, storyboardOptions);
118
+ const imageUrl = `https://image.mux.com/${assetInfo.playbackId}/storyboard.png?width=640`;
119
+ let analysisResult = null;
120
+ if (provider === 'openai') {
121
+ // Handle OpenAI with structured outputs directly
122
+ const OpenAI = require('openai').default;
123
+ const openaiClient = new OpenAI({ apiKey: openaiKey });
124
+ if (imageSubmissionMode === 'base64') {
125
+ const { downloadImageAsBase64 } = require('./utils/image-download');
126
+ const downloadResult = await downloadImageAsBase64(imageUrl, imageDownloadOptions);
127
+ const response = await openaiClient.responses.parse({
128
+ model: finalModel,
129
+ input: [
130
+ {
131
+ role: "system",
132
+ content: ANTHROPIC_SYSTEM_PROMPT,
133
+ },
134
+ {
135
+ role: "user",
136
+ content: [
137
+ {
138
+ type: "input_text",
139
+ text: ANTHROPIC_USER_PROMPT,
140
+ },
141
+ {
142
+ type: "input_image",
143
+ image_url: downloadResult.base64Data,
144
+ detail: "high",
145
+ },
146
+ ],
147
+ },
148
+ ],
149
+ text: {
150
+ format: zodTextFormat(burnedInCaptionsSchema, "analysis"),
151
+ },
152
+ });
153
+ analysisResult = response.output_parsed;
154
+ }
155
+ else {
156
+ // URL-based submission with retry logic for structured outputs
157
+ let retryAttempt = 0;
158
+ const maxRetries = 3;
159
+ while (retryAttempt <= maxRetries) {
160
+ try {
161
+ const response = await openaiClient.responses.parse({
162
+ model: finalModel,
163
+ input: [
164
+ {
165
+ role: "system",
166
+ content: ANTHROPIC_SYSTEM_PROMPT,
167
+ },
168
+ {
169
+ role: "user",
170
+ content: [
171
+ {
172
+ type: "input_text",
173
+ text: ANTHROPIC_USER_PROMPT,
174
+ },
175
+ {
176
+ type: "input_image",
177
+ image_url: imageUrl,
178
+ detail: "high",
179
+ },
180
+ ],
181
+ },
182
+ ],
183
+ text: {
184
+ format: zodTextFormat(burnedInCaptionsSchema, "analysis"),
185
+ },
186
+ });
187
+ analysisResult = response.output_parsed;
188
+ break;
189
+ }
190
+ catch (error) {
191
+ const isTimeoutError = error instanceof Error && error.message && error.message.includes('Timeout while downloading');
192
+ if (isTimeoutError && retryAttempt < maxRetries) {
193
+ await new Promise(resolve => setTimeout(resolve, 5000));
194
+ retryAttempt++;
195
+ continue;
196
+ }
197
+ throw new Error(`Failed to analyze storyboard with OpenAI: ${error instanceof Error ? error.message : 'Unknown error'}`);
198
+ }
199
+ }
200
+ }
201
+ }
202
+ else if (provider === 'anthropic') {
203
+ const anthropicPrompt = `${ANTHROPIC_USER_PROMPT}
204
+
205
+ ${ANTHROPIC_JSON_PROMPT}`;
206
+ const responseParser = (response) => {
207
+ const content = response.content[0];
208
+ if (content.type === 'text') {
209
+ const jsonText = content.text.trim();
210
+ try {
211
+ return JSON.parse(jsonText);
212
+ }
213
+ catch (parseError) {
214
+ throw new Error(`Failed to parse JSON response from Anthropic: ${parseError instanceof Error ? parseError.message : 'Unknown error'}`);
215
+ }
216
+ }
217
+ else {
218
+ throw new Error('Unexpected response type from Anthropic');
219
+ }
220
+ };
221
+ analysisResult = await processStoryboardWithAnthropic(imageUrl, anthropicPrompt, {
222
+ apiKey: anthropicKey,
223
+ model: finalModel,
224
+ responseParser,
225
+ imageSubmissionMode,
226
+ imageDownloadOptions
227
+ });
228
+ }
229
+ else {
230
+ throw new Error(`Unsupported provider: ${provider}`);
231
+ }
232
+ if (!analysisResult) {
233
+ throw new Error('No analysis result received from AI provider');
234
+ }
235
+ return {
236
+ assetId,
237
+ hasBurnedInCaptions: analysisResult.hasBurnedInCaptions ?? false,
238
+ confidence: analysisResult.confidence ?? 0,
239
+ detectedLanguage: analysisResult.detectedLanguage ?? null,
240
+ storyboardUrl: imageUrl,
241
+ };
242
+ }
243
+ //# sourceMappingURL=burned-in-captions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"burned-in-captions.js","sourceRoot":"","sources":["../src/burned-in-captions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGnD,OAAO,EACL,YAAY,EAEZ,8BAA8B,EAE/B,MAAM,8BAA8B,CAAC;AAmBtC,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,mBAAmB,EAAE,CAAC,CAAC,OAAO,EAAE;IAChC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACpC,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACxC,CAAC,CAAC;AAEH,MAAM,qBAAqB,GAAG;;;;;;;;;;;;;;;;;;;;;;;4EAuB8C,CAAC;AAE7E,MAAM,uBAAuB,GAAG;;;;;;;;;;;8DAW8B,CAAC;AAE/D,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;;;;;;;;;;4BAsBA,CAAC;AAE7B,MAAM,qBAAqB,GAAG;;;;;;;;;;;;;4CAac,CAAC;AAE7C,MAAM,qBAAqB,GAAG;;;;;;;;;;;wFAW0D,CAAC;AAEzF,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,OAAe,EACf,UAAmC,EAAE;IAErC,MAAM,EACJ,QAAQ,GAAG,QAAQ,EACnB,KAAK,EACL,mBAAmB,GAAG,KAAK,EAC3B,oBAAoB,EACpB,UAAU,EACV,cAAc,EACd,YAAY,EACZ,eAAe,EACf,GAAG,MAAM,EACV,GAAG,OAAO,CAAC;IAEZ,uCAAuC;IACvC,MAAM,YAAY,GAAG,QAAQ,KAAK,WAAW,CAAC,CAAC,CAAC,2BAA2B,CAAC,CAAC,CAAC,aAAa,CAAC;IAC5F,MAAM,UAAU,GAAG,KAAK,IAAI,YAAY,CAAC;IAEzC,gCAAgC;IAChC,MAAM,SAAS,GAAG,YAAY,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IAC7D,MAAM,YAAY,GAAG,eAAe,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IAEtE,IAAI,QAAQ,KAAK,QAAQ,IAAI,CAAC,SAAS,EAAE,CAAC;QACxC,MAAM,IAAI,KAAK,CAAC,6HAA6H,CAAC,CAAC;IACjJ,CAAC;IAED,IAAI,QAAQ,KAAK,WAAW,IAAI,CAAC,YAAY,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CAAC,yIAAyI,CAAC,CAAC;IAC7J,CAAC;IAED,wBAAwB;IACxB,MAAM,iBAAiB,GAA+B;QACpD,UAAU;QACV,cAAc;QACd,YAAY;QACZ,eAAe;QACf,mBAAmB;QACnB,oBAAoB;KACrB,CAAC;IAEF,MAAM,SAAS,GAAG,MAAM,YAAY,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC;IACjE,MAAM,QAAQ,GAAG,yBAAyB,SAAS,CAAC,UAAU,2BAA2B,CAAC;IAE1F,IAAI,cAAc,GAAoG,IAAI,CAAC;IAE3H,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC1B,iDAAiD;QACjD,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC;QACzC,MAAM,YAAY,GAAG,IAAI,MAAM,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;QAEvD,IAAI,mBAAmB,KAAK,QAAQ,EAAE,CAAC;YACrC,MAAM,EAAE,qBAAqB,EAAE,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAAC;YACpE,MAAM,cAAc,GAAG,MAAM,qBAAqB,CAAC,QAAQ,EAAE,oBAAoB,CAAC,CAAC;YAEnF,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,SAAS,CAAC,KAAK,CAAC;gBAClD,KAAK,EAAE,UAAU;gBACjB,KAAK,EAAE;oBACL;wBACE,IAAI,EAAE,QAAQ;wBACd,OAAO,EAAE,uBAAuB;qBACjC;oBACD;wBACE,IAAI,EAAE,MAAM;wBACZ,OAAO,EAAE;4BACP;gCACE,IAAI,EAAE,YAAY;gCAClB,IAAI,EAAE,qBAAqB;6BAC5B;4BACD;gCACE,IAAI,EAAE,aAAa;gCACnB,SAAS,EAAE,cAAc,CAAC,UAAU;gCACpC,MAAM,EAAE,MAAM;6BACf;yBACF;qBACF;iBACF;gBACD,IAAI,EAAE;oBACJ,MAAM,EAAE,aAAa,CAAC,sBAAsB,EAAE,UAAU,CAAC;iBAC1D;aACF,CAAC,CAAC;YAEH,cAAc,GAAG,QAAQ,CAAC,aAAa,CAAC;QAC1C,CAAC;aAAM,CAAC;YACN,+DAA+D;YAC/D,IAAI,YAAY,GAAG,CAAC,CAAC;YACrB,MAAM,UAAU,GAAG,CAAC,CAAC;YAErB,OAAO,YAAY,IAAI,UAAU,EAAE,CAAC;gBAClC,IAAI,CAAC;oBACH,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,SAAS,CAAC,KAAK,CAAC;wBAClD,KAAK,EAAE,UAAU;wBACjB,KAAK,EAAE;4BACL;gCACE,IAAI,EAAE,QAAQ;gCACd,OAAO,EAAE,uBAAuB;6BACjC;4BACD;gCACE,IAAI,EAAE,MAAM;gCACZ,OAAO,EAAE;oCACP;wCACE,IAAI,EAAE,YAAY;wCAClB,IAAI,EAAE,qBAAqB;qCAC5B;oCACD;wCACE,IAAI,EAAE,aAAa;wCACnB,SAAS,EAAE,QAAQ;wCACnB,MAAM,EAAE,MAAM;qCACf;iCACF;6BACF;yBACF;wBACD,IAAI,EAAE;4BACJ,MAAM,EAAE,aAAa,CAAC,sBAAsB,EAAE,UAAU,CAAC;yBAC1D;qBACF,CAAC,CAAC;oBAEH,cAAc,GAAG,QAAQ,CAAC,aAAa,CAAC;oBACxC,MAAM;gBAER,CAAC;gBAAC,OAAO,KAAc,EAAE,CAAC;oBACxB,MAAM,cAAc,GAAG,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,2BAA2B,CAAC,CAAC;oBAEtH,IAAI,cAAc,IAAI,YAAY,GAAG,UAAU,EAAE,CAAC;wBAChD,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;wBACxD,YAAY,EAAE,CAAC;wBACf,SAAS;oBACX,CAAC;oBAED,MAAM,IAAI,KAAK,CAAC,6CAA6C,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC;gBAC3H,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;SAAM,IAAI,QAAQ,KAAK,WAAW,EAAE,CAAC;QACpC,MAAM,eAAe,GAAG,GAAG,qBAAqB;;EAElD,qBAAqB,EAAE,CAAC;QAEtB,MAAM,cAAc,GAAG,CAAC,QAAa,EAAE,EAAE;YACvC,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YACpC,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBAC5B,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;gBACrC,IAAI,CAAC;oBACH,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;gBAC9B,CAAC;gBAAC,OAAO,UAAU,EAAE,CAAC;oBACpB,MAAM,IAAI,KAAK,CAAC,iDAAiD,UAAU,YAAY,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC;gBACzI,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;YAC7D,CAAC;QACH,CAAC,CAAC;QAEF,cAAc,GAAG,MAAM,8BAA8B,CACnD,QAAQ,EACR,eAAe,EACf;YACE,MAAM,EAAE,YAAa;YACrB,KAAK,EAAE,UAAU;YACjB,cAAc;YACd,mBAAmB;YACnB,oBAAoB;SACrB,CACF,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,MAAM,IAAI,KAAK,CAAC,yBAAyB,QAAQ,EAAE,CAAC,CAAC;IACvD,CAAC;IAED,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAClE,CAAC;IAED,OAAO;QACL,OAAO;QACP,mBAAmB,EAAE,cAAc,CAAC,mBAAmB,IAAI,KAAK;QAChE,UAAU,EAAE,cAAc,CAAC,UAAU,IAAI,CAAC;QAC1C,gBAAgB,EAAE,cAAc,CAAC,gBAAgB,IAAI,IAAI;QACzD,aAAa,EAAE,QAAQ;KACxB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { MuxAIOptions } from './types';
2
+ export interface Chapter {
3
+ /** Start time in seconds */
4
+ startTime: number;
5
+ /** Chapter title */
6
+ title: string;
7
+ }
8
+ export interface ChaptersResult {
9
+ assetId: string;
10
+ languageCode: string;
11
+ chapters: Chapter[];
12
+ }
13
+ export interface ChaptersOptions extends MuxAIOptions {
14
+ provider?: 'openai' | 'anthropic';
15
+ model?: string;
16
+ }
17
+ export declare function generateChapters(assetId: string, languageCode: string, options?: ChaptersOptions): Promise<ChaptersResult>;
18
+ //# sourceMappingURL=chapters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chapters.d.ts","sourceRoot":"","sources":["../src/chapters.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,MAAM,WAAW,OAAO;IACtB,4BAA4B;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,oBAAoB;IACpB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,eAAgB,SAAQ,YAAY;IACnD,QAAQ,CAAC,EAAE,QAAQ,GAAG,WAAW,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA8FD,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,cAAc,CAAC,CAoMzB"}