samsar-js 0.48.14 → 0.48.16

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
@@ -34,11 +34,11 @@ const samsar = new SamsarClient({ apiKey: process.env.SAMSAR_API_KEY! });
34
34
  const video = await samsar.createVideoFromText(
35
35
  {
36
36
  prompt: 'A drone shot of a beach at sunrise',
37
- image_model: 'GPTIMAGE1',
37
+ image_model: 'GPTIMAGE2',
38
38
  video_model: 'RUNWAYML',
39
39
  duration: 30,
40
40
  font_key: 'Poppins',
41
- enable_subtitles: false,
41
+ enable_subtitles: true,
42
42
  },
43
43
  { webhookUrl: 'https://example.com/webhook' },
44
44
  );
@@ -49,13 +49,48 @@ const videoFromImages = await samsar.createVideoFromImageList(
49
49
  image_urls: ['https://example.com/a.jpg', 'https://example.com/b.jpg'],
50
50
  prompt: 'Cinematic sequence with smooth transitions',
51
51
  metadata: { project: 'demo' },
52
+ video_model: 'RUNWAYML',
53
+ aspect_ratio: '16:9',
52
54
  language: 'en',
53
55
  font_key: 'Poppins',
54
- enable_subtitles: false,
56
+ enable_subtitles: true,
55
57
  },
56
58
  { webhookUrl: 'https://example.com/webhook' },
57
59
  );
58
60
 
61
+ // Create a video with a provided outro image
62
+ await samsar.createVideoFromImageList({
63
+ image_urls: ['https://example.com/a.jpg', 'https://example.com/b.jpg'],
64
+ prompt: 'Product launch teaser with a clean final CTA',
65
+ video_model: 'KLING3.0',
66
+ aspect_ratio: '16:9',
67
+ outro_image_url: 'https://cdn.example.com/outro.png',
68
+ add_outro_animation: true,
69
+ add_outro_focus_area: true,
70
+ outro_focust_area: { x: 680, y: 296, width: 432, height: 432 },
71
+ });
72
+
73
+ // Create a video and generate the QR outro server-side from the input images
74
+ await samsar.createVideoFromImageList({
75
+ image_urls: [
76
+ { image_url: 'https://example.com/a.jpg', title: 'Blue Lagoon Tour' },
77
+ { image_url: 'https://example.com/b.jpg', title: 'Sunset Dinner' },
78
+ ],
79
+ prompt: 'Travel offer reel with a scannable booking outro',
80
+ video_model: 'RUNWAYML',
81
+ aspect_ratio: '9:16',
82
+ generate_outro_image: true,
83
+ cta_url: 'https://example.com/book',
84
+ cta_text_top: 'Scan to book',
85
+ cta_text_bottom: 'Limited availability',
86
+ cta_logo: 'https://cdn.example.com/logo-white.png',
87
+ add_footer_animation: true,
88
+ footer_metadata: [
89
+ { url: 'https://example.com/blue-lagoon', title: 'Blue Lagoon Tour' },
90
+ { url: 'https://example.com/sunset-dinner', title: 'Sunset Dinner' },
91
+ ],
92
+ });
93
+
59
94
  // Translate an existing video session into another language
60
95
  const translated = await samsar.translateVideo(
61
96
  {
@@ -217,7 +252,9 @@ const replaced = await samsar.replaceBrandingFromImage({
217
252
  // Extend image set
218
253
  const images = await samsar.extendImageList({
219
254
  image_urls: ['https://example.com/extra.jpg'],
255
+ prompt: 'Create a cinematic travel header banner',
220
256
  num_images: 4,
257
+ aspect_ratio: '16:9',
221
258
  });
222
259
 
223
260
  // Create a reusable receipt template from one sample receipt image (free endpoint)
@@ -322,7 +359,7 @@ await platform.setExternalAssistantSystemPrompt(
322
359
  // Create a render attributed to that external user
323
360
  const externalRender = await platform.createExternalVideoFromText(externalUser, {
324
361
  prompt: 'A sleek teaser for a futuristic running shoe',
325
- image_model: 'GPTIMAGE1',
362
+ image_model: 'GPTIMAGE2',
326
363
  video_model: 'RUNWAYML',
327
364
  duration: 10,
328
365
  enable_subtitles: true,
@@ -389,9 +426,12 @@ console.log(externalLibrary.data.requests.map((request) => request.request_id));
389
426
  ```
390
427
 
391
428
  Video model support notes:
392
- - `createVideoFromText` image model keys include: `GPTIMAGE1`, `IMAGEN4`, `SEEDREAM`, `HUNYUAN`, `NANOBANANA2`.
393
- - `createVideoFromText` supports all express video models: `RUNWAYML`, `KLINGIMGTOVID3PRO`, `HAILUO`, `HAILUOPRO`, `SEEDANCEI2V`, `VEO3.1I2V`, `VEO3.1I2VFAST`, `SORA2`, `SORA2PRO`.
394
- - `createVideoFromImageList` uses a fixed Veo2.1 pipeline model (`VEO3.1I2V`) and does not accept a `video_model` override.
429
+ - `createVideoFromText` image model keys include: `GPTIMAGE2`, `IMAGEN4`, `SEEDREAM`, `HUNYUAN`, `NANOBANANA2`.
430
+ - `createVideoFromText` supports all express video models: `RUNWAYML`, `KLINGIMGTOVID3PRO`, `HAILUO`, `HAILUOPRO`, `SEEDANCEI2V` (Seedance 2.0), `VEO3.1I2V`, `VEO3.1I2VFAST`, `SORA2`, `SORA2PRO`.
431
+ - `createVideoFromImageList` supports `VEO3.1I2V`, `SEEDANCEI2V`, `KLING3.0`, and `RUNWAYML` via `video_model`; if omitted, it defaults to `VEO3.1I2V`. `KLINGIMGTOVID3PRO` is also accepted as a compatibility alias for `KLING3.0`. Use `aspect_ratio: '16:9'` or `'9:16'`; omitted or invalid values fall back to `16:9`.
432
+ - `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.
433
+ - `createVideoFromImageList` can render per-scene footer QR cards by setting `add_footer_animation: true` and providing one `footer_metadata` item per image scene.
434
+ - Image-list video pricing is per rendered second: `VEO3.1I2V` and `SEEDANCEI2V` are 75 credits/sec, `KLING3.0` is 50 credits/sec, and `RUNWAYML` is 25 credits/sec.
395
435
 
396
436
  Each method returns `{ data, status, headers, creditsCharged, creditsRemaining, raw }`. Non-2xx responses throw `SamsarRequestError` containing status, body, and credit headers (if present).
397
437
 
package/dist/index.d.ts CHANGED
@@ -64,6 +64,12 @@ export interface OutroFocusArea {
64
64
  width: number;
65
65
  height: number;
66
66
  }
67
+ export interface FooterMetadataItem {
68
+ url: string;
69
+ title?: string;
70
+ }
71
+ export type ImageListToVideoAspectRatio = '16:9' | '9:16';
72
+ export type ImageListToVideoModel = 'VEO3.1I2V' | 'SEEDANCEI2V' | 'KLING3.0' | 'KLINGIMGTOVID3PRO' | 'RUNWAYML';
67
73
  export interface ImageListToVideoItem {
68
74
  image_url?: string;
69
75
  imageUrl?: string;
@@ -79,12 +85,25 @@ export interface ImageListToVideoItem {
79
85
  fromEnhancedList?: boolean;
80
86
  skip_enhancement?: boolean;
81
87
  skipEnhancement?: boolean;
88
+ source?: string;
89
+ title?: string;
90
+ image_title?: string;
91
+ imageTitle?: string;
92
+ image_text?: string;
93
+ imageText?: string;
94
+ activity_title?: string;
95
+ activityTitle?: string;
96
+ name?: string;
97
+ label?: string;
82
98
  [key: string]: unknown;
83
99
  }
84
100
  export interface CreateVideoFromImageListInput {
85
101
  image_urls: Array<string | ImageListToVideoItem>;
86
102
  metadata?: Record<string, unknown>;
87
103
  prompt?: string;
104
+ video_model?: ImageListToVideoModel;
105
+ aspect_ratio?: ImageListToVideoAspectRatio;
106
+ aspectRatio?: ImageListToVideoAspectRatio;
88
107
  language?: string;
89
108
  languageString?: string | null;
90
109
  font_key?: string;
@@ -98,9 +117,29 @@ export interface CreateVideoFromImageListInput {
98
117
  sessionId?: string;
99
118
  sessionID?: string;
100
119
  outro_image_url?: string;
120
+ outroImageUrl?: string;
101
121
  add_outro_animation?: boolean;
122
+ addOutroAnimation?: boolean;
102
123
  add_outro_focus_area?: boolean;
124
+ addOutroFocusArea?: boolean;
103
125
  outro_focust_area?: OutroFocusArea | null;
126
+ outro_focus_area?: OutroFocusArea | null;
127
+ outroFocustArea?: OutroFocusArea | null;
128
+ outroFocusArea?: OutroFocusArea | null;
129
+ generate_outro_image?: boolean;
130
+ generateOutroImage?: boolean;
131
+ cta_url?: string;
132
+ ctaUrl?: string;
133
+ cta_text_top?: string;
134
+ ctaTextTop?: string;
135
+ cta_text_bottom?: string;
136
+ ctaTextBottom?: string;
137
+ cta_logo?: string;
138
+ ctaLogo?: string;
139
+ add_footer_animation?: boolean;
140
+ addFooterAnimation?: boolean;
141
+ footer_metadata?: FooterMetadataItem[];
142
+ footerMetadata?: FooterMetadataItem[];
104
143
  [key: string]: unknown;
105
144
  }
106
145
  export interface TranscriptBuilderPayload {
@@ -688,6 +727,7 @@ export interface ExtendImageListRequest {
688
727
  num_images: number;
689
728
  prompt?: string;
690
729
  metadata?: Record<string, unknown>;
730
+ aspect_ratio?: string;
691
731
  }
692
732
  export interface ExtendImageListResponse {
693
733
  status?: string;
@@ -700,6 +740,7 @@ export interface ExtendImageListResponse {
700
740
  metadata?: Record<string, unknown>;
701
741
  prompt?: string;
702
742
  num_images?: number;
743
+ aspect_ratio?: string;
703
744
  userId?: string;
704
745
  creditsCharged?: number;
705
746
  remainingCredits?: number;
package/dist/index.js CHANGED
@@ -15,6 +15,72 @@ export class SamsarRequestError extends Error {
15
15
  this.creditsRemaining = init.creditsRemaining;
16
16
  }
17
17
  }
18
+ function resolveAliasedInputValue(raw, aliases, canonicalName) {
19
+ let resolved;
20
+ let hasResolved = false;
21
+ for (const alias of aliases) {
22
+ const value = raw[alias];
23
+ if (value === undefined) {
24
+ continue;
25
+ }
26
+ if (!hasResolved) {
27
+ resolved = value;
28
+ hasResolved = true;
29
+ continue;
30
+ }
31
+ if (value !== resolved) {
32
+ throw new Error(`${canonicalName} was provided with conflicting alias values.`);
33
+ }
34
+ }
35
+ return hasResolved ? resolved : undefined;
36
+ }
37
+ function assertOptionalBoolean(value, fieldName) {
38
+ if (value !== undefined && typeof value !== 'boolean') {
39
+ throw new Error(`${fieldName} must be a boolean for createVideoFromImageList`);
40
+ }
41
+ }
42
+ function normalizeCreateVideoFromImageListInput(input) {
43
+ const raw = input;
44
+ if (!Array.isArray(input.image_urls) || input.image_urls.length === 0) {
45
+ throw new Error('image_urls must be a non-empty array for createVideoFromImageList');
46
+ }
47
+ const normalized = { ...input };
48
+ const aliases = [
49
+ ['session_id', ['session_id', 'sessionId', 'sessionID']],
50
+ ['aspect_ratio', ['aspect_ratio', 'aspectRatio']],
51
+ ['outro_image_url', ['outro_image_url', 'outroImageUrl']],
52
+ ['add_outro_animation', ['add_outro_animation', 'addOutroAnimation']],
53
+ ['add_outro_focus_area', ['add_outro_focus_area', 'addOutroFocusArea']],
54
+ ['outro_focust_area', ['outro_focust_area', 'outro_focus_area', 'outroFocustArea', 'outroFocusArea']],
55
+ ['generate_outro_image', ['generate_outro_image', 'generateOutroImage']],
56
+ ['cta_url', ['cta_url', 'ctaUrl']],
57
+ ['cta_text_top', ['cta_text_top', 'ctaTextTop']],
58
+ ['cta_text_bottom', ['cta_text_bottom', 'ctaTextBottom']],
59
+ ['cta_logo', ['cta_logo', 'ctaLogo']],
60
+ ['enable_subtitles', ['enable_subtitles', 'enableSubtitles']],
61
+ ['font_key', ['font_key', 'fontKey']],
62
+ ];
63
+ for (const [canonicalName, aliasList] of aliases) {
64
+ const value = resolveAliasedInputValue(raw, aliasList, canonicalName);
65
+ if (value !== undefined) {
66
+ normalized[canonicalName] = value;
67
+ }
68
+ }
69
+ assertOptionalBoolean(normalized.enable_subtitles, 'enable_subtitles');
70
+ assertOptionalBoolean(normalized.add_outro_animation, 'add_outro_animation');
71
+ assertOptionalBoolean(normalized.add_outro_focus_area, 'add_outro_focus_area');
72
+ assertOptionalBoolean(normalized.generate_outro_image, 'generate_outro_image');
73
+ if (normalized.generate_outro_image === true) {
74
+ const ctaUrl = typeof normalized.cta_url === 'string' ? normalized.cta_url.trim() : '';
75
+ if (!ctaUrl) {
76
+ throw new Error('cta_url is required when generate_outro_image is true for createVideoFromImageList');
77
+ }
78
+ }
79
+ else if (normalized.add_outro_focus_area === true && normalized.add_outro_animation !== true) {
80
+ throw new Error('add_outro_focus_area requires add_outro_animation to be true for createVideoFromImageList');
81
+ }
82
+ return normalized;
83
+ }
18
84
  export class SamsarClient {
19
85
  constructor(options) {
20
86
  if (!options?.apiKey) {
@@ -44,8 +110,9 @@ export class SamsarClient {
44
110
  * Create a video from a list of image URLs with optional prompt/metadata via the image_list_to_video route.
45
111
  */
46
112
  async createVideoFromImageList(input, options) {
113
+ const normalizedInput = normalizeCreateVideoFromImageListInput(input);
47
114
  const body = {
48
- input,
115
+ input: normalizedInput,
49
116
  webhookUrl: options?.webhookUrl,
50
117
  };
51
118
  const response = await this.post('video/image_list_to_video', body, options);
@@ -99,9 +166,10 @@ export class SamsarClient {
99
166
  * Create an image-list-to-video request attributed to an external user while billing against the shared API key.
100
167
  */
101
168
  async createExternalVideoFromImageList(externalUser, input, options) {
169
+ const normalizedInput = normalizeCreateVideoFromImageListInput(input);
102
170
  const body = {
103
171
  external_user: normalizeExternalUserIdentity(externalUser),
104
- input,
172
+ input: normalizedInput,
105
173
  webhookUrl: options?.webhookUrl,
106
174
  };
107
175
  return this.post('external_users/image_list_to_video', body, options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "samsar-js",
3
- "version": "0.48.14",
3
+ "version": "0.48.16",
4
4
  "description": "TypeScript client for the Samsar Processor API routes.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",