samsar-js 0.48.18 → 0.48.19

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
@@ -423,6 +423,20 @@ const externalRender = await platform.createExternalVideoFromText(externalUser,
423
423
  enable_subtitles: true,
424
424
  });
425
425
 
426
+ // Update an external user's existing outro by generating a new QR CTA outro
427
+ const externalOutroUpdate = await platform.updateExternalVideoOutroImage(externalUser, {
428
+ request_id: externalRender.data.request_id,
429
+ generate_outro_image: true,
430
+ cta_url: 'https://example.com/shop',
431
+ cta_text_top: 'Scan to shop',
432
+ cta_text_bottom: 'Limited drop',
433
+ add_outro_animation: true,
434
+ });
435
+ console.log(externalOutroUpdate.data.request_id);
436
+
437
+ // Repeated video routes accept the returned extreq_ id or the normalized external id.
438
+ // The API resolves ownership through external request mappings and GlobalSession records.
439
+
426
440
  // Run an assistant completion against one of that external user's sessions.
427
441
  // Credits are deducted from the external user, while the owning Samsar account model config is used internally.
428
442
  const externalAssistant = await platform.createExternalAssistantCompletion(
@@ -491,9 +505,36 @@ Video model support notes:
491
505
  - `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.
492
506
  - `createVideoFromImageList` can render per-scene footer QR cards by setting `add_footer_animation: true` and providing one `footer_metadata` item per image scene.
493
507
  - `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.
508
+ - 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`, `addVideoOutroImage`, and `updateVideoOutroImage`; the explicit external variants are available when you want to call `/external_users/*` directly. Do not strip the `extreq_` prefix.
494
509
  - `publishPublication`, `editPublication`, and `revokePublication` manage public feed publications for completed sessions through free `/publications/*` endpoints. They work with account API keys, customer sub-account API keys, and client auth tokens when the session belongs to the authenticated actor.
495
510
  - 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.
496
511
 
512
+ Upcoming `/v2` omni route adapters:
513
+ - `/v2` is additive; `/v1` is not deprecated.
514
+ - `createV2VideoFromText`, `createV2VideoFromImageList`, `updateV2VideoOutroImage`, `addV2VideoOutroImage`, `getV2Status`, `getV2Credits`, `listV2Requests`, and `createV2Session` call the new omni route surface.
515
+ - Omit `externalUser` for internal account billing, pass `externalUser` to scope an external user with the account API key, or authenticate the client directly with an external-user auth token/API key.
516
+
517
+ ```ts
518
+ const v2Video = await platform.createV2VideoFromImageList({
519
+ image_urls: ['https://cdn.example.com/a.png', 'https://cdn.example.com/b.png'],
520
+ video_model: 'RUNWAYML',
521
+ generate_outro_image: true,
522
+ cta_url: 'https://example.com/book',
523
+ cta_text_top: 'Scan to book',
524
+ });
525
+
526
+ const v2ExternalVideo = await platform.createV2VideoFromImageList(
527
+ {
528
+ image_urls: ['https://cdn.example.com/a.png', 'https://cdn.example.com/b.png'],
529
+ video_model: 'KLING3.0',
530
+ },
531
+ { externalUser },
532
+ );
533
+
534
+ const v2Status = await platform.getV2Status(v2Video.data.request_id!);
535
+ console.log(v2Status.data.status);
536
+ ```
537
+
497
538
  Each method returns `{ data, status, headers, creditsCharged, creditsRemaining, raw }`. Non-2xx responses throw `SamsarRequestError` containing status, body, and credit headers (if present).
498
539
 
499
540
  ## Billing notes
package/dist/index.d.ts CHANGED
@@ -224,6 +224,12 @@ export interface UpdateVideoOutroImageInput {
224
224
  sessionID?: string;
225
225
  request_id?: string;
226
226
  requestId?: string;
227
+ source_request_id?: string;
228
+ sourceRequestId?: string;
229
+ external_request_id?: string;
230
+ externalRequestId?: string;
231
+ external_session_id?: string;
232
+ externalSessionId?: string;
227
233
  outro_image_url?: string;
228
234
  outroImageUrl?: string;
229
235
  new_outro_image_url?: string;
@@ -265,6 +271,12 @@ export interface AddVideoOutroImageInput {
265
271
  sessionID?: string;
266
272
  request_id?: string;
267
273
  requestId?: string;
274
+ source_request_id?: string;
275
+ sourceRequestId?: string;
276
+ external_request_id?: string;
277
+ externalRequestId?: string;
278
+ external_session_id?: string;
279
+ externalSessionId?: string;
268
280
  outro_image_url?: string;
269
281
  outroImageUrl?: string;
270
282
  new_outro_image_url?: string;
@@ -1294,6 +1306,34 @@ export interface ExternalRequestsListResponse {
1294
1306
  externalUser?: ExternalUserSummary | null;
1295
1307
  [key: string]: unknown;
1296
1308
  }
1309
+ export interface V2RequestOptions extends SamsarRequestOptions {
1310
+ externalUser?: ExternalUserIdentity | null;
1311
+ webhookUrl?: string;
1312
+ }
1313
+ export interface V2SessionResponse {
1314
+ account_type?: 'internal' | 'external' | string;
1315
+ auth_type?: string;
1316
+ user?: {
1317
+ id?: string;
1318
+ email?: string | null;
1319
+ displayName?: string | null;
1320
+ username?: string | null;
1321
+ pfpUrl?: string | null;
1322
+ preferredLanguage?: string | null;
1323
+ [key: string]: unknown;
1324
+ };
1325
+ external_api_key?: string | null;
1326
+ external_user?: ExternalUserSummary | null;
1327
+ externalUser?: ExternalUserSummary | null;
1328
+ remainingCredits?: number | null;
1329
+ [key: string]: unknown;
1330
+ }
1331
+ export interface V2RequestsListResponse {
1332
+ requests: Array<ExternalRequestSummary | Record<string, unknown>>;
1333
+ external_user?: ExternalUserSummary | null;
1334
+ externalUser?: ExternalUserSummary | null;
1335
+ [key: string]: unknown;
1336
+ }
1297
1337
  export interface ExternalArchiveResponse {
1298
1338
  request?: ExternalRequestSummary | null;
1299
1339
  external_user?: ExternalUserSummary | null;
@@ -1483,6 +1523,32 @@ export declare class SamsarClient {
1483
1523
  private readonly defaultHeaders;
1484
1524
  private readonly externalUserApiKey?;
1485
1525
  constructor(options: SamsarClientOptions);
1526
+ /**
1527
+ * Low-level POST adapter for the upcoming /v2 omni routes.
1528
+ * Pass options.externalUser to include an external_user payload; omit it for internal-account calls
1529
+ * or when authenticating directly with an external-user auth token/API key.
1530
+ */
1531
+ postV2<T = Record<string, unknown>>(path: string, payload?: Record<string, unknown>, options?: V2RequestOptions): Promise<SamsarResult<T>>;
1532
+ /**
1533
+ * Low-level GET adapter for the upcoming /v2 omni routes.
1534
+ */
1535
+ getV2<T = Record<string, unknown>>(path: string, options?: V2RequestOptions): Promise<SamsarResult<T>>;
1536
+ createV2Session(options?: V2RequestOptions): Promise<SamsarResult<V2SessionResponse>>;
1537
+ getV2Credits(options?: V2RequestOptions): Promise<SamsarResult<CreditsBalanceResponse | ExternalCreditsBalanceResponse>>;
1538
+ listV2Requests(options?: V2RequestOptions): Promise<SamsarResult<V2RequestsListResponse>>;
1539
+ createV2LoginToken(options?: V2RequestOptions & {
1540
+ redirect?: string;
1541
+ }): Promise<SamsarResult<CreateLoginTokenResponse | ExternalCreateLoginTokenResponse>>;
1542
+ createV2VideoFromText(input: CreateVideoFromTextInput, options?: V2RequestOptions): Promise<SamsarResult<CreateVideoResponse | ExternalRequestResponse>>;
1543
+ createV2VideoFromImageList(input: CreateVideoFromImageListInput, options?: V2RequestOptions): Promise<SamsarResult<CreateVideoFromImageListResponse | ExternalRequestResponse>>;
1544
+ uploadV2ImageData(imageData: string[], options?: V2RequestOptions): Promise<SamsarResult<{
1545
+ image_urls: string[];
1546
+ }>>;
1547
+ updateV2VideoOutroImage(input: UpdateVideoOutroImageInput, options?: V2RequestOptions): Promise<SamsarResult<UpdateVideoOutroImageResponse | ExternalRequestResponse>>;
1548
+ addV2VideoOutroImage(input: AddVideoOutroImageInput, options?: V2RequestOptions): Promise<SamsarResult<AddVideoOutroImageResponse | ExternalRequestResponse>>;
1549
+ getV2Status(requestId: string, options?: V2RequestOptions & {
1550
+ queryParams?: QueryParams;
1551
+ }): Promise<SamsarResult<GlobalStatusResponse | ExternalStatusResponse>>;
1486
1552
  /**
1487
1553
  * Create a new video generation job from text.
1488
1554
  */
@@ -1584,6 +1650,13 @@ export declare class SamsarClient {
1584
1650
  updateVideoOutroImage(input: UpdateVideoOutroImageInput, options?: {
1585
1651
  webhookUrl?: string;
1586
1652
  } & SamsarRequestOptions): Promise<SamsarResult<UpdateVideoOutroImageResponse>>;
1653
+ /**
1654
+ * Update the outro image for an external-user video request by resolving the external request id
1655
+ * through /external_users/update_outro_image.
1656
+ */
1657
+ updateExternalVideoOutroImage(externalUser: ExternalUserIdentity, input: UpdateVideoOutroImageInput, options?: {
1658
+ webhookUrl?: string;
1659
+ } & SamsarRequestOptions): Promise<SamsarResult<ExternalRequestResponse>>;
1587
1660
  /**
1588
1661
  * Add or replace the outro image for an existing video session by cloning it into a new session and
1589
1662
  * re-running frame/video generation.
@@ -1591,6 +1664,12 @@ export declare class SamsarClient {
1591
1664
  addVideoOutroImage(input: AddVideoOutroImageInput, options?: {
1592
1665
  webhookUrl?: string;
1593
1666
  } & SamsarRequestOptions): Promise<SamsarResult<AddVideoOutroImageResponse>>;
1667
+ /**
1668
+ * Add or replace an outro image for an external-user video request by resolving the external request id.
1669
+ */
1670
+ addExternalVideoOutroImage(externalUser: ExternalUserIdentity, input: AddVideoOutroImageInput, options?: {
1671
+ webhookUrl?: string;
1672
+ } & SamsarRequestOptions): Promise<SamsarResult<ExternalRequestResponse>>;
1594
1673
  /**
1595
1674
  * Fetch the latest available render URL for a given video session id.
1596
1675
  * Maps to GET /video/fetch_latest_version?session_id={sessionId}.
@@ -1826,5 +1905,7 @@ export declare class SamsarClient {
1826
1905
  private buildHeaders;
1827
1906
  private buildUrl;
1828
1907
  private buildRootUrl;
1908
+ private withV2ExternalUser;
1909
+ private buildV2Url;
1829
1910
  }
1830
1911
  export default SamsarClient;
package/dist/index.js CHANGED
@@ -126,6 +126,96 @@ function normalizeCreateVideoFromImageListInput(input) {
126
126
  }
127
127
  return normalized;
128
128
  }
129
+ function normalizeUpdateVideoOutroImageInput(input, context = 'updateVideoOutroImage') {
130
+ const raw = input;
131
+ const videoSessionId = raw.videoSessionId ??
132
+ raw.video_session_id ??
133
+ raw.videoSessionID ??
134
+ raw.session_id ??
135
+ raw.sessionId ??
136
+ raw.sessionID ??
137
+ raw.request_id ??
138
+ raw.requestId ??
139
+ raw.source_request_id ??
140
+ raw.sourceRequestId ??
141
+ raw.external_request_id ??
142
+ raw.externalRequestId ??
143
+ raw.external_session_id ??
144
+ raw.externalSessionId;
145
+ const outroImageUrl = raw.outro_image_url ??
146
+ raw.outroImageUrl ??
147
+ raw.new_outro_image_url ??
148
+ raw.newOutroImageUrl;
149
+ const rawGenerateOutroImage = raw.generate_outro_image ??
150
+ raw.generateOutroImage;
151
+ const rawAddOutroAnimation = raw.add_outro_animation ??
152
+ raw.addOutroAnimation;
153
+ const rawAddOutroFocusArea = raw.add_outro_focus_area ??
154
+ raw.addOutroFocusArea;
155
+ const rawOutroFocusArea = raw.outro_focust_area ??
156
+ raw.outro_focus_area ??
157
+ raw.outroFocustArea ??
158
+ raw.outroFocusArea;
159
+ const ctaUrl = raw.cta_url ??
160
+ raw.ctaUrl;
161
+ const ctaTextTop = raw.cta_text_top ??
162
+ raw.ctaTextTop;
163
+ const ctaTextBottom = raw.cta_text_bottom ??
164
+ raw.ctaTextBottom;
165
+ const ctaLogo = raw.cta_logo ??
166
+ raw.ctaLogo;
167
+ const generateOutroImage = rawGenerateOutroImage === true ||
168
+ (rawGenerateOutroImage === undefined && !outroImageUrl && Boolean(ctaUrl));
169
+ if (!videoSessionId) {
170
+ throw new Error(`videoSessionId is required for ${context}`);
171
+ }
172
+ if (rawGenerateOutroImage !== undefined && typeof rawGenerateOutroImage !== 'boolean') {
173
+ throw new Error(`generate_outro_image must be a boolean for ${context}`);
174
+ }
175
+ if (rawAddOutroAnimation !== undefined && typeof rawAddOutroAnimation !== 'boolean') {
176
+ throw new Error(`add_outro_animation must be a boolean for ${context}`);
177
+ }
178
+ if (rawAddOutroFocusArea !== undefined && typeof rawAddOutroFocusArea !== 'boolean') {
179
+ throw new Error(`add_outro_focus_area must be a boolean for ${context}`);
180
+ }
181
+ if (generateOutroImage && outroImageUrl) {
182
+ throw new Error(`Use either generate_outro_image with cta_url or outro_image_url for ${context}`);
183
+ }
184
+ if (!generateOutroImage && !outroImageUrl) {
185
+ throw new Error(`outro_image_url is required for ${context} unless generate_outro_image is true`);
186
+ }
187
+ if (generateOutroImage) {
188
+ if (!ctaUrl || !String(ctaUrl).trim()) {
189
+ throw new Error(`cta_url is required when generate_outro_image is true for ${context}`);
190
+ }
191
+ }
192
+ else if (rawAddOutroFocusArea === true && rawAddOutroAnimation !== true) {
193
+ throw new Error(`add_outro_focus_area requires add_outro_animation to be true for ${context}`);
194
+ }
195
+ if (!generateOutroImage && rawAddOutroFocusArea === true) {
196
+ if (!rawOutroFocusArea || typeof rawOutroFocusArea !== 'object' || Array.isArray(rawOutroFocusArea)) {
197
+ throw new Error(`outro_focust_area must be an object with x, y, width, height for ${context}`);
198
+ }
199
+ const { x, y, width, height } = rawOutroFocusArea;
200
+ const isInvalid = [x, y, width, height].some((value) => typeof value !== 'number' || !Number.isFinite(value));
201
+ if (isInvalid) {
202
+ throw new Error(`outro_focust_area x, y, width, height must be valid numbers for ${context}`);
203
+ }
204
+ }
205
+ return {
206
+ ...input,
207
+ videoSessionId: String(videoSessionId),
208
+ ...(outroImageUrl ? { outro_image_url: String(outroImageUrl) } : {}),
209
+ generate_outro_image: generateOutroImage,
210
+ ...(generateOutroImage ? { cta_url: String(ctaUrl).trim() } : {}),
211
+ ...(ctaTextTop ? { cta_text_top: String(ctaTextTop) } : {}),
212
+ ...(ctaTextBottom ? { cta_text_bottom: String(ctaTextBottom) } : {}),
213
+ ...(ctaLogo ? { cta_logo: String(ctaLogo) } : {}),
214
+ ...(rawAddOutroAnimation !== undefined ? { add_outro_animation: rawAddOutroAnimation === true } : {}),
215
+ ...(rawAddOutroFocusArea !== undefined ? { add_outro_focus_area: rawAddOutroFocusArea === true } : {}),
216
+ ...(rawOutroFocusArea !== undefined ? { outro_focust_area: rawOutroFocusArea } : {}),
217
+ };
218
+ }
129
219
  function normalizeSessionPublicationInput(input, context) {
130
220
  if (typeof input !== 'string' && (!input || typeof input !== 'object' || Array.isArray(input))) {
131
221
  throw new Error(`input must be a session id or object for ${context}`);
@@ -193,6 +283,85 @@ export class SamsarClient {
193
283
  throw new Error('A fetch implementation is required. Provide one via the "fetch" option when running outside environments with global fetch.');
194
284
  }
195
285
  }
286
+ /**
287
+ * Low-level POST adapter for the upcoming /v2 omni routes.
288
+ * Pass options.externalUser to include an external_user payload; omit it for internal-account calls
289
+ * or when authenticating directly with an external-user auth token/API key.
290
+ */
291
+ async postV2(path, payload = {}, options) {
292
+ return this.post(this.buildV2Url(path), this.withV2ExternalUser(payload, options), options);
293
+ }
294
+ /**
295
+ * Low-level GET adapter for the upcoming /v2 omni routes.
296
+ */
297
+ async getV2(path, options) {
298
+ const query = {
299
+ ...(options?.externalUser ? buildExternalUserQuery(options.externalUser) : {}),
300
+ ...(options?.query ?? {}),
301
+ };
302
+ return this.get(this.buildV2Url(path), { ...(options ?? {}), query });
303
+ }
304
+ async createV2Session(options) {
305
+ return this.postV2('session', {}, options);
306
+ }
307
+ async getV2Credits(options) {
308
+ return this.getV2('credits', options);
309
+ }
310
+ async listV2Requests(options) {
311
+ return this.getV2('requests', options);
312
+ }
313
+ async createV2LoginToken(options) {
314
+ const body = options?.redirect ? { redirect: options.redirect } : {};
315
+ return this.postV2('create_login_token', body, options);
316
+ }
317
+ async createV2VideoFromText(input, options) {
318
+ const normalizedInput = normalizeCreateVideoFromTextInput(input);
319
+ return this.postV2('text_to_video', {
320
+ input: normalizedInput,
321
+ webhookUrl: options?.webhookUrl,
322
+ }, options);
323
+ }
324
+ async createV2VideoFromImageList(input, options) {
325
+ const normalizedInput = normalizeCreateVideoFromImageListInput(input);
326
+ return this.postV2('image_list_to_video', {
327
+ input: normalizedInput,
328
+ webhookUrl: options?.webhookUrl,
329
+ }, options);
330
+ }
331
+ async uploadV2ImageData(imageData, options) {
332
+ if (!Array.isArray(imageData) || imageData.length === 0) {
333
+ throw new Error('imageData must be a non-empty array of data URLs');
334
+ }
335
+ return this.postV2('upload_image_data', {
336
+ input: {
337
+ image_data: imageData,
338
+ },
339
+ }, options);
340
+ }
341
+ async updateV2VideoOutroImage(input, options) {
342
+ const normalizedInput = normalizeUpdateVideoOutroImageInput(input, 'updateV2VideoOutroImage');
343
+ return this.postV2('update_outro_image', {
344
+ input: normalizedInput,
345
+ webhookUrl: options?.webhookUrl,
346
+ }, options);
347
+ }
348
+ async addV2VideoOutroImage(input, options) {
349
+ return this.postV2('add_outro_image', {
350
+ input,
351
+ webhookUrl: options?.webhookUrl,
352
+ }, options);
353
+ }
354
+ async getV2Status(requestId, options) {
355
+ const queryParams = {
356
+ ...(options?.externalUser ? buildExternalUserQuery(options.externalUser) : {}),
357
+ ...(options?.queryParams ?? {}),
358
+ };
359
+ return this.getStatus(requestId, {
360
+ ...options,
361
+ path: this.buildV2Url('status'),
362
+ queryParams,
363
+ });
364
+ }
196
365
  /**
197
366
  * Create a new video generation job from text.
198
367
  */
@@ -563,6 +732,50 @@ export class SamsarClient {
563
732
  * only the final video render step.
564
733
  */
565
734
  async updateVideoOutroImage(input, options) {
735
+ const normalizedInput = normalizeUpdateVideoOutroImageInput(input, 'updateVideoOutroImage');
736
+ const body = {
737
+ input: normalizedInput,
738
+ webhookUrl: options?.webhookUrl,
739
+ };
740
+ const response = await this.post('video/update_outro_image', body, options);
741
+ const data = response.data;
742
+ if (data && typeof data === 'object') {
743
+ const sessionId = typeof data.sessionID === 'string'
744
+ ? data.sessionID
745
+ : typeof data.session_id === 'string'
746
+ ? data.session_id
747
+ : typeof data.request_id === 'string'
748
+ ? data.request_id
749
+ : undefined;
750
+ const normalizedSessionId = sessionId ? String(sessionId) : undefined;
751
+ const normalizedData = {
752
+ ...data,
753
+ sessionID: data.sessionID ?? normalizedSessionId ?? '',
754
+ session_id: data.session_id ?? normalizedSessionId,
755
+ request_id: data.request_id ?? normalizedSessionId,
756
+ };
757
+ return { ...response, data: normalizedData };
758
+ }
759
+ return response;
760
+ }
761
+ /**
762
+ * Update the outro image for an external-user video request by resolving the external request id
763
+ * through /external_users/update_outro_image.
764
+ */
765
+ async updateExternalVideoOutroImage(externalUser, input, options) {
766
+ const normalizedInput = normalizeUpdateVideoOutroImageInput(input, 'updateExternalVideoOutroImage');
767
+ const body = {
768
+ external_user: normalizeExternalUserIdentity(externalUser),
769
+ input: normalizedInput,
770
+ webhookUrl: options?.webhookUrl,
771
+ };
772
+ return this.post('external_users/update_outro_image', body, options);
773
+ }
774
+ /**
775
+ * Add or replace the outro image for an existing video session by cloning it into a new session and
776
+ * re-running frame/video generation.
777
+ */
778
+ async addVideoOutroImage(input, options) {
566
779
  const raw = input;
567
780
  const videoSessionId = raw.videoSessionId ??
568
781
  raw.video_session_id ??
@@ -576,8 +789,6 @@ export class SamsarClient {
576
789
  raw.outroImageUrl ??
577
790
  raw.new_outro_image_url ??
578
791
  raw.newOutroImageUrl;
579
- const rawGenerateOutroImage = raw.generate_outro_image ??
580
- raw.generateOutroImage;
581
792
  const rawAddOutroAnimation = raw.add_outro_animation ??
582
793
  raw.addOutroAnimation;
583
794
  const rawAddOutroFocusArea = raw.add_outro_focus_area ??
@@ -586,69 +797,43 @@ export class SamsarClient {
586
797
  raw.outro_focus_area ??
587
798
  raw.outroFocustArea ??
588
799
  raw.outroFocusArea;
589
- const ctaUrl = raw.cta_url ??
590
- raw.ctaUrl;
591
- const ctaTextTop = raw.cta_text_top ??
592
- raw.ctaTextTop;
593
- const ctaTextBottom = raw.cta_text_bottom ??
594
- raw.ctaTextBottom;
595
- const ctaLogo = raw.cta_logo ??
596
- raw.ctaLogo;
597
- const generateOutroImage = rawGenerateOutroImage === true ||
598
- (rawGenerateOutroImage === undefined && !outroImageUrl && Boolean(ctaUrl));
599
800
  if (!videoSessionId) {
600
- throw new Error('videoSessionId is required for updateVideoOutroImage');
801
+ throw new Error('videoSessionId is required for addVideoOutroImage');
601
802
  }
602
- if (rawGenerateOutroImage !== undefined && typeof rawGenerateOutroImage !== 'boolean') {
603
- throw new Error('generate_outro_image must be a boolean for updateVideoOutroImage');
803
+ if (!outroImageUrl) {
804
+ throw new Error('outro_image_url is required for addVideoOutroImage');
604
805
  }
605
806
  if (rawAddOutroAnimation !== undefined && typeof rawAddOutroAnimation !== 'boolean') {
606
- throw new Error('add_outro_animation must be a boolean for updateVideoOutroImage');
807
+ throw new Error('add_outro_animation must be a boolean for addVideoOutroImage');
607
808
  }
608
809
  if (rawAddOutroFocusArea !== undefined && typeof rawAddOutroFocusArea !== 'boolean') {
609
- throw new Error('add_outro_focus_area must be a boolean for updateVideoOutroImage');
610
- }
611
- if (generateOutroImage && outroImageUrl) {
612
- throw new Error('Use either generate_outro_image with cta_url or outro_image_url for updateVideoOutroImage');
613
- }
614
- if (!generateOutroImage && !outroImageUrl) {
615
- throw new Error('outro_image_url is required for updateVideoOutroImage unless generate_outro_image is true');
616
- }
617
- if (generateOutroImage) {
618
- if (!ctaUrl || !String(ctaUrl).trim()) {
619
- throw new Error('cta_url is required when generate_outro_image is true for updateVideoOutroImage');
620
- }
810
+ throw new Error('add_outro_focus_area must be a boolean for addVideoOutroImage');
621
811
  }
622
- else if (rawAddOutroFocusArea === true && rawAddOutroAnimation !== true) {
623
- throw new Error('add_outro_focus_area requires add_outro_animation to be true for updateVideoOutroImage');
812
+ if (rawAddOutroFocusArea === true && rawAddOutroAnimation !== true) {
813
+ throw new Error('add_outro_focus_area requires add_outro_animation to be true for addVideoOutroImage');
624
814
  }
625
- if (!generateOutroImage && rawAddOutroFocusArea === true) {
815
+ if (rawAddOutroFocusArea === true) {
626
816
  if (!rawOutroFocusArea || typeof rawOutroFocusArea !== 'object' || Array.isArray(rawOutroFocusArea)) {
627
- throw new Error('outro_focust_area must be an object with x, y, width, height for updateVideoOutroImage');
817
+ throw new Error('outro_focust_area must be an object with x, y, width, height for addVideoOutroImage');
628
818
  }
629
819
  const { x, y, width, height } = rawOutroFocusArea;
630
820
  const isInvalid = [x, y, width, height].some((value) => typeof value !== 'number' || !Number.isFinite(value));
631
821
  if (isInvalid) {
632
- throw new Error('outro_focust_area x, y, width, height must be valid numbers for updateVideoOutroImage');
822
+ throw new Error('outro_focust_area x, y, width, height must be valid numbers for addVideoOutroImage');
633
823
  }
634
824
  }
635
825
  const body = {
636
826
  input: {
637
827
  ...input,
638
828
  videoSessionId: String(videoSessionId),
639
- ...(outroImageUrl ? { outro_image_url: String(outroImageUrl) } : {}),
640
- generate_outro_image: generateOutroImage,
641
- ...(generateOutroImage ? { cta_url: String(ctaUrl).trim() } : {}),
642
- ...(ctaTextTop ? { cta_text_top: String(ctaTextTop) } : {}),
643
- ...(ctaTextBottom ? { cta_text_bottom: String(ctaTextBottom) } : {}),
644
- ...(ctaLogo ? { cta_logo: String(ctaLogo) } : {}),
829
+ outro_image_url: String(outroImageUrl),
645
830
  ...(rawAddOutroAnimation !== undefined ? { add_outro_animation: rawAddOutroAnimation === true } : {}),
646
831
  ...(rawAddOutroFocusArea !== undefined ? { add_outro_focus_area: rawAddOutroFocusArea === true } : {}),
647
832
  ...(rawOutroFocusArea !== undefined ? { outro_focust_area: rawOutroFocusArea } : {}),
648
833
  },
649
834
  webhookUrl: options?.webhookUrl,
650
835
  };
651
- const response = await this.post('video/update_outro_image', body, options);
836
+ const response = await this.post('video/add_outro_image', body, options);
652
837
  const data = response.data;
653
838
  if (data && typeof data === 'object') {
654
839
  const sessionId = typeof data.sessionID === 'string'
@@ -670,10 +855,9 @@ export class SamsarClient {
670
855
  return response;
671
856
  }
672
857
  /**
673
- * Add or replace the outro image for an existing video session by cloning it into a new session and
674
- * re-running frame/video generation.
858
+ * Add or replace an outro image for an external-user video request by resolving the external request id.
675
859
  */
676
- async addVideoOutroImage(input, options) {
860
+ async addExternalVideoOutroImage(externalUser, input, options) {
677
861
  const raw = input;
678
862
  const videoSessionId = raw.videoSessionId ??
679
863
  raw.video_session_id ??
@@ -682,7 +866,13 @@ export class SamsarClient {
682
866
  raw.sessionId ??
683
867
  raw.sessionID ??
684
868
  raw.request_id ??
685
- raw.requestId;
869
+ raw.requestId ??
870
+ raw.source_request_id ??
871
+ raw.sourceRequestId ??
872
+ raw.external_request_id ??
873
+ raw.externalRequestId ??
874
+ raw.external_session_id ??
875
+ raw.externalSessionId;
686
876
  const outroImageUrl = raw.outro_image_url ??
687
877
  raw.outroImageUrl ??
688
878
  raw.new_outro_image_url ??
@@ -696,31 +886,32 @@ export class SamsarClient {
696
886
  raw.outroFocustArea ??
697
887
  raw.outroFocusArea;
698
888
  if (!videoSessionId) {
699
- throw new Error('videoSessionId is required for addVideoOutroImage');
889
+ throw new Error('videoSessionId is required for addExternalVideoOutroImage');
700
890
  }
701
891
  if (!outroImageUrl) {
702
- throw new Error('outro_image_url is required for addVideoOutroImage');
892
+ throw new Error('outro_image_url is required for addExternalVideoOutroImage');
703
893
  }
704
894
  if (rawAddOutroAnimation !== undefined && typeof rawAddOutroAnimation !== 'boolean') {
705
- throw new Error('add_outro_animation must be a boolean for addVideoOutroImage');
895
+ throw new Error('add_outro_animation must be a boolean for addExternalVideoOutroImage');
706
896
  }
707
897
  if (rawAddOutroFocusArea !== undefined && typeof rawAddOutroFocusArea !== 'boolean') {
708
- throw new Error('add_outro_focus_area must be a boolean for addVideoOutroImage');
898
+ throw new Error('add_outro_focus_area must be a boolean for addExternalVideoOutroImage');
709
899
  }
710
900
  if (rawAddOutroFocusArea === true && rawAddOutroAnimation !== true) {
711
- throw new Error('add_outro_focus_area requires add_outro_animation to be true for addVideoOutroImage');
901
+ throw new Error('add_outro_focus_area requires add_outro_animation to be true for addExternalVideoOutroImage');
712
902
  }
713
903
  if (rawAddOutroFocusArea === true) {
714
904
  if (!rawOutroFocusArea || typeof rawOutroFocusArea !== 'object' || Array.isArray(rawOutroFocusArea)) {
715
- throw new Error('outro_focust_area must be an object with x, y, width, height for addVideoOutroImage');
905
+ throw new Error('outro_focust_area must be an object with x, y, width, height for addExternalVideoOutroImage');
716
906
  }
717
907
  const { x, y, width, height } = rawOutroFocusArea;
718
908
  const isInvalid = [x, y, width, height].some((value) => typeof value !== 'number' || !Number.isFinite(value));
719
909
  if (isInvalid) {
720
- throw new Error('outro_focust_area x, y, width, height must be valid numbers for addVideoOutroImage');
910
+ throw new Error('outro_focust_area x, y, width, height must be valid numbers for addExternalVideoOutroImage');
721
911
  }
722
912
  }
723
913
  const body = {
914
+ external_user: normalizeExternalUserIdentity(externalUser),
724
915
  input: {
725
916
  ...input,
726
917
  videoSessionId: String(videoSessionId),
@@ -731,26 +922,7 @@ export class SamsarClient {
731
922
  },
732
923
  webhookUrl: options?.webhookUrl,
733
924
  };
734
- const response = await this.post('video/add_outro_image', body, options);
735
- const data = response.data;
736
- if (data && typeof data === 'object') {
737
- const sessionId = typeof data.sessionID === 'string'
738
- ? data.sessionID
739
- : typeof data.session_id === 'string'
740
- ? data.session_id
741
- : typeof data.request_id === 'string'
742
- ? data.request_id
743
- : undefined;
744
- const normalizedSessionId = sessionId ? String(sessionId) : undefined;
745
- const normalizedData = {
746
- ...data,
747
- sessionID: data.sessionID ?? normalizedSessionId ?? '',
748
- session_id: data.session_id ?? normalizedSessionId,
749
- request_id: data.request_id ?? normalizedSessionId,
750
- };
751
- return { ...response, data: normalizedData };
752
- }
753
- return response;
925
+ return this.post('external_users/add_outro_image', body, options);
754
926
  }
755
927
  /**
756
928
  * Fetch the latest available render URL for a given video session id.
@@ -1478,6 +1650,19 @@ export class SamsarClient {
1478
1650
  const rootUrl = baseUrl.endsWith('/v1') ? baseUrl.slice(0, -3) : baseUrl;
1479
1651
  return `${rootUrl}/${cleanedPath}`;
1480
1652
  }
1653
+ withV2ExternalUser(body, options) {
1654
+ if (!options?.externalUser) {
1655
+ return body;
1656
+ }
1657
+ return {
1658
+ ...body,
1659
+ external_user: normalizeExternalUserIdentity(options.externalUser),
1660
+ };
1661
+ }
1662
+ buildV2Url(path) {
1663
+ const cleanedPath = path.replace(/^\/+/, '');
1664
+ return this.buildRootUrl(`v2/${cleanedPath}`);
1665
+ }
1481
1666
  }
1482
1667
  function trimTrailingSlash(url) {
1483
1668
  return url.replace(/\/+$/, '');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "samsar-js",
3
- "version": "0.48.18",
3
+ "version": "0.48.19",
4
4
  "description": "TypeScript client for the Samsar Processor API routes.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",