samsar-js 0.48.32 → 0.48.34

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 CHANGED
@@ -37,6 +37,10 @@ const video = await samsar.createVideoFromText(
37
37
  image_model: 'GPTIMAGE2',
38
38
  video_model: 'RUNWAYML',
39
39
  duration: 30,
40
+ backingtrack_model: 'LYRIA3',
41
+ tts_model: 'OPENAI',
42
+ speakerOptions: { openAISpeakers: ['nova'] },
43
+ inference_model: 'gpt-5.5',
40
44
  font_key: 'Poppins',
41
45
  enable_subtitles: true,
42
46
  },
@@ -67,6 +71,10 @@ const videoFromImages = await samsar.createVideoFromImageList(
67
71
  video_model: 'RUNWAYML',
68
72
  aspect_ratio: '16:9',
69
73
  language: 'en',
74
+ backingtrack_model: 'ELEVENLABS_MUSIC',
75
+ tts_model: 'ELEVENLABS',
76
+ speakerOptions: { elevenLabsSpeakers: ['EXAVITQu4vr4xnSDxMaL'] },
77
+ inference_model: 'gemini-3.1-pro',
70
78
  font_key: 'Poppins',
71
79
  enable_subtitles: true,
72
80
  },
@@ -107,6 +115,20 @@ await samsar.createVideoFromImageList({
107
115
  ],
108
116
  });
109
117
 
118
+ // Create a video and generate the outro with a center logo/CTA image instead of a QR code
119
+ await samsar.createVideoFromImageList({
120
+ image_urls: ['https://example.com/a.jpg', 'https://example.com/b.jpg'],
121
+ prompt: 'Product launch teaser with a branded CTA outro',
122
+ video_model: 'RUNWAYML',
123
+ aspect_ratio: '9:16',
124
+ generate_outro_image: true,
125
+ outro_cta_image: {
126
+ top_text: 'Shop the drop',
127
+ middle_image: { url: 'https://cdn.example.com/drop-logo.png' },
128
+ bottom_text: 'Limited availability',
129
+ },
130
+ });
131
+
110
132
  // Create the same generated QR outro and per-scene footer CTAs from one CTA link
111
133
  await samsar.createVideoFromImageList({
112
134
  image_urls: [
@@ -146,6 +168,20 @@ await samsar.updateVideoOutroImage(
146
168
  { webhookUrl: 'https://example.com/webhook' },
147
169
  );
148
170
 
171
+ // Update an existing outro by generating a new CTA outro with a center logo/CTA image
172
+ await samsar.updateVideoOutroImage(
173
+ {
174
+ videoSessionId: videoFromImages.data.session_id ?? videoFromImages.data.request_id!,
175
+ generate_outro_image: true,
176
+ outro_cta_image: {
177
+ top_text: 'Shop the drop',
178
+ middle_image: { url: 'https://cdn.example.com/drop-logo.png' },
179
+ bottom_text: 'Limited availability',
180
+ },
181
+ },
182
+ { webhookUrl: 'https://example.com/webhook' },
183
+ );
184
+
149
185
  // Update the footer CTA on an existing video session
150
186
  await samsar.updateVideoFooterImage(
151
187
  {
@@ -489,13 +525,15 @@ const externalRender = await platform.createExternalVideoFromText(externalUser,
489
525
  enable_subtitles: true,
490
526
  });
491
527
 
492
- // Update an external user's existing outro by generating a new QR CTA outro
528
+ // Update an external user's existing outro by generating a center logo/CTA image outro
493
529
  const externalOutroUpdate = await platform.updateExternalVideoOutroImage(externalUser, {
494
530
  request_id: externalRender.data.request_id,
495
531
  generate_outro_image: true,
496
- cta_url: 'https://example.com/shop',
497
- cta_text_top: 'Scan to shop',
498
- cta_text_bottom: 'Limited drop',
532
+ outro_cta_image: {
533
+ top_text: 'Shop the drop',
534
+ middle_image: { url: 'https://cdn.example.com/drop-logo.png' },
535
+ bottom_text: 'Limited availability',
536
+ },
499
537
  add_outro_animation: true,
500
538
  });
501
539
  console.log(externalOutroUpdate.data.request_id);
@@ -574,14 +612,17 @@ console.log(externalLibrary.data.requests.map((request) => request.request_id));
574
612
 
575
613
  Video model support notes:
576
614
  - `createVideoFromText` image model keys include: `GPTIMAGE2`, `IMAGEN4`, `SEEDREAM`, `NANOBANANA2`, `NANOBANANAPRO`, `CUSTOM_TEXT_TO_IMAGE`.
577
- - `createVideoFromText` supports these video models: `RUNWAYML`, `VEO3.1I2V`, `VEO3.1I2VFAST`, `SEEDANCEI2V` (Seedance 1.5), `KLINGIMGTOVID3PRO`, and `HAPPYHORSEI2V`.
578
- - `createVideoFromText` accepts either a provided outro (`outro_image_url`) or server-generated QR outro (`generate_outro_image: true` with `cta_url`). It can also render bottom CTA footer QR cards with `add_footer_animation` and `footer_metadata`; one footer item applies to every generated scene, while multiple items map by scene index.
579
- - `createVideoFromImageList` supports `RUNWAYML`, `VEO3.1I2V`, `VEO3.1I2VFAST`, `SEEDANCEI2V`, `KLINGIMGTOVID3PRO`, and `HAPPYHORSEI2V` via `video_model`; if omitted, it defaults to `RUNWAYML`. It also accepts the same `image_model` keys as text-to-video. Use `aspect_ratio: '16:9'` or `'9:16'`; omitted or invalid values fall back to `16:9`.
580
- - `createVideoFromImageList` accepts either a provided outro (`outro_image_url`) or server-generated QR outro (`generate_outro_image: true` with `cta_url`). Do not combine the two modes in a single request.
615
+ - `createVideoFromText` supports these video models: `RUNWAYML`, `VEO3.1I2V`, `VEO3.1I2VFAST`, `COSMOS3SUPERI2V`, `SEEDANCEI2V` (Seedance 1.5), `KLINGIMGTOVID3PRO`, and `HAPPYHORSEI2V`.
616
+ - `createVideoFromText` accepts either a provided outro (`outro_image_url`) or a server-generated CTA outro (`generate_outro_image: true` with `cta_url` for a QR center image, or `outro_cta_image` for a supplied center logo/CTA image). It can also render bottom CTA footer QR cards with `add_footer_animation` and `footer_metadata`; one footer item applies to every generated scene, while multiple items map by scene index.
617
+ - `createVideoFromImageList` supports `RUNWAYML`, `VEO3.1I2V`, `VEO3.1I2VFAST`, `COSMOS3SUPERI2V`, `SEEDANCEI2V`, `KLINGIMGTOVID3PRO`, and `HAPPYHORSEI2V` via `video_model`; if omitted, it defaults to `RUNWAYML`. It also accepts the same `image_model` keys as text-to-video. Use `aspect_ratio: '16:9'` or `'9:16'`; omitted or invalid values fall back to `16:9`.
618
+ - Text and image-list video creation both accept optional `backingtrack_model` / `backing_track_model` / `backingTrackModel` / `music_provider` / `musicProvider`, `tts_model` / `ttsModel` / `tts_provider` / `ttsProvider`, `speakerOptions` / `speaker_options`, and `inference_model` / `inferenceModel`. The adapter normalizes these to `backingtrack_model`, `tts_model`, `speakerOptions`, and `inference_model` in the request payload. Omit `inference_model` to use the account default; supported request values are `gpt-5.5` and `gemini-3.1-pro`. When `tts_model` is set, Samsar limits assignment to the matching speaker list (`openAISpeakers`, `elevenLabsSpeakers`, or `googleSpeakers`; Google TTS requests should include `googleSpeakerDetails`).
619
+ - `video_model_sub_type` is no longer used by the API and is stripped from text and image-list payloads before sending.
620
+ - `createVideoFromImageList` accepts either a provided outro (`outro_image_url`) or a server-generated CTA outro (`generate_outro_image: true` with `cta_url` for a QR center image, or `outro_cta_image` for a supplied center logo/CTA image). Do not combine the two modes in a single request.
621
+ - `outro_cta_image` uses the structured shape `{ top_text, middle_image, bottom_text }`. `middle_image` accepts a public image URL (`{ url }`, `{ image_url }`) or image data (`{ data_url }`, `{ image_data, mime_type }`) and is resized into the same center area used by QR outros without changing aspect ratio.
581
622
  - `createVideoFromImageList` can render per-scene footer QR cards by setting `add_footer_animation: true` and providing one `footer_metadata` item per image scene.
582
623
  - `createVideoFromImageList` can also generate QR outro CTA text and each scene footer CTA from a single link by setting `express_cta_generation: true` with `cta_url`. CamelCase `expressCtaGeneration` and compatibility aliases `auto_generate_cta_text` / `generate_cta_texts` are normalized to the same API field.
583
624
  - `createVideoFromImageList` accepts `limit_single_narrator: true` to keep all narration under one narrator identity. `add_narrator_avatar: true` automatically enables `limit_single_narrator`, generates an influencer-style human narrator avatar, and overlays it bottom-center or centered in the footer row when footer metadata is present.
584
- - `updateVideoOutroImage` accepts either a replacement outro image URL (`outro_image_url`, `outroImageUrl`, `new_outro_image_url`) or a generated QR CTA outro (`generate_outro_image: true` with `cta_url`, or just `cta_url` when no outro image URL is supplied). Generated outro updates reuse the existing session image layers for tiling and only queue frame/video regeneration.
625
+ - `updateVideoOutroImage` accepts either a replacement outro image URL (`outro_image_url`, `outroImageUrl`, `new_outro_image_url`) or a generated CTA outro (`generate_outro_image: true` with `cta_url` or `outro_cta_image`, or just `cta_url` / `outro_cta_image` when no outro image URL is supplied). Generated outro updates reuse the existing session image layers for tiling and only queue frame/video regeneration.
585
626
  - `updateVideoFooterImage` updates the footer CTA on a cloned session with `cta_text`, `cta_logo`, and/or `cta_url`, or removes all scene footers with `remove_footer: true`. Footer updates queue only frame/video regeneration.
586
627
  - `cloneVideo` creates a deep copy of a completed session and queues only the final video render so the clone receives a new rendered video path and URL. It does not charge credits.
587
628
  - Main video methods and external-user methods accept the same generated outro and footer parameters. The API can resolve either internal session ids or external `extreq_...` ids on repeated video routes, so client code can keep using `translateVideo`, `joinVideos`, `addSubtitles`, `removeSubtitles`, `addVideoOutroImage`, `updateVideoOutroImage`, and `updateVideoFooterImage`; the explicit external variants are available when you want to call `/external_users/*` directly. Do not strip the `extreq_` prefix.
package/dist/index.d.ts CHANGED
@@ -36,6 +36,83 @@ export interface FontOptions {
36
36
  language?: string;
37
37
  family?: string;
38
38
  }
39
+ export type BackingTrackModel = 'LYRIA3' | 'LYRIA2' | 'ELEVENLABS_MUSIC' | 'Lyria 3' | 'Lyria 2' | 'ElevenLabs music' | string;
40
+ export type TTSModel = 'ELEVENLABS' | 'OPENAI' | 'GOOGLE' | 'ElevenLabs' | 'OpenAI' | 'Google TTS' | string;
41
+ export type InferenceModel = 'gpt-5.5' | 'GPT 5.5' | 'gemini-3.1-pro' | 'Gemini 3.1 Pro' | string;
42
+ export interface GoogleTTSSpeakerDetail {
43
+ provider?: 'GOOGLE' | string;
44
+ value?: string;
45
+ voiceId?: string;
46
+ name?: string;
47
+ label?: string;
48
+ languageCode?: string;
49
+ languageCodes?: string[];
50
+ Gender?: 'M' | 'F' | '' | string | null;
51
+ gender?: string;
52
+ genderCode?: string;
53
+ ssmlGender?: string;
54
+ naturalSampleRateHertz?: number | null;
55
+ voiceType?: string;
56
+ previewRequiresAuth?: boolean;
57
+ [key: string]: unknown;
58
+ }
59
+ export interface TTSSpeakerOptions {
60
+ allowOpenAI?: boolean;
61
+ allowElevenLabs?: boolean;
62
+ allowGoogle?: boolean;
63
+ openAISpeakers?: string[];
64
+ elevenLabsSpeakers?: string[];
65
+ googleSpeakers?: string[];
66
+ googleSpeakerDetails?: GoogleTTSSpeakerDetail[];
67
+ [key: string]: unknown;
68
+ }
69
+ export type OutroCtaImageSource = string | {
70
+ url?: string;
71
+ image_url?: string;
72
+ imageUrl?: string;
73
+ public_url?: string;
74
+ publicUrl?: string;
75
+ source_url?: string;
76
+ sourceUrl?: string;
77
+ source?: string;
78
+ src?: string;
79
+ image?: string;
80
+ data_url?: string;
81
+ dataUrl?: string;
82
+ image_data?: string;
83
+ imageData?: string;
84
+ data?: string;
85
+ mime_type?: string;
86
+ mimeType?: string;
87
+ content_type?: string;
88
+ contentType?: string;
89
+ [key: string]: unknown;
90
+ };
91
+ export interface OutroCtaImagePayload {
92
+ top_text?: string;
93
+ topText?: string;
94
+ cta_text_top?: string;
95
+ ctaTextTop?: string;
96
+ middle_image?: OutroCtaImageSource;
97
+ middleImage?: OutroCtaImageSource;
98
+ center_image?: OutroCtaImageSource;
99
+ centerImage?: OutroCtaImageSource;
100
+ middle?: OutroCtaImageSource;
101
+ center?: OutroCtaImageSource;
102
+ bottom_text?: string;
103
+ bottomText?: string;
104
+ cta_text_bottom?: string;
105
+ ctaTextBottom?: string;
106
+ url?: string;
107
+ image_url?: string;
108
+ imageUrl?: string;
109
+ src?: string;
110
+ data_url?: string;
111
+ dataUrl?: string;
112
+ image_data?: string;
113
+ imageData?: string;
114
+ [key: string]: unknown;
115
+ }
39
116
  export type V2StepGenerationMode = 'one_step' | 'two_step';
40
117
  export interface V2StepGenerationOptions {
41
118
  auto_render_full_video?: boolean;
@@ -50,7 +127,19 @@ export interface CreateVideoFromTextInput extends V2StepGenerationOptions {
50
127
  duration: number;
51
128
  tone?: string;
52
129
  aspect_ratio?: string;
53
- video_model_sub_type?: string;
130
+ backingtrack_model?: BackingTrackModel;
131
+ backing_track_model?: BackingTrackModel;
132
+ backingTrackModel?: BackingTrackModel;
133
+ music_provider?: BackingTrackModel;
134
+ musicProvider?: BackingTrackModel;
135
+ tts_model?: TTSModel;
136
+ ttsModel?: TTSModel;
137
+ tts_provider?: TTSModel;
138
+ ttsProvider?: TTSModel;
139
+ inference_model?: InferenceModel;
140
+ inferenceModel?: InferenceModel;
141
+ speakerOptions?: TTSSpeakerOptions;
142
+ speaker_options?: TTSSpeakerOptions;
54
143
  font_key?: string;
55
144
  fontKey?: string;
56
145
  subtitle_font?: string;
@@ -85,6 +174,10 @@ export interface CreateVideoFromTextInput extends V2StepGenerationOptions {
85
174
  ctaTextBottom?: string;
86
175
  cta_logo?: string;
87
176
  ctaLogo?: string;
177
+ outro_cta_image?: OutroCtaImagePayload | string;
178
+ outroCtaImage?: OutroCtaImagePayload | string;
179
+ cta_image?: OutroCtaImagePayload | string;
180
+ ctaImage?: OutroCtaImagePayload | string;
88
181
  add_footer_animation?: boolean;
89
182
  addFooterAnimation?: boolean;
90
183
  footer_metadata?: FooterMetadataItem[];
@@ -118,7 +211,7 @@ export interface FooterMetadataItem {
118
211
  footerLogoImagePath?: string;
119
212
  }
120
213
  export type ImageListToVideoAspectRatio = '16:9' | '9:16';
121
- export type ImageListToVideoModel = 'RUNWAYML' | 'VEO3.1I2V' | 'VEO3.1I2VFAST' | 'SEEDANCEI2V' | 'KLINGIMGTOVID3PRO' | 'HAPPYHORSEI2V';
214
+ export type ImageListToVideoModel = 'RUNWAYML' | 'VEO3.1I2V' | 'VEO3.1I2VFAST' | 'COSMOS3SUPERI2V' | 'SEEDANCEI2V' | 'KLINGIMGTOVID3PRO' | 'HAPPYHORSEI2V';
122
215
  export interface ImageListToVideoItem {
123
216
  image_url?: string;
124
217
  imageUrl?: string;
@@ -155,6 +248,19 @@ export interface CreateVideoFromImageListInput extends V2StepGenerationOptions {
155
248
  aspectRatio?: ImageListToVideoAspectRatio;
156
249
  language?: string;
157
250
  languageString?: string | null;
251
+ backingtrack_model?: BackingTrackModel;
252
+ backing_track_model?: BackingTrackModel;
253
+ backingTrackModel?: BackingTrackModel;
254
+ music_provider?: BackingTrackModel;
255
+ musicProvider?: BackingTrackModel;
256
+ tts_model?: TTSModel;
257
+ ttsModel?: TTSModel;
258
+ tts_provider?: TTSModel;
259
+ ttsProvider?: TTSModel;
260
+ inference_model?: InferenceModel;
261
+ inferenceModel?: InferenceModel;
262
+ speakerOptions?: TTSSpeakerOptions;
263
+ speaker_options?: TTSSpeakerOptions;
158
264
  font_key?: string;
159
265
  fontKey?: string;
160
266
  subtitle_font?: string;
@@ -193,6 +299,10 @@ export interface CreateVideoFromImageListInput extends V2StepGenerationOptions {
193
299
  ctaTextBottom?: string;
194
300
  cta_logo?: string;
195
301
  ctaLogo?: string;
302
+ outro_cta_image?: OutroCtaImagePayload | string;
303
+ outroCtaImage?: OutroCtaImagePayload | string;
304
+ cta_image?: OutroCtaImagePayload | string;
305
+ ctaImage?: OutroCtaImagePayload | string;
196
306
  add_footer_animation?: boolean;
197
307
  addFooterAnimation?: boolean;
198
308
  footer_metadata?: FooterMetadataItem[];
@@ -319,6 +429,10 @@ export interface UpdateVideoOutroImageInput {
319
429
  ctaTextBottom?: string;
320
430
  cta_logo?: string;
321
431
  ctaLogo?: string;
432
+ outro_cta_image?: OutroCtaImagePayload | string;
433
+ outroCtaImage?: OutroCtaImagePayload | string;
434
+ cta_image?: OutroCtaImagePayload | string;
435
+ ctaImage?: OutroCtaImagePayload | string;
322
436
  [key: string]: unknown;
323
437
  }
324
438
  export interface UpdateVideoOutroImageResponse {
@@ -389,6 +503,20 @@ export interface AddVideoOutroImageInput {
389
503
  outro_focus_area?: OutroFocusArea | null;
390
504
  outroFocustArea?: OutroFocusArea | null;
391
505
  outroFocusArea?: OutroFocusArea | null;
506
+ generate_outro_image?: boolean;
507
+ generateOutroImage?: boolean;
508
+ cta_url?: string;
509
+ ctaUrl?: string;
510
+ cta_text_top?: string;
511
+ ctaTextTop?: string;
512
+ cta_text_bottom?: string;
513
+ ctaTextBottom?: string;
514
+ cta_logo?: string;
515
+ ctaLogo?: string;
516
+ outro_cta_image?: OutroCtaImagePayload | string;
517
+ outroCtaImage?: OutroCtaImagePayload | string;
518
+ cta_image?: OutroCtaImagePayload | string;
519
+ ctaImage?: OutroCtaImagePayload | string;
392
520
  [key: string]: unknown;
393
521
  }
394
522
  export interface AddVideoOutroImageResponse {
package/dist/index.js CHANGED
@@ -41,6 +41,58 @@ function assertOptionalBoolean(value, fieldName, context = 'createVideoFromImage
41
41
  throw new Error(`${fieldName} must be a boolean for ${context}`);
42
42
  }
43
43
  }
44
+ function hasOutroCtaImageSource(value) {
45
+ if (typeof value === 'string') {
46
+ return Boolean(value.trim());
47
+ }
48
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
49
+ return false;
50
+ }
51
+ const record = value;
52
+ const middleImage = getFirstDefinedInputValue(record, [
53
+ 'middle_image',
54
+ 'middleImage',
55
+ 'center_image',
56
+ 'centerImage',
57
+ 'middle',
58
+ 'center',
59
+ ]);
60
+ if (middleImage !== undefined) {
61
+ return hasOutroCtaImageSource(middleImage);
62
+ }
63
+ return Boolean(getTrimmedString(record.url) ??
64
+ getTrimmedString(record.image_url) ??
65
+ getTrimmedString(record.imageUrl) ??
66
+ getTrimmedString(record.public_url) ??
67
+ getTrimmedString(record.publicUrl) ??
68
+ getTrimmedString(record.source_url) ??
69
+ getTrimmedString(record.sourceUrl) ??
70
+ getTrimmedString(record.source) ??
71
+ getTrimmedString(record.src) ??
72
+ getTrimmedString(record.image) ??
73
+ getTrimmedString(record.middle_image_url) ??
74
+ getTrimmedString(record.middleImageUrl) ??
75
+ getTrimmedString(record.center_image_url) ??
76
+ getTrimmedString(record.centerImageUrl) ??
77
+ getTrimmedString(record.data_url) ??
78
+ getTrimmedString(record.dataUrl) ??
79
+ getTrimmedString(record.image_data) ??
80
+ getTrimmedString(record.imageData) ??
81
+ getTrimmedString(record.data));
82
+ }
83
+ function normalizeOutroCtaImageInput(raw, context) {
84
+ const value = resolveAliasedInputValue(raw, ['outro_cta_image', 'outroCtaImage', 'cta_image', 'ctaImage'], 'outro_cta_image');
85
+ if (value === undefined || value === null) {
86
+ return undefined;
87
+ }
88
+ if (typeof value !== 'string' && (typeof value !== 'object' || Array.isArray(value))) {
89
+ throw new Error(`outro_cta_image must be a string or object for ${context}`);
90
+ }
91
+ if (!hasOutroCtaImageSource(value)) {
92
+ throw new Error(`outro_cta_image.middle_image is required for ${context}`);
93
+ }
94
+ return value;
95
+ }
44
96
  function getFirstDefinedInputValue(raw, aliases) {
45
97
  for (const alias of aliases) {
46
98
  if (Object.prototype.hasOwnProperty.call(raw, alias)) {
@@ -135,8 +187,13 @@ function normalizeCreateVideoFromTextInput(input) {
135
187
  ['cta_text_top', ['cta_text_top', 'ctaTextTop']],
136
188
  ['cta_text_bottom', ['cta_text_bottom', 'ctaTextBottom']],
137
189
  ['cta_logo', ['cta_logo', 'ctaLogo']],
190
+ ['outro_cta_image', ['outro_cta_image', 'outroCtaImage', 'cta_image', 'ctaImage']],
138
191
  ['add_footer_animation', ['add_footer_animation', 'addFooterAnimation']],
139
192
  ['footer_metadata', ['footer_metadata', 'footerMetadata']],
193
+ ['backingtrack_model', ['backingtrack_model', 'backing_track_model', 'backingTrackModel', 'music_provider', 'musicProvider']],
194
+ ['tts_model', ['tts_model', 'ttsModel', 'tts_provider', 'ttsProvider']],
195
+ ['inference_model', ['inference_model', 'inferenceModel']],
196
+ ['speakerOptions', ['speakerOptions', 'speaker_options']],
140
197
  ['enable_subtitles', ['enable_subtitles', 'enableSubtitles']],
141
198
  ['font_key', ['font_key', 'fontKey']],
142
199
  ];
@@ -146,6 +203,8 @@ function normalizeCreateVideoFromTextInput(input) {
146
203
  normalized[canonicalName] = value;
147
204
  }
148
205
  }
206
+ delete normalized.video_model_sub_type;
207
+ delete normalized.videoModelSubType;
149
208
  assertOptionalBoolean(normalized.enable_subtitles, 'enable_subtitles', 'createVideoFromText');
150
209
  assertOptionalBoolean(normalized.add_outro_animation, 'add_outro_animation', 'createVideoFromText');
151
210
  assertOptionalBoolean(normalized.add_outro_focus_area, 'add_outro_focus_area', 'createVideoFromText');
@@ -153,11 +212,21 @@ function normalizeCreateVideoFromTextInput(input) {
153
212
  assertOptionalBoolean(normalized.add_footer_animation, 'add_footer_animation', 'createVideoFromText');
154
213
  if (normalized.generate_outro_image === true) {
155
214
  const ctaUrl = typeof normalized.cta_url === 'string' ? normalized.cta_url.trim() : '';
156
- if (!ctaUrl) {
157
- throw new Error('cta_url is required when generate_outro_image is true for createVideoFromText');
215
+ const outroCtaImage = normalizeOutroCtaImageInput(normalized, 'createVideoFromText');
216
+ if (outroCtaImage !== undefined) {
217
+ normalized.outro_cta_image = outroCtaImage;
218
+ }
219
+ if (!ctaUrl && !outroCtaImage) {
220
+ throw new Error('cta_url or outro_cta_image is required when generate_outro_image is true for createVideoFromText');
158
221
  }
159
222
  }
160
- else if (normalized.add_outro_focus_area === true && normalized.add_outro_animation !== true) {
223
+ else {
224
+ const outroCtaImage = normalizeOutroCtaImageInput(normalized, 'createVideoFromText');
225
+ if (outroCtaImage !== undefined) {
226
+ normalized.outro_cta_image = outroCtaImage;
227
+ }
228
+ }
229
+ if (normalized.generate_outro_image !== true && normalized.add_outro_focus_area === true && normalized.add_outro_animation !== true) {
161
230
  throw new Error('add_outro_focus_area requires add_outro_animation to be true for createVideoFromText');
162
231
  }
163
232
  return normalized;
@@ -191,8 +260,13 @@ function normalizeCreateVideoFromImageListInput(input) {
191
260
  ['cta_text_top', ['cta_text_top', 'ctaTextTop']],
192
261
  ['cta_text_bottom', ['cta_text_bottom', 'ctaTextBottom']],
193
262
  ['cta_logo', ['cta_logo', 'ctaLogo']],
263
+ ['outro_cta_image', ['outro_cta_image', 'outroCtaImage', 'cta_image', 'ctaImage']],
194
264
  ['add_footer_animation', ['add_footer_animation', 'addFooterAnimation']],
195
265
  ['footer_metadata', ['footer_metadata', 'footerMetadata']],
266
+ ['backingtrack_model', ['backingtrack_model', 'backing_track_model', 'backingTrackModel', 'music_provider', 'musicProvider']],
267
+ ['tts_model', ['tts_model', 'ttsModel', 'tts_provider', 'ttsProvider']],
268
+ ['inference_model', ['inference_model', 'inferenceModel']],
269
+ ['speakerOptions', ['speakerOptions', 'speaker_options']],
196
270
  ['limit_single_narrator', ['limit_single_narrator', 'limitSingleNarrator']],
197
271
  ['add_narrator_avatar', ['add_narrator_avatar', 'addNarratorAvatar']],
198
272
  ['enable_subtitles', ['enable_subtitles', 'enableSubtitles']],
@@ -204,6 +278,8 @@ function normalizeCreateVideoFromImageListInput(input) {
204
278
  normalized[canonicalName] = value;
205
279
  }
206
280
  }
281
+ delete normalized.video_model_sub_type;
282
+ delete normalized.videoModelSubType;
207
283
  assertOptionalBoolean(normalized.enable_subtitles, 'enable_subtitles');
208
284
  assertOptionalBoolean(normalized.add_outro_animation, 'add_outro_animation');
209
285
  assertOptionalBoolean(normalized.add_outro_focus_area, 'add_outro_focus_area');
@@ -212,6 +288,10 @@ function normalizeCreateVideoFromImageListInput(input) {
212
288
  assertOptionalBoolean(normalized.add_footer_animation, 'add_footer_animation');
213
289
  assertOptionalBoolean(normalized.limit_single_narrator, 'limit_single_narrator');
214
290
  assertOptionalBoolean(normalized.add_narrator_avatar, 'add_narrator_avatar');
291
+ const outroCtaImage = normalizeOutroCtaImageInput(normalized, 'createVideoFromImageList');
292
+ if (outroCtaImage !== undefined) {
293
+ normalized.outro_cta_image = outroCtaImage;
294
+ }
215
295
  if (normalized.add_narrator_avatar === true) {
216
296
  normalized.limit_single_narrator = true;
217
297
  }
@@ -228,8 +308,8 @@ function normalizeCreateVideoFromImageListInput(input) {
228
308
  }
229
309
  if (normalized.generate_outro_image === true) {
230
310
  const ctaUrl = typeof normalized.cta_url === 'string' ? normalized.cta_url.trim() : '';
231
- if (!ctaUrl) {
232
- throw new Error('cta_url is required when generate_outro_image is true for createVideoFromImageList');
311
+ if (!ctaUrl && !outroCtaImage) {
312
+ throw new Error('cta_url or outro_cta_image is required when generate_outro_image is true for createVideoFromImageList');
233
313
  }
234
314
  }
235
315
  else if (normalized.add_outro_focus_area === true && normalized.add_outro_animation !== true) {
@@ -358,8 +438,9 @@ function normalizeUpdateVideoOutroImageInput(input, context = 'updateVideoOutroI
358
438
  raw.ctaTextBottom;
359
439
  const ctaLogo = raw.cta_logo ??
360
440
  raw.ctaLogo;
441
+ const outroCtaImage = normalizeOutroCtaImageInput(raw, context);
361
442
  const generateOutroImage = rawGenerateOutroImage === true ||
362
- (rawGenerateOutroImage === undefined && !outroImageUrl && Boolean(ctaUrl));
443
+ (rawGenerateOutroImage === undefined && !outroImageUrl && (Boolean(ctaUrl) || Boolean(outroCtaImage)));
363
444
  if (!videoSessionId) {
364
445
  throw new Error(`videoSessionId is required for ${context}`);
365
446
  }
@@ -372,15 +453,15 @@ function normalizeUpdateVideoOutroImageInput(input, context = 'updateVideoOutroI
372
453
  if (rawAddOutroFocusArea !== undefined && typeof rawAddOutroFocusArea !== 'boolean') {
373
454
  throw new Error(`add_outro_focus_area must be a boolean for ${context}`);
374
455
  }
375
- if (generateOutroImage && outroImageUrl) {
376
- throw new Error(`Use either generate_outro_image with cta_url or outro_image_url for ${context}`);
456
+ if ((generateOutroImage || outroCtaImage) && outroImageUrl) {
457
+ throw new Error(`Use either generate_outro_image with cta_url/outro_cta_image or outro_image_url for ${context}`);
377
458
  }
378
459
  if (!generateOutroImage && !outroImageUrl) {
379
460
  throw new Error(`outro_image_url is required for ${context} unless generate_outro_image is true`);
380
461
  }
381
462
  if (generateOutroImage) {
382
- if (!ctaUrl || !String(ctaUrl).trim()) {
383
- throw new Error(`cta_url is required when generate_outro_image is true for ${context}`);
463
+ if ((!ctaUrl || !String(ctaUrl).trim()) && !outroCtaImage) {
464
+ throw new Error(`cta_url or outro_cta_image is required when generate_outro_image is true for ${context}`);
384
465
  }
385
466
  }
386
467
  else if (rawAddOutroFocusArea === true && rawAddOutroAnimation !== true) {
@@ -401,7 +482,8 @@ function normalizeUpdateVideoOutroImageInput(input, context = 'updateVideoOutroI
401
482
  videoSessionId: String(videoSessionId),
402
483
  ...(outroImageUrl ? { outro_image_url: String(outroImageUrl) } : {}),
403
484
  generate_outro_image: generateOutroImage,
404
- ...(generateOutroImage ? { cta_url: String(ctaUrl).trim() } : {}),
485
+ ...(generateOutroImage && ctaUrl ? { cta_url: String(ctaUrl).trim() } : {}),
486
+ ...(generateOutroImage && outroCtaImage ? { outro_cta_image: outroCtaImage } : {}),
405
487
  ...(ctaTextTop ? { cta_text_top: String(ctaTextTop) } : {}),
406
488
  ...(ctaTextBottom ? { cta_text_bottom: String(ctaTextBottom) } : {}),
407
489
  ...(ctaLogo ? { cta_logo: String(ctaLogo) } : {}),
@@ -867,8 +949,9 @@ export class SamsarClient {
867
949
  }, options);
868
950
  }
869
951
  async addV2VideoOutroImage(input, options) {
952
+ const normalizedInput = normalizeUpdateVideoOutroImageInput(input, 'addV2VideoOutroImage');
870
953
  return this.postV2('add_outro_image', {
871
- input,
954
+ input: normalizedInput,
872
955
  webhookUrl: options?.webhookUrl,
873
956
  }, options);
874
957
  }
@@ -1472,61 +1555,9 @@ export class SamsarClient {
1472
1555
  * re-running frame/video generation.
1473
1556
  */
1474
1557
  async addVideoOutroImage(input, options) {
1475
- const raw = input;
1476
- const videoSessionId = raw.videoSessionId ??
1477
- raw.video_session_id ??
1478
- raw.videoSessionID ??
1479
- raw.session_id ??
1480
- raw.sessionId ??
1481
- raw.sessionID ??
1482
- raw.request_id ??
1483
- raw.requestId;
1484
- const outroImageUrl = raw.outro_image_url ??
1485
- raw.outroImageUrl ??
1486
- raw.new_outro_image_url ??
1487
- raw.newOutroImageUrl;
1488
- const rawAddOutroAnimation = raw.add_outro_animation ??
1489
- raw.addOutroAnimation;
1490
- const rawAddOutroFocusArea = raw.add_outro_focus_area ??
1491
- raw.addOutroFocusArea;
1492
- const rawOutroFocusArea = raw.outro_focust_area ??
1493
- raw.outro_focus_area ??
1494
- raw.outroFocustArea ??
1495
- raw.outroFocusArea;
1496
- if (!videoSessionId) {
1497
- throw new Error('videoSessionId is required for addVideoOutroImage');
1498
- }
1499
- if (!outroImageUrl) {
1500
- throw new Error('outro_image_url is required for addVideoOutroImage');
1501
- }
1502
- if (rawAddOutroAnimation !== undefined && typeof rawAddOutroAnimation !== 'boolean') {
1503
- throw new Error('add_outro_animation must be a boolean for addVideoOutroImage');
1504
- }
1505
- if (rawAddOutroFocusArea !== undefined && typeof rawAddOutroFocusArea !== 'boolean') {
1506
- throw new Error('add_outro_focus_area must be a boolean for addVideoOutroImage');
1507
- }
1508
- if (rawAddOutroFocusArea === true && rawAddOutroAnimation !== true) {
1509
- throw new Error('add_outro_focus_area requires add_outro_animation to be true for addVideoOutroImage');
1510
- }
1511
- if (rawAddOutroFocusArea === true) {
1512
- if (!rawOutroFocusArea || typeof rawOutroFocusArea !== 'object' || Array.isArray(rawOutroFocusArea)) {
1513
- throw new Error('outro_focust_area must be an object with x, y, width, height for addVideoOutroImage');
1514
- }
1515
- const { x, y, width, height } = rawOutroFocusArea;
1516
- const isInvalid = [x, y, width, height].some((value) => typeof value !== 'number' || !Number.isFinite(value));
1517
- if (isInvalid) {
1518
- throw new Error('outro_focust_area x, y, width, height must be valid numbers for addVideoOutroImage');
1519
- }
1520
- }
1558
+ const normalizedInput = normalizeUpdateVideoOutroImageInput(input, 'addVideoOutroImage');
1521
1559
  const body = {
1522
- input: {
1523
- ...input,
1524
- videoSessionId: String(videoSessionId),
1525
- outro_image_url: String(outroImageUrl),
1526
- ...(rawAddOutroAnimation !== undefined ? { add_outro_animation: rawAddOutroAnimation === true } : {}),
1527
- ...(rawAddOutroFocusArea !== undefined ? { add_outro_focus_area: rawAddOutroFocusArea === true } : {}),
1528
- ...(rawOutroFocusArea !== undefined ? { outro_focust_area: rawOutroFocusArea } : {}),
1529
- },
1560
+ input: normalizedInput,
1530
1561
  webhookUrl: options?.webhookUrl,
1531
1562
  };
1532
1563
  const response = await this.post('video/add_outro_image', body, options);
@@ -1554,68 +1585,10 @@ export class SamsarClient {
1554
1585
  * Add or replace an outro image for an external-user video request by resolving the external request id.
1555
1586
  */
1556
1587
  async addExternalVideoOutroImage(externalUser, input, options) {
1557
- const raw = input;
1558
- const videoSessionId = raw.videoSessionId ??
1559
- raw.video_session_id ??
1560
- raw.videoSessionID ??
1561
- raw.session_id ??
1562
- raw.sessionId ??
1563
- raw.sessionID ??
1564
- raw.request_id ??
1565
- raw.requestId ??
1566
- raw.source_request_id ??
1567
- raw.sourceRequestId ??
1568
- raw.external_request_id ??
1569
- raw.externalRequestId ??
1570
- raw.external_session_id ??
1571
- raw.externalSessionId;
1572
- const outroImageUrl = raw.outro_image_url ??
1573
- raw.outroImageUrl ??
1574
- raw.new_outro_image_url ??
1575
- raw.newOutroImageUrl;
1576
- const rawAddOutroAnimation = raw.add_outro_animation ??
1577
- raw.addOutroAnimation;
1578
- const rawAddOutroFocusArea = raw.add_outro_focus_area ??
1579
- raw.addOutroFocusArea;
1580
- const rawOutroFocusArea = raw.outro_focust_area ??
1581
- raw.outro_focus_area ??
1582
- raw.outroFocustArea ??
1583
- raw.outroFocusArea;
1584
- if (!videoSessionId) {
1585
- throw new Error('videoSessionId is required for addExternalVideoOutroImage');
1586
- }
1587
- if (!outroImageUrl) {
1588
- throw new Error('outro_image_url is required for addExternalVideoOutroImage');
1589
- }
1590
- if (rawAddOutroAnimation !== undefined && typeof rawAddOutroAnimation !== 'boolean') {
1591
- throw new Error('add_outro_animation must be a boolean for addExternalVideoOutroImage');
1592
- }
1593
- if (rawAddOutroFocusArea !== undefined && typeof rawAddOutroFocusArea !== 'boolean') {
1594
- throw new Error('add_outro_focus_area must be a boolean for addExternalVideoOutroImage');
1595
- }
1596
- if (rawAddOutroFocusArea === true && rawAddOutroAnimation !== true) {
1597
- throw new Error('add_outro_focus_area requires add_outro_animation to be true for addExternalVideoOutroImage');
1598
- }
1599
- if (rawAddOutroFocusArea === true) {
1600
- if (!rawOutroFocusArea || typeof rawOutroFocusArea !== 'object' || Array.isArray(rawOutroFocusArea)) {
1601
- throw new Error('outro_focust_area must be an object with x, y, width, height for addExternalVideoOutroImage');
1602
- }
1603
- const { x, y, width, height } = rawOutroFocusArea;
1604
- const isInvalid = [x, y, width, height].some((value) => typeof value !== 'number' || !Number.isFinite(value));
1605
- if (isInvalid) {
1606
- throw new Error('outro_focust_area x, y, width, height must be valid numbers for addExternalVideoOutroImage');
1607
- }
1608
- }
1588
+ const normalizedInput = normalizeUpdateVideoOutroImageInput(input, 'addExternalVideoOutroImage');
1609
1589
  const body = {
1610
1590
  external_user: normalizeExternalUserIdentity(externalUser),
1611
- input: {
1612
- ...input,
1613
- videoSessionId: String(videoSessionId),
1614
- outro_image_url: String(outroImageUrl),
1615
- ...(rawAddOutroAnimation !== undefined ? { add_outro_animation: rawAddOutroAnimation === true } : {}),
1616
- ...(rawAddOutroFocusArea !== undefined ? { add_outro_focus_area: rawAddOutroFocusArea === true } : {}),
1617
- ...(rawOutroFocusArea !== undefined ? { outro_focust_area: rawOutroFocusArea } : {}),
1618
- },
1591
+ input: normalizedInput,
1619
1592
  webhookUrl: options?.webhookUrl,
1620
1593
  };
1621
1594
  return this.post('external_users/add_outro_image', body, options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "samsar-js",
3
- "version": "0.48.32",
3
+ "version": "0.48.34",
4
4
  "description": "TypeScript client for the Samsar Processor API routes.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",