samsar-js 0.48.22 → 0.48.24

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
@@ -131,6 +131,23 @@ await samsar.updateVideoOutroImage(
131
131
  { webhookUrl: 'https://example.com/webhook' },
132
132
  );
133
133
 
134
+ // Update the footer CTA on an existing video session
135
+ await samsar.updateVideoFooterImage(
136
+ {
137
+ videoSessionId: videoFromImages.data.session_id ?? videoFromImages.data.request_id!,
138
+ cta_text: 'Scan to book',
139
+ cta_logo: 'https://cdn.example.com/logo-white.png',
140
+ cta_url: 'https://example.com/book',
141
+ },
142
+ { webhookUrl: 'https://example.com/webhook' },
143
+ );
144
+
145
+ // Remove the footer from a cloned rerender
146
+ await samsar.updateVideoFooterImage({
147
+ videoSessionId: videoFromImages.data.session_id ?? videoFromImages.data.request_id!,
148
+ remove_footer: true,
149
+ });
150
+
134
151
  // Translate an existing video session into another language
135
152
  const translated = await samsar.translateVideo(
136
153
  {
@@ -161,9 +178,17 @@ const noSubtitles = await samsar.removeSubtitles(
161
178
  { webhookUrl: 'https://example.com/webhook' },
162
179
  );
163
180
 
181
+ // Clone a session and add subtitle/transcript text overlays
182
+ const withSubtitles = await samsar.addSubtitles(
183
+ {
184
+ videoSessionId: noSubtitles.data.session_id ?? noSubtitles.data.request_id!,
185
+ },
186
+ { webhookUrl: 'https://example.com/webhook' },
187
+ );
188
+
164
189
  // Cancel an in-progress render
165
190
  const cancelled = await samsar.cancelRender({
166
- videoSessionId: noSubtitles.data.session_id ?? noSubtitles.data.request_id!,
191
+ videoSessionId: withSubtitles.data.session_id ?? withSubtitles.data.request_id!,
167
192
  });
168
193
  console.log(cancelled.data.status, cancelled.data.cancelled);
169
194
 
@@ -331,15 +356,18 @@ const rollup = await samsar.enhanceAndGenerateRollupBanner({
331
356
 
332
357
  // Check status by request_id (defaults to /v1/status)
333
358
  const status = await samsar.getStatus(video.data.request_id);
359
+ if (status.data.status === 'COMPLETED') {
360
+ console.log(status.data.result_url, status.data.has_subtitles, status.data.result_language);
361
+ }
334
362
 
335
363
  // Fetch the latest render URL for a session (when available)
336
364
  const latest = await samsar.fetchLatestVideoVersion(video.data.session_id ?? video.data.request_id);
337
- console.log(latest.data.result_url ?? latest.data.status);
365
+ console.log(latest.data.result_url ?? latest.data.status, latest.data.has_subtitles, latest.data.result_language);
338
366
 
339
367
  // List completed video sessions for this API key
340
368
  const completedSessions = await samsar.listCompletedVideoSessions();
341
369
  completedSessions.data.forEach((session) => {
342
- console.log(session.session_id, session.langauge, session.result_url);
370
+ console.log(session.session_id, session.result_language ?? session.langauge, session.has_subtitles, session.result_url);
343
371
  });
344
372
 
345
373
  // Publish, edit, or revoke a completed session in the public publication feed (free endpoints)
@@ -434,6 +462,15 @@ const externalOutroUpdate = await platform.updateExternalVideoOutroImage(externa
434
462
  });
435
463
  console.log(externalOutroUpdate.data.request_id);
436
464
 
465
+ // Update or remove an external user's existing footer CTA.
466
+ const externalFooterUpdate = await platform.updateExternalVideoFooterImage(externalUser, {
467
+ request_id: externalRender.data.request_id,
468
+ cta_text: 'Scan to shop',
469
+ cta_logo: 'https://cdn.example.com/logo-white.png',
470
+ cta_url: 'https://example.com/shop',
471
+ });
472
+ console.log(externalFooterUpdate.data.request_id);
473
+
437
474
  // Repeated video routes accept the returned extreq_ id or the normalized external id.
438
475
  // The API resolves ownership through external request mappings and GlobalSession records.
439
476
 
@@ -505,13 +542,15 @@ Video model support notes:
505
542
  - `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.
506
543
  - `createVideoFromImageList` can render per-scene footer QR cards by setting `add_footer_animation: true` and providing one `footer_metadata` item per image scene.
507
544
  - `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.
545
+ - `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.
546
+ - 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.
547
+ - Completed video status, latest-version, and completed-session list responses expose `has_subtitles` and `result_language` when the session metadata is available.
509
548
  - `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.
510
549
  - 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.
511
550
 
512
551
  Upcoming `/v2` omni route adapters:
513
552
  - `/v2` is additive; `/v1` is not deprecated.
514
- - `createV2VideoFromText`, `createV2VideoFromImageList`, `updateV2VideoOutroImage`, `addV2VideoOutroImage`, `getV2Status`, `getV2Credits`, `listV2Requests`, and `createV2Session` call the new omni route surface.
553
+ - `createV2VideoFromText`, `createV2VideoFromImageList`, `updateV2VideoOutroImage`, `updateV2VideoFooterImage`, `addV2VideoOutroImage`, `getV2Status`, `getV2Credits`, `listV2Requests`, and `createV2Session` call the new omni route surface.
515
554
  - Programmatic user billing helpers include `createV2UserRechargeCredits`, `refreshV2UserToken`, `createV2UserAppKey`, `refreshV2UserAppKey`, `getV2UserCredits`, `getV2UserUsageLogs`, and `getV2UserPaymentStatus`.
516
555
  - 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.
517
556
 
package/dist/index.d.ts CHANGED
@@ -51,6 +51,8 @@ export interface CreateVideoFromTextInput {
51
51
  languageString?: string | null;
52
52
  enable_subtitles?: boolean;
53
53
  enableSubtitles?: boolean;
54
+ add_subtitles?: boolean;
55
+ addSubtitles?: boolean;
54
56
  session_id?: string;
55
57
  sessionId?: string;
56
58
  sessionID?: string;
@@ -93,8 +95,18 @@ export interface OutroFocusArea {
93
95
  height: number;
94
96
  }
95
97
  export interface FooterMetadataItem {
96
- url: string;
98
+ url?: string;
99
+ cta_url?: string;
100
+ ctaUrl?: string;
97
101
  title?: string;
102
+ text?: string;
103
+ cta_text?: string;
104
+ ctaText?: string;
105
+ cta_logo?: string;
106
+ ctaLogo?: string;
107
+ logoUrl?: string;
108
+ logoImagePath?: string;
109
+ footerLogoImagePath?: string;
98
110
  }
99
111
  export type ImageListToVideoAspectRatio = '16:9' | '9:16';
100
112
  export type ImageListToVideoModel = 'VEO3.1I2V' | 'SEEDANCEI2V' | 'KLING3.0' | 'KLINGIMGTOVID3PRO' | 'RUNWAYML';
@@ -141,6 +153,8 @@ export interface CreateVideoFromImageListInput {
141
153
  font?: FontOptions;
142
154
  enable_subtitles?: boolean;
143
155
  enableSubtitles?: boolean;
156
+ add_subtitles?: boolean;
157
+ addSubtitles?: boolean;
144
158
  session_id?: string;
145
159
  sessionId?: string;
146
160
  sessionID?: string;
@@ -266,6 +280,39 @@ export interface UpdateVideoOutroImageResponse {
266
280
  remainingCredits?: number | null;
267
281
  [key: string]: unknown;
268
282
  }
283
+ export interface UpdateVideoFooterImageInput {
284
+ videoSessionId?: string;
285
+ video_session_id?: string;
286
+ videoSessionID?: string;
287
+ session_id?: string;
288
+ sessionId?: string;
289
+ sessionID?: string;
290
+ request_id?: string;
291
+ requestId?: string;
292
+ source_request_id?: string;
293
+ sourceRequestId?: string;
294
+ external_request_id?: string;
295
+ externalRequestId?: string;
296
+ external_session_id?: string;
297
+ externalSessionId?: string;
298
+ remove_footer?: boolean;
299
+ removeFooter?: boolean;
300
+ cta_text?: string;
301
+ ctaText?: string;
302
+ cta_logo?: string;
303
+ ctaLogo?: string;
304
+ cta_url?: string;
305
+ ctaUrl?: string;
306
+ [key: string]: unknown;
307
+ }
308
+ export interface UpdateVideoFooterImageResponse {
309
+ request_id?: string;
310
+ session_id?: string;
311
+ sessionID?: string;
312
+ creditsCharged?: number;
313
+ remainingCredits?: number | null;
314
+ [key: string]: unknown;
315
+ }
269
316
  export interface AddVideoOutroImageInput {
270
317
  videoSessionId?: string;
271
318
  video_session_id?: string;
@@ -339,6 +386,10 @@ export interface RemoveSubtitlesResponse {
339
386
  remainingCredits?: number | null;
340
387
  [key: string]: unknown;
341
388
  }
389
+ export interface AddSubtitlesInput extends RemoveSubtitlesInput {
390
+ }
391
+ export interface AddSubtitlesResponse extends RemoveSubtitlesResponse {
392
+ }
342
393
  export interface CancelRenderInput {
343
394
  videoSessionId?: string;
344
395
  video_session_id?: string;
@@ -362,6 +413,8 @@ export interface CancelRenderResponse {
362
413
  export interface FetchLatestVideoVersionResponse {
363
414
  session_id: string;
364
415
  result_url?: string;
416
+ has_subtitles?: boolean | null;
417
+ result_language?: string | null;
365
418
  status?: string;
366
419
  message?: string;
367
420
  [key: string]: unknown;
@@ -370,6 +423,8 @@ export interface CompletedVideoSession {
370
423
  session_id: string;
371
424
  langauge: string;
372
425
  language?: string;
426
+ result_language?: string;
427
+ has_subtitles?: boolean | null;
373
428
  result_url: string;
374
429
  [key: string]: unknown;
375
430
  }
@@ -1103,6 +1158,8 @@ export interface GlobalStatusResponse {
1103
1158
  type?: 'image' | 'video' | string;
1104
1159
  provider?: string | null;
1105
1160
  result_url?: string | null;
1161
+ has_subtitles?: boolean | null;
1162
+ result_language?: string | null;
1106
1163
  thumbnail_url?: string | null;
1107
1164
  result_urls?: string[];
1108
1165
  videoLink?: string | null;
@@ -1664,6 +1721,7 @@ export declare class SamsarClient {
1664
1721
  image_urls: string[];
1665
1722
  }>>;
1666
1723
  updateV2VideoOutroImage(input: UpdateVideoOutroImageInput, options?: V2RequestOptions): Promise<SamsarResult<UpdateVideoOutroImageResponse | ExternalRequestResponse>>;
1724
+ updateV2VideoFooterImage(input: UpdateVideoFooterImageInput, options?: V2RequestOptions): Promise<SamsarResult<UpdateVideoFooterImageResponse | ExternalRequestResponse>>;
1667
1725
  addV2VideoOutroImage(input: AddVideoOutroImageInput, options?: V2RequestOptions): Promise<SamsarResult<AddVideoOutroImageResponse | ExternalRequestResponse>>;
1668
1726
  getV2Status(requestId: string, options?: V2RequestOptions & {
1669
1727
  queryParams?: QueryParams;
@@ -1758,6 +1816,13 @@ export declare class SamsarClient {
1758
1816
  removeSubtitles(input: RemoveSubtitlesInput, options?: {
1759
1817
  webhookUrl?: string;
1760
1818
  } & SamsarRequestOptions): Promise<SamsarResult<RemoveSubtitlesResponse>>;
1819
+ /**
1820
+ * Add subtitle/transcript text overlays by cloning an existing session and re-running
1821
+ * transcription + frame + video generation on the new session.
1822
+ */
1823
+ addSubtitles(input: AddSubtitlesInput, options?: {
1824
+ webhookUrl?: string;
1825
+ } & SamsarRequestOptions): Promise<SamsarResult<AddSubtitlesResponse>>;
1761
1826
  /**
1762
1827
  * Cancel an in-progress render for an existing video session.
1763
1828
  */
@@ -1776,6 +1841,20 @@ export declare class SamsarClient {
1776
1841
  updateExternalVideoOutroImage(externalUser: ExternalUserIdentity, input: UpdateVideoOutroImageInput, options?: {
1777
1842
  webhookUrl?: string;
1778
1843
  } & SamsarRequestOptions): Promise<SamsarResult<ExternalRequestResponse>>;
1844
+ /**
1845
+ * Update or remove the footer for an existing video session by cloning it into a new session and
1846
+ * re-running frame/video generation.
1847
+ */
1848
+ updateVideoFooterImage(input: UpdateVideoFooterImageInput, options?: {
1849
+ webhookUrl?: string;
1850
+ } & SamsarRequestOptions): Promise<SamsarResult<UpdateVideoFooterImageResponse>>;
1851
+ /**
1852
+ * Update or remove the footer for an external-user video request through
1853
+ * /external_users/update_footer_image.
1854
+ */
1855
+ updateExternalVideoFooterImage(externalUser: ExternalUserIdentity, input: UpdateVideoFooterImageInput, options?: {
1856
+ webhookUrl?: string;
1857
+ } & SamsarRequestOptions): Promise<SamsarResult<ExternalRequestResponse>>;
1779
1858
  /**
1780
1859
  * Add or replace the outro image for an existing video session by cloning it into a new session and
1781
1860
  * re-running frame/video generation.
@@ -1791,11 +1870,13 @@ export declare class SamsarClient {
1791
1870
  } & SamsarRequestOptions): Promise<SamsarResult<ExternalRequestResponse>>;
1792
1871
  /**
1793
1872
  * Fetch the latest available render URL for a given video session id.
1873
+ * Completed responses include result_url, has_subtitles, and result_language.
1794
1874
  * Maps to GET /video/fetch_latest_version?session_id={sessionId}.
1795
1875
  */
1796
1876
  fetchLatestVideoVersion(sessionId: string, options?: SamsarRequestOptions): Promise<SamsarResult<FetchLatestVideoVersionResponse>>;
1797
1877
  /**
1798
1878
  * List completed video sessions for the authenticated API key.
1879
+ * Each item includes result_url plus session metadata such as has_subtitles and result_language.
1799
1880
  * Maps to GET /video/list_completed_video_sessions.
1800
1881
  */
1801
1882
  listCompletedVideoSessions(options?: SamsarRequestOptions & {
@@ -2001,6 +2082,7 @@ export declare class SamsarClient {
2001
2082
  * Retrieve the status of an asynchronous request by request_id.
2002
2083
  * Defaults to GET /status?request_id={requestId}, but the path can be overridden.
2003
2084
  * Normalizes the response to expose status/result_url for both image and video flows.
2085
+ * Completed video responses can also include has_subtitles and result_language.
2004
2086
  */
2005
2087
  getStatus(requestId: string, options?: SamsarRequestOptions & {
2006
2088
  path?: string;
package/dist/index.js CHANGED
@@ -216,6 +216,61 @@ function normalizeUpdateVideoOutroImageInput(input, context = 'updateVideoOutroI
216
216
  ...(rawOutroFocusArea !== undefined ? { outro_focust_area: rawOutroFocusArea } : {}),
217
217
  };
218
218
  }
219
+ function normalizeUpdateVideoFooterImageInput(input, context = 'updateVideoFooterImage') {
220
+ const raw = input;
221
+ const videoSessionId = raw.videoSessionId ??
222
+ raw.video_session_id ??
223
+ raw.videoSessionID ??
224
+ raw.session_id ??
225
+ raw.sessionId ??
226
+ raw.sessionID ??
227
+ raw.request_id ??
228
+ raw.requestId ??
229
+ raw.source_request_id ??
230
+ raw.sourceRequestId ??
231
+ raw.external_request_id ??
232
+ raw.externalRequestId ??
233
+ raw.external_session_id ??
234
+ raw.externalSessionId;
235
+ const rawRemoveFooter = raw.remove_footer ??
236
+ raw.removeFooter;
237
+ const ctaText = raw.cta_text ??
238
+ raw.ctaText;
239
+ const ctaLogo = raw.cta_logo ??
240
+ raw.ctaLogo;
241
+ const ctaUrl = raw.cta_url ??
242
+ raw.ctaUrl;
243
+ const removeFooter = rawRemoveFooter === true;
244
+ if (!videoSessionId) {
245
+ throw new Error(`videoSessionId is required for ${context}`);
246
+ }
247
+ if (rawRemoveFooter !== undefined && typeof rawRemoveFooter !== 'boolean') {
248
+ throw new Error(`remove_footer must be a boolean for ${context}`);
249
+ }
250
+ if (ctaText !== undefined && typeof ctaText !== 'string') {
251
+ throw new Error(`cta_text must be a string for ${context}`);
252
+ }
253
+ if (ctaLogo !== undefined && typeof ctaLogo !== 'string') {
254
+ throw new Error(`cta_logo must be a string for ${context}`);
255
+ }
256
+ if (ctaUrl !== undefined && typeof ctaUrl !== 'string') {
257
+ throw new Error(`cta_url must be a string for ${context}`);
258
+ }
259
+ const normalizedCtaText = typeof ctaText === 'string' ? ctaText.trim() : '';
260
+ const normalizedCtaLogo = typeof ctaLogo === 'string' ? ctaLogo.trim() : '';
261
+ const normalizedCtaUrl = typeof ctaUrl === 'string' ? ctaUrl.trim() : '';
262
+ if (!removeFooter && !normalizedCtaText && !normalizedCtaLogo && !normalizedCtaUrl) {
263
+ throw new Error(`At least one of cta_text, cta_logo, or cta_url is required for ${context} unless remove_footer is true`);
264
+ }
265
+ return {
266
+ ...input,
267
+ videoSessionId: String(videoSessionId),
268
+ remove_footer: removeFooter,
269
+ ...(normalizedCtaText ? { cta_text: normalizedCtaText } : {}),
270
+ ...(normalizedCtaLogo ? { cta_logo: normalizedCtaLogo } : {}),
271
+ ...(normalizedCtaUrl ? { cta_url: normalizedCtaUrl } : {}),
272
+ };
273
+ }
219
274
  function normalizeSessionPublicationInput(input, context) {
220
275
  if (typeof input !== 'string' && (!input || typeof input !== 'object' || Array.isArray(input))) {
221
276
  throw new Error(`input must be a session id or object for ${context}`);
@@ -452,6 +507,13 @@ export class SamsarClient {
452
507
  webhookUrl: options?.webhookUrl,
453
508
  }, options);
454
509
  }
510
+ async updateV2VideoFooterImage(input, options) {
511
+ const normalizedInput = normalizeUpdateVideoFooterImageInput(input, 'updateV2VideoFooterImage');
512
+ return this.postV2('update_footer_image', {
513
+ input: normalizedInput,
514
+ webhookUrl: options?.webhookUrl,
515
+ }, options);
516
+ }
455
517
  async addV2VideoOutroImage(input, options) {
456
518
  return this.postV2('add_outro_image', {
457
519
  input,
@@ -791,6 +853,51 @@ export class SamsarClient {
791
853
  }
792
854
  return response;
793
855
  }
856
+ /**
857
+ * Add subtitle/transcript text overlays by cloning an existing session and re-running
858
+ * transcription + frame + video generation on the new session.
859
+ */
860
+ async addSubtitles(input, options) {
861
+ const raw = input;
862
+ const videoSessionId = raw.videoSessionId ??
863
+ raw.video_session_id ??
864
+ raw.videoSessionID ??
865
+ raw.session_id ??
866
+ raw.sessionId ??
867
+ raw.sessionID ??
868
+ raw.request_id ??
869
+ raw.requestId;
870
+ if (!videoSessionId) {
871
+ throw new Error('videoSessionId is required for addSubtitles');
872
+ }
873
+ const body = {
874
+ input: {
875
+ ...input,
876
+ videoSessionId: String(videoSessionId),
877
+ },
878
+ webhookUrl: options?.webhookUrl,
879
+ };
880
+ const response = await this.post('video/add_subtitles', body, options);
881
+ const data = response.data;
882
+ if (data && typeof data === 'object') {
883
+ const sessionId = typeof data.sessionID === 'string'
884
+ ? data.sessionID
885
+ : typeof data.session_id === 'string'
886
+ ? data.session_id
887
+ : typeof data.request_id === 'string'
888
+ ? data.request_id
889
+ : undefined;
890
+ const normalizedSessionId = sessionId ? String(sessionId) : undefined;
891
+ const normalizedData = {
892
+ ...data,
893
+ sessionID: data.sessionID ?? normalizedSessionId ?? '',
894
+ session_id: data.session_id ?? normalizedSessionId,
895
+ request_id: data.request_id ?? normalizedSessionId,
896
+ };
897
+ return { ...response, data: normalizedData };
898
+ }
899
+ return response;
900
+ }
794
901
  /**
795
902
  * Cancel an in-progress render for an existing video session.
796
903
  */
@@ -878,6 +985,50 @@ export class SamsarClient {
878
985
  };
879
986
  return this.post('external_users/update_outro_image', body, options);
880
987
  }
988
+ /**
989
+ * Update or remove the footer for an existing video session by cloning it into a new session and
990
+ * re-running frame/video generation.
991
+ */
992
+ async updateVideoFooterImage(input, options) {
993
+ const normalizedInput = normalizeUpdateVideoFooterImageInput(input, 'updateVideoFooterImage');
994
+ const body = {
995
+ input: normalizedInput,
996
+ webhookUrl: options?.webhookUrl,
997
+ };
998
+ const response = await this.post('video/update_footer_image', body, options);
999
+ const data = response.data;
1000
+ if (data && typeof data === 'object') {
1001
+ const sessionId = typeof data.sessionID === 'string'
1002
+ ? data.sessionID
1003
+ : typeof data.session_id === 'string'
1004
+ ? data.session_id
1005
+ : typeof data.request_id === 'string'
1006
+ ? data.request_id
1007
+ : undefined;
1008
+ const normalizedSessionId = sessionId ? String(sessionId) : undefined;
1009
+ const normalizedData = {
1010
+ ...data,
1011
+ sessionID: data.sessionID ?? normalizedSessionId ?? '',
1012
+ session_id: data.session_id ?? normalizedSessionId,
1013
+ request_id: data.request_id ?? normalizedSessionId,
1014
+ };
1015
+ return { ...response, data: normalizedData };
1016
+ }
1017
+ return response;
1018
+ }
1019
+ /**
1020
+ * Update or remove the footer for an external-user video request through
1021
+ * /external_users/update_footer_image.
1022
+ */
1023
+ async updateExternalVideoFooterImage(externalUser, input, options) {
1024
+ const normalizedInput = normalizeUpdateVideoFooterImageInput(input, 'updateExternalVideoFooterImage');
1025
+ const body = {
1026
+ external_user: normalizeExternalUserIdentity(externalUser),
1027
+ input: normalizedInput,
1028
+ webhookUrl: options?.webhookUrl,
1029
+ };
1030
+ return this.post('external_users/update_footer_image', body, options);
1031
+ }
881
1032
  /**
882
1033
  * Add or replace the outro image for an existing video session by cloning it into a new session and
883
1034
  * re-running frame/video generation.
@@ -1033,6 +1184,7 @@ export class SamsarClient {
1033
1184
  }
1034
1185
  /**
1035
1186
  * Fetch the latest available render URL for a given video session id.
1187
+ * Completed responses include result_url, has_subtitles, and result_language.
1036
1188
  * Maps to GET /video/fetch_latest_version?session_id={sessionId}.
1037
1189
  */
1038
1190
  async fetchLatestVideoVersion(sessionId, options) {
@@ -1050,6 +1202,7 @@ export class SamsarClient {
1050
1202
  }
1051
1203
  /**
1052
1204
  * List completed video sessions for the authenticated API key.
1205
+ * Each item includes result_url plus session metadata such as has_subtitles and result_language.
1053
1206
  * Maps to GET /video/list_completed_video_sessions.
1054
1207
  */
1055
1208
  async listCompletedVideoSessions(options) {
@@ -1581,6 +1734,7 @@ export class SamsarClient {
1581
1734
  * Retrieve the status of an asynchronous request by request_id.
1582
1735
  * Defaults to GET /status?request_id={requestId}, but the path can be overridden.
1583
1736
  * Normalizes the response to expose status/result_url for both image and video flows.
1737
+ * Completed video responses can also include has_subtitles and result_language.
1584
1738
  */
1585
1739
  async getStatus(requestId, options) {
1586
1740
  if (!requestId) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "samsar-js",
3
- "version": "0.48.22",
3
+ "version": "0.48.24",
4
4
  "description": "TypeScript client for the Samsar Processor API routes.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",