samsar-js 0.48.29 → 0.48.31
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 +69 -15
- package/dist/express-video-pricing.d.ts +6 -0
- package/dist/express-video-pricing.js +5 -0
- package/dist/index.d.ts +67 -7
- package/dist/index.js +251 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -107,6 +107,20 @@ await samsar.createVideoFromImageList({
|
|
|
107
107
|
],
|
|
108
108
|
});
|
|
109
109
|
|
|
110
|
+
// Create the same generated QR outro and per-scene footer CTAs from one CTA link
|
|
111
|
+
await samsar.createVideoFromImageList({
|
|
112
|
+
image_urls: [
|
|
113
|
+
{ image_url: 'https://example.com/a.jpg', title: 'Blue Lagoon Tour' },
|
|
114
|
+
{ image_url: 'https://example.com/b.jpg', title: 'Sunset Dinner' },
|
|
115
|
+
],
|
|
116
|
+
prompt: 'Travel offer reel with concise scene CTAs',
|
|
117
|
+
metadata: { brand: 'Guidestination', offer: 'Bookable Bangkok experiences' },
|
|
118
|
+
video_model: 'RUNWAYML',
|
|
119
|
+
aspect_ratio: '9:16',
|
|
120
|
+
express_cta_generation: true,
|
|
121
|
+
cta_url: 'https://example.com/book',
|
|
122
|
+
});
|
|
123
|
+
|
|
110
124
|
// Update an existing outro with a new provided outro image URL
|
|
111
125
|
await samsar.updateVideoOutroImage(
|
|
112
126
|
{
|
|
@@ -332,6 +346,24 @@ const images = await samsar.extendImageList({
|
|
|
332
346
|
aspect_ratio: '16:9',
|
|
333
347
|
});
|
|
334
348
|
|
|
349
|
+
// Assign a short SEO-friendly image title
|
|
350
|
+
const imageTitle = await samsar.assignImageTitle({
|
|
351
|
+
image_url: 'https://example.com/product-photo.png',
|
|
352
|
+
metadata: {
|
|
353
|
+
product: 'linen travel shirt',
|
|
354
|
+
collection: 'spring essentials',
|
|
355
|
+
},
|
|
356
|
+
});
|
|
357
|
+
console.log(imageTitle.data.content); // "Linen Travel Shirt"
|
|
358
|
+
|
|
359
|
+
// Binary uploads are also supported in browser/Node 18+ runtimes
|
|
360
|
+
const fileTitle = await samsar.assignImageTitle({
|
|
361
|
+
image: fileOrBlob,
|
|
362
|
+
fileName: 'product-photo.png',
|
|
363
|
+
mimeType: 'image/png',
|
|
364
|
+
metadata: { product: 'linen travel shirt' },
|
|
365
|
+
});
|
|
366
|
+
|
|
335
367
|
// Create a reusable receipt template from one sample receipt image (free endpoint)
|
|
336
368
|
const receiptTemplate = await samsar.createReceiptTemplate({
|
|
337
369
|
image_url: 'https://example.com/receipt-template.png',
|
|
@@ -547,11 +579,12 @@ console.log(externalLibrary.data.requests.map((request) => request.request_id));
|
|
|
547
579
|
|
|
548
580
|
Video model support notes:
|
|
549
581
|
- `createVideoFromText` image model keys include: `GPTIMAGE2`, `IMAGEN4`, `SEEDREAM`, `NANOBANANA2`, `CUSTOM_TEXT_TO_IMAGE`.
|
|
550
|
-
- `createVideoFromText` supports these video models: `RUNWAYML`, `VEO3.1I2V`, `VEO3.1I2VFAST`, `SEEDANCEI2V` (Seedance 1.5), `KLINGIMGTOVID3PRO`, and `
|
|
582
|
+
- `createVideoFromText` supports these video models: `RUNWAYML`, `VEO3.1I2V`, `VEO3.1I2VFAST`, `SEEDANCEI2V` (Seedance 1.5), `KLINGIMGTOVID3PRO`, and `HAPPYHORSEI2V`.
|
|
551
583
|
- `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.
|
|
552
|
-
- `createVideoFromImageList` supports `RUNWAYML`, `VEO3.1I2V`, `VEO3.1I2VFAST`, `SEEDANCEI2V`, `KLINGIMGTOVID3PRO`, and `
|
|
584
|
+
- `createVideoFromImageList` supports `RUNWAYML`, `VEO3.1I2V`, `VEO3.1I2VFAST`, `SEEDANCEI2V`, `KLINGIMGTOVID3PRO`, and `HAPPYHORSEI2V` via `video_model`; if omitted, it defaults to `RUNWAYML`. Use `aspect_ratio: '16:9'` or `'9:16'`; omitted or invalid values fall back to `16:9`.
|
|
553
585
|
- `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.
|
|
554
586
|
- `createVideoFromImageList` can render per-scene footer QR cards by setting `add_footer_animation: true` and providing one `footer_metadata` item per image scene.
|
|
587
|
+
- `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.
|
|
555
588
|
- `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.
|
|
556
589
|
- `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.
|
|
557
590
|
- `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.
|
|
@@ -559,13 +592,13 @@ Video model support notes:
|
|
|
559
592
|
- 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.
|
|
560
593
|
- Completed video status, latest-version, and completed-session list responses expose `has_subtitles` and `result_language` when the session metadata is available.
|
|
561
594
|
- `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.
|
|
562
|
-
- Text-to-video and image-list video pricing use the same per-rendered-second rates for standard express models: `VEO3.1I2V` is 60 credits/sec, `VEO3.1I2VFAST` is 36 credits/sec, `SEEDANCEI2V` is 30 credits/sec, `KLINGIMGTOVID3PRO` is 36 credits/sec, and `RUNWAYML` is 30 credits/sec. Image-list narrator avatar generation adds 4 credits/sec when `add_narrator_avatar` is true.
|
|
563
|
-
- Standard express video models expose a per-second pricing distribution through `EXPRESS_VIDEO_PRICING_DISTRIBUTION_PER_SECOND_BY_MODEL`: pipeline 4, inference 4, image gen/edit 2, speech 2, music 2, effects and lipsync 2,
|
|
595
|
+
- Text-to-video and image-list video pricing use the same per-rendered-second rates for standard express models: `VEO3.1I2V` is 60 credits/sec, `VEO3.1I2VFAST` is 36 credits/sec, `SEEDANCEI2V` is 30 credits/sec, `KLINGIMGTOVID3PRO` is 36 credits/sec, `HAPPYHORSEI2V` is 36 credits/sec, and `RUNWAYML` is 30 credits/sec. Image-list narrator avatar generation adds 4 credits/sec when `add_narrator_avatar` is true; express CTA generation adds 1 credit/sec when `express_cta_generation` is true.
|
|
596
|
+
- Standard express video models expose a per-second pricing distribution through `EXPRESS_VIDEO_PRICING_DISTRIBUTION_PER_SECOND_BY_MODEL`: pipeline 4, inference 4, image gen/edit 2, speech 2, music 2, effects and lipsync 2, video as the model-specific remainder, and `optionalAddons.express_cta_generation` at 1 credit/sec.
|
|
564
597
|
|
|
565
598
|
Upcoming `/v2` omni route adapters:
|
|
566
599
|
- `/v2` is additive; `/v1` is not deprecated.
|
|
567
|
-
- `createV2VideoFromText`, `createV2VideoFromImageList`, `translateV2Video`, `cloneV2Video`, `regenerateV2VideoAvatar`, `updateV2VideoOutroImage`, `updateV2VideoFooterImage`, `addV2VideoOutroImage`, `getV2Status`, `getV2StatusDetailed`, `getV2Credits`, `listV2Requests`, and `createV2Session` call the new omni route surface.
|
|
568
|
-
- Step-controlled video helpers include `createV2StepVideoFromText`, `createV2StepTextToVideo`, `createV2StepVideoFromImage`, `createV2StepImageToVideo`, `getV2StepVideoStatus`, `getV2StepVideoStatusDetailed`, and `processNextV2StepVideo`.
|
|
600
|
+
- `createV2VideoFromText`, `createV2VideoFromImageList`, `assignV2ImageTitle`, `translateV2Video`, `cloneV2Video`, `regenerateV2VideoAvatar`, `updateV2VideoOutroImage`, `updateV2VideoFooterImage`, `addV2VideoOutroImage`, `getV2Status`, `getV2StatusDetailed`, `getV2Credits`, `listV2Requests`, and `createV2Session` call the new omni route surface.
|
|
601
|
+
- Step-controlled video helpers include `createV2StepVideoFromText`, `createV2StepTextToVideo`, `createV2StepVideoFromImage`, `createV2StepImageToVideo`, `getV2StepVideoStatus`, `getV2StepVideoStatusDetailed`, and `processNextV2StepVideo`. They default to 1-step express rendering by sending `auto_render_full_video: true` and `manual_step_stages: []`; pass `{ stepMode: 'two_step' }` or `manual_step_stages: ['ai_video_generation']` to require an explicit second-step approval before image-to-video generation.
|
|
569
602
|
- Programmatic user helpers include `createV2ExternalUser`, `createV2UserRechargeCredits`, `refreshV2UserToken`, `createV2UserAppKey`, `refreshV2UserAppKey`, `getV2UserCredits`, `getV2UserUsageLogs`, and `getV2UserPaymentStatus`.
|
|
570
603
|
- 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. V2 external users can be referenced by `unique_key`; if `unique_key` is omitted during creation, the server uses `external_user_id` as the key.
|
|
571
604
|
- Detailed status adapters return the normal status payload plus a normalized `session` preview shape with `layers`, `audioLayers`, `globalAudioLayers`, and `globalVideos`. Layer timing uses `startTime` and `endTime` so clients can preview generated images, scene clips, and speech before final render completion.
|
|
@@ -574,9 +607,8 @@ Upcoming `/v2` omni route adapters:
|
|
|
574
607
|
const v2Video = await platform.createV2VideoFromImageList({
|
|
575
608
|
image_urls: ['https://cdn.example.com/a.png', 'https://cdn.example.com/b.png'],
|
|
576
609
|
video_model: 'RUNWAYML',
|
|
577
|
-
|
|
610
|
+
express_cta_generation: true,
|
|
578
611
|
cta_url: 'https://example.com/book',
|
|
579
|
-
cta_text_top: 'Scan to book',
|
|
580
612
|
});
|
|
581
613
|
|
|
582
614
|
const v2ExternalVideo = await platform.createV2VideoFromImageList(
|
|
@@ -587,6 +619,12 @@ const v2ExternalVideo = await platform.createV2VideoFromImageList(
|
|
|
587
619
|
{ externalUser },
|
|
588
620
|
);
|
|
589
621
|
|
|
622
|
+
const v2ImageTitle = await platform.assignV2ImageTitle({
|
|
623
|
+
image_url: 'https://cdn.example.com/product-frame.png',
|
|
624
|
+
metadata: { product: 'travel shirt', channel: 'marketplace' },
|
|
625
|
+
});
|
|
626
|
+
console.log(v2ImageTitle.data.content);
|
|
627
|
+
|
|
590
628
|
const v2Translated = await platform.translateV2Video({
|
|
591
629
|
videoSessionId: v2Video.data.request_id!,
|
|
592
630
|
language: 'es',
|
|
@@ -610,7 +648,7 @@ const v2Detailed = await platform.getV2StatusDetailed(v2Video.data.request_id!);
|
|
|
610
648
|
console.log(v2Detailed.data.session?.previewStage, v2Detailed.data.session?.layers?.[0]?.preview?.url);
|
|
611
649
|
```
|
|
612
650
|
|
|
613
|
-
Step
|
|
651
|
+
Step video generation defaults to a 1-step express render, so it does not pause before image-to-video generation:
|
|
614
652
|
|
|
615
653
|
```ts
|
|
616
654
|
const stepVideo = await platform.createV2StepVideoFromText({
|
|
@@ -622,12 +660,28 @@ const stepVideo = await platform.createV2StepVideoFromText({
|
|
|
622
660
|
enable_subtitles: true,
|
|
623
661
|
});
|
|
624
662
|
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
663
|
+
const stepDetailed = await platform.getV2StepVideoStatusDetailed(stepVideo.data.request_id);
|
|
664
|
+
console.log(stepDetailed.data.session?.previewStage, stepDetailed.data.session?.layers?.[0]?.preview?.url);
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
Use 2-step mode only when you want to review generated image/audio assets before starting the image-to-video stage:
|
|
668
|
+
|
|
669
|
+
```ts
|
|
670
|
+
const twoStepVideo = await platform.createV2StepVideoFromText(
|
|
671
|
+
{
|
|
672
|
+
prompt: 'A 20 second launch teaser for a new travel app',
|
|
673
|
+
image_model: 'GPTIMAGE2',
|
|
674
|
+
video_model: 'RUNWAYML',
|
|
675
|
+
duration: 20,
|
|
676
|
+
aspect_ratio: '16:9',
|
|
677
|
+
enable_subtitles: true,
|
|
678
|
+
},
|
|
679
|
+
{ stepMode: 'two_step' },
|
|
680
|
+
);
|
|
681
|
+
|
|
682
|
+
let stepStatus = await platform.getV2StepVideoStatus(twoStepVideo.data.request_id);
|
|
683
|
+
if (stepStatus.data.waiting_for_process_next || stepStatus.data.can_process_next) {
|
|
684
|
+
stepStatus = await platform.processNextV2StepVideo(twoStepVideo.data.request_id);
|
|
631
685
|
}
|
|
632
686
|
|
|
633
687
|
const stepImageVideo = await platform.createV2StepVideoFromImage({
|
|
@@ -9,7 +9,12 @@ export declare const EXPRESS_VIDEO_FIXED_PRICING_COMPONENTS_PER_SECOND: {
|
|
|
9
9
|
export type ExpressVideoPricingDistribution = typeof EXPRESS_VIDEO_FIXED_PRICING_COMPONENTS_PER_SECOND & {
|
|
10
10
|
video: number;
|
|
11
11
|
total: number;
|
|
12
|
+
optionalAddons: ExpressVideoOptionalAddons;
|
|
12
13
|
};
|
|
14
|
+
export declare const EXPRESS_VIDEO_OPTIONAL_ADDON_CREDITS_PER_SECOND: {
|
|
15
|
+
readonly express_cta_generation: 1;
|
|
16
|
+
};
|
|
17
|
+
export type ExpressVideoOptionalAddons = typeof EXPRESS_VIDEO_OPTIONAL_ADDON_CREDITS_PER_SECOND;
|
|
13
18
|
export declare const EXPRESS_VIDEO_FIXED_COMPONENTS_TOTAL_PER_SECOND: number;
|
|
14
19
|
export declare const EXPRESS_VIDEO_CREDITS_PER_SECOND_BY_MODEL: {
|
|
15
20
|
readonly 'VEO3.1I2V': 60;
|
|
@@ -17,6 +22,7 @@ export declare const EXPRESS_VIDEO_CREDITS_PER_SECOND_BY_MODEL: {
|
|
|
17
22
|
readonly SEEDANCEI2V: 30;
|
|
18
23
|
readonly KLINGIMGTOVID3PRO: 36;
|
|
19
24
|
readonly KLINGIMGTOVIDTURBO: 36;
|
|
25
|
+
readonly HAPPYHORSEI2V: 36;
|
|
20
26
|
readonly RUNWAYML: 30;
|
|
21
27
|
};
|
|
22
28
|
export declare const EXPRESS_VIDEO_PRICING_DISTRIBUTION_PER_SECOND_BY_MODEL: Record<string, ExpressVideoPricingDistribution>;
|
|
@@ -6,6 +6,9 @@ export const EXPRESS_VIDEO_FIXED_PRICING_COMPONENTS_PER_SECOND = {
|
|
|
6
6
|
music: 2,
|
|
7
7
|
effects_and_lipsync: 2,
|
|
8
8
|
};
|
|
9
|
+
export const EXPRESS_VIDEO_OPTIONAL_ADDON_CREDITS_PER_SECOND = {
|
|
10
|
+
express_cta_generation: 1,
|
|
11
|
+
};
|
|
9
12
|
export const EXPRESS_VIDEO_FIXED_COMPONENTS_TOTAL_PER_SECOND = Object.values(EXPRESS_VIDEO_FIXED_PRICING_COMPONENTS_PER_SECOND).reduce((total, value) => total + value, 0);
|
|
10
13
|
export const EXPRESS_VIDEO_CREDITS_PER_SECOND_BY_MODEL = {
|
|
11
14
|
'VEO3.1I2V': 60,
|
|
@@ -13,6 +16,7 @@ export const EXPRESS_VIDEO_CREDITS_PER_SECOND_BY_MODEL = {
|
|
|
13
16
|
SEEDANCEI2V: 30,
|
|
14
17
|
KLINGIMGTOVID3PRO: 36,
|
|
15
18
|
KLINGIMGTOVIDTURBO: 36,
|
|
19
|
+
HAPPYHORSEI2V: 36,
|
|
16
20
|
RUNWAYML: 30,
|
|
17
21
|
};
|
|
18
22
|
export const EXPRESS_VIDEO_PRICING_DISTRIBUTION_PER_SECOND_BY_MODEL = Object.fromEntries(Object.entries(EXPRESS_VIDEO_CREDITS_PER_SECOND_BY_MODEL)
|
|
@@ -22,6 +26,7 @@ export const EXPRESS_VIDEO_PRICING_DISTRIBUTION_PER_SECOND_BY_MODEL = Object.fro
|
|
|
22
26
|
...EXPRESS_VIDEO_FIXED_PRICING_COMPONENTS_PER_SECOND,
|
|
23
27
|
video: total - EXPRESS_VIDEO_FIXED_COMPONENTS_TOTAL_PER_SECOND,
|
|
24
28
|
total,
|
|
29
|
+
optionalAddons: EXPRESS_VIDEO_OPTIONAL_ADDON_CREDITS_PER_SECOND,
|
|
25
30
|
},
|
|
26
31
|
]));
|
|
27
32
|
export function getExpressVideoCreditsPerSecond(model) {
|
package/dist/index.d.ts
CHANGED
|
@@ -36,7 +36,14 @@ export interface FontOptions {
|
|
|
36
36
|
language?: string;
|
|
37
37
|
family?: string;
|
|
38
38
|
}
|
|
39
|
-
export
|
|
39
|
+
export type V2StepGenerationMode = 'one_step' | 'two_step';
|
|
40
|
+
export interface V2StepGenerationOptions {
|
|
41
|
+
auto_render_full_video?: boolean;
|
|
42
|
+
autoRenderFullVideo?: boolean;
|
|
43
|
+
manual_step_stages?: Array<V2StepVideoStage | string>;
|
|
44
|
+
manualStepStages?: Array<V2StepVideoStage | string>;
|
|
45
|
+
}
|
|
46
|
+
export interface CreateVideoFromTextInput extends V2StepGenerationOptions {
|
|
40
47
|
prompt: string;
|
|
41
48
|
image_model: string;
|
|
42
49
|
video_model: string;
|
|
@@ -111,7 +118,7 @@ export interface FooterMetadataItem {
|
|
|
111
118
|
footerLogoImagePath?: string;
|
|
112
119
|
}
|
|
113
120
|
export type ImageListToVideoAspectRatio = '16:9' | '9:16';
|
|
114
|
-
export type ImageListToVideoModel = 'RUNWAYML' | 'VEO3.1I2V' | 'VEO3.1I2VFAST' | 'SEEDANCEI2V' | 'KLINGIMGTOVID3PRO' | '
|
|
121
|
+
export type ImageListToVideoModel = 'RUNWAYML' | 'VEO3.1I2V' | 'VEO3.1I2VFAST' | 'SEEDANCEI2V' | 'KLINGIMGTOVID3PRO' | 'HAPPYHORSEI2V';
|
|
115
122
|
export interface ImageListToVideoItem {
|
|
116
123
|
image_url?: string;
|
|
117
124
|
imageUrl?: string;
|
|
@@ -139,7 +146,7 @@ export interface ImageListToVideoItem {
|
|
|
139
146
|
label?: string;
|
|
140
147
|
[key: string]: unknown;
|
|
141
148
|
}
|
|
142
|
-
export interface CreateVideoFromImageListInput {
|
|
149
|
+
export interface CreateVideoFromImageListInput extends V2StepGenerationOptions {
|
|
143
150
|
image_urls: Array<string | ImageListToVideoItem>;
|
|
144
151
|
metadata?: Record<string, unknown>;
|
|
145
152
|
prompt?: string;
|
|
@@ -172,6 +179,12 @@ export interface CreateVideoFromImageListInput {
|
|
|
172
179
|
outroFocusArea?: OutroFocusArea | null;
|
|
173
180
|
generate_outro_image?: boolean;
|
|
174
181
|
generateOutroImage?: boolean;
|
|
182
|
+
express_cta_generation?: boolean;
|
|
183
|
+
expressCtaGeneration?: boolean;
|
|
184
|
+
auto_generate_cta_text?: boolean;
|
|
185
|
+
autoGenerateCtaText?: boolean;
|
|
186
|
+
generate_cta_texts?: boolean;
|
|
187
|
+
generateCtaTexts?: boolean;
|
|
175
188
|
cta_url?: string;
|
|
176
189
|
ctaUrl?: string;
|
|
177
190
|
cta_text_top?: string;
|
|
@@ -962,6 +975,33 @@ export interface EnhanceImageResponse {
|
|
|
962
975
|
remainingCredits?: number;
|
|
963
976
|
[key: string]: unknown;
|
|
964
977
|
}
|
|
978
|
+
export type AssignImageTitleMimeType = 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif';
|
|
979
|
+
export type AssignImageTitleBinaryInput = Blob | ArrayBuffer | Uint8Array;
|
|
980
|
+
export interface AssignImageTitleRequest {
|
|
981
|
+
image?: string | AssignImageTitleBinaryInput;
|
|
982
|
+
file?: AssignImageTitleBinaryInput;
|
|
983
|
+
image_file?: AssignImageTitleBinaryInput;
|
|
984
|
+
imageFile?: AssignImageTitleBinaryInput;
|
|
985
|
+
image_url?: string;
|
|
986
|
+
imageUrl?: string;
|
|
987
|
+
url?: string;
|
|
988
|
+
image_data?: string;
|
|
989
|
+
imageData?: string;
|
|
990
|
+
image_data_url?: string;
|
|
991
|
+
imageDataUrl?: string;
|
|
992
|
+
metadata?: Record<string, unknown> | string;
|
|
993
|
+
metadata_json?: Record<string, unknown> | string;
|
|
994
|
+
metadataJson?: Record<string, unknown> | string;
|
|
995
|
+
fileName?: string;
|
|
996
|
+
filename?: string;
|
|
997
|
+
mimeType?: AssignImageTitleMimeType | string;
|
|
998
|
+
[key: string]: unknown;
|
|
999
|
+
}
|
|
1000
|
+
export interface AssignImageTitleResponse {
|
|
1001
|
+
content: string;
|
|
1002
|
+
title?: string;
|
|
1003
|
+
[key: string]: unknown;
|
|
1004
|
+
}
|
|
965
1005
|
export interface ExtendImageListRequest {
|
|
966
1006
|
image_urls: string[];
|
|
967
1007
|
num_images: number;
|
|
@@ -1411,6 +1451,9 @@ export interface V2RequestOptions extends SamsarRequestOptions {
|
|
|
1411
1451
|
externalUser?: V2ExternalUserIdentity | null;
|
|
1412
1452
|
webhookUrl?: string;
|
|
1413
1453
|
}
|
|
1454
|
+
export interface V2StepVideoRequestOptions extends V2RequestOptions, V2StepGenerationOptions {
|
|
1455
|
+
stepMode?: V2StepGenerationMode;
|
|
1456
|
+
}
|
|
1414
1457
|
export type V2StepVideoStage = 'prompt_generation' | 'image_generation' | 'speech_generation' | 'music_generation' | 'audio_generation' | 'ai_video_generation' | 'lip_sync_generation' | 'sound_effect_generation' | 'narrator_avatar_generation' | 'delete_reflow' | 'timeline_reflowed' | 'transcript_generation' | 'frame_generation' | 'video_generation';
|
|
1415
1458
|
export type V2StepVideoStatus = 'INIT' | 'PENDING' | 'COMPLETED' | 'FAILED' | string;
|
|
1416
1459
|
export interface VideoSessionPreviewAsset {
|
|
@@ -1541,7 +1584,11 @@ export interface V2StepVideoState {
|
|
|
1541
1584
|
current_step?: V2StepVideoStage | string | null;
|
|
1542
1585
|
current_step_label?: string | null;
|
|
1543
1586
|
next_step?: V2StepVideoStage | string | null;
|
|
1587
|
+
manual_step_stages?: Array<V2StepVideoStage | string>;
|
|
1588
|
+
auto_advance_step_stages?: Array<V2StepVideoStage | string>;
|
|
1544
1589
|
waiting_for_process_next?: boolean;
|
|
1590
|
+
requires_user_action?: boolean;
|
|
1591
|
+
can_process_next?: boolean;
|
|
1545
1592
|
updated_at?: string | null;
|
|
1546
1593
|
[key: string]: unknown;
|
|
1547
1594
|
}
|
|
@@ -1944,10 +1991,10 @@ export declare class SamsarClient {
|
|
|
1944
1991
|
}): Promise<SamsarResult<CreateLoginTokenResponse | ExternalCreateLoginTokenResponse>>;
|
|
1945
1992
|
createV2VideoFromText(input: CreateVideoFromTextInput, options?: V2RequestOptions): Promise<SamsarResult<CreateVideoResponse | ExternalRequestResponse>>;
|
|
1946
1993
|
createV2VideoFromImageList(input: CreateVideoFromImageListInput, options?: V2RequestOptions): Promise<SamsarResult<CreateVideoFromImageListResponse | ExternalRequestResponse>>;
|
|
1947
|
-
createV2StepVideoFromText(input: CreateVideoFromTextInput, options?:
|
|
1948
|
-
createV2StepTextToVideo(input: CreateVideoFromTextInput, options?:
|
|
1949
|
-
createV2StepVideoFromImage(input: CreateV2StepImageToVideoInput, options?:
|
|
1950
|
-
createV2StepImageToVideo(input: CreateV2StepImageToVideoInput, options?:
|
|
1994
|
+
createV2StepVideoFromText(input: CreateVideoFromTextInput, options?: V2StepVideoRequestOptions): Promise<SamsarResult<V2StepVideoCreateResponse>>;
|
|
1995
|
+
createV2StepTextToVideo(input: CreateVideoFromTextInput, options?: V2StepVideoRequestOptions): Promise<SamsarResult<V2StepVideoCreateResponse>>;
|
|
1996
|
+
createV2StepVideoFromImage(input: CreateV2StepImageToVideoInput, options?: V2StepVideoRequestOptions): Promise<SamsarResult<V2StepVideoCreateResponse>>;
|
|
1997
|
+
createV2StepImageToVideo(input: CreateV2StepImageToVideoInput, options?: V2StepVideoRequestOptions): Promise<SamsarResult<V2StepVideoCreateResponse>>;
|
|
1951
1998
|
getV2StepVideoStatus(requestId: string, options?: V2RequestOptions): Promise<SamsarResult<V2StepVideoStatusResponse>>;
|
|
1952
1999
|
getV2StepVideoStatusDetailed(requestId: string, options?: V2RequestOptions): Promise<SamsarResult<V2StepVideoDetailedStatusResponse>>;
|
|
1953
2000
|
getV2StepVideoDetailedStatus(requestId: string, options?: V2RequestOptions): Promise<SamsarResult<V2StepVideoDetailedStatusResponse>>;
|
|
@@ -1958,6 +2005,12 @@ export declare class SamsarClient {
|
|
|
1958
2005
|
uploadV2ImageData(imageData: string[], options?: V2RequestOptions): Promise<SamsarResult<{
|
|
1959
2006
|
image_urls: string[];
|
|
1960
2007
|
}>>;
|
|
2008
|
+
/**
|
|
2009
|
+
* Assign a short SEO-friendly title to an image using the /v2 image route.
|
|
2010
|
+
* Supports image_url/image_data JSON payloads or binary image/FormData uploads.
|
|
2011
|
+
*/
|
|
2012
|
+
assignV2ImageTitle(payload: AssignImageTitleRequest | FormData, options?: V2RequestOptions): Promise<SamsarResult<AssignImageTitleResponse>>;
|
|
2013
|
+
assignV2TitleToImage(payload: AssignImageTitleRequest | FormData, options?: V2RequestOptions): Promise<SamsarResult<AssignImageTitleResponse>>;
|
|
1961
2014
|
updateV2VideoOutroImage(input: UpdateVideoOutroImageInput, options?: V2RequestOptions): Promise<SamsarResult<UpdateVideoOutroImageResponse | ExternalRequestResponse>>;
|
|
1962
2015
|
updateV2VideoFooterImage(input: UpdateVideoFooterImageInput, options?: V2RequestOptions): Promise<SamsarResult<UpdateVideoFooterImageResponse | ExternalRequestResponse>>;
|
|
1963
2016
|
addV2VideoOutroImage(input: AddVideoOutroImageInput, options?: V2RequestOptions): Promise<SamsarResult<AddVideoOutroImageResponse | ExternalRequestResponse>>;
|
|
@@ -2243,6 +2296,12 @@ export declare class SamsarClient {
|
|
|
2243
2296
|
* Enhance an image by upscaling it with the specified resolution.
|
|
2244
2297
|
*/
|
|
2245
2298
|
enhanceImage(payload: EnhanceImageRequest, options?: SamsarRequestOptions): Promise<SamsarResult<EnhanceImageResponse>>;
|
|
2299
|
+
/**
|
|
2300
|
+
* Assign a short SEO-friendly title to an image.
|
|
2301
|
+
* Supports image_url/image_data JSON payloads or binary image/FormData uploads.
|
|
2302
|
+
*/
|
|
2303
|
+
assignImageTitle(payload: AssignImageTitleRequest | FormData, options?: SamsarRequestOptions): Promise<SamsarResult<AssignImageTitleResponse>>;
|
|
2304
|
+
assignTitleToImage(payload: AssignImageTitleRequest | FormData, options?: SamsarRequestOptions): Promise<SamsarResult<AssignImageTitleResponse>>;
|
|
2246
2305
|
/**
|
|
2247
2306
|
* Add or extend a saved image list for the authenticated API key.
|
|
2248
2307
|
*/
|
|
@@ -2359,6 +2418,7 @@ export declare class SamsarClient {
|
|
|
2359
2418
|
}): Promise<SamsarResult<GlobalStatusResponse>>;
|
|
2360
2419
|
private get;
|
|
2361
2420
|
private post;
|
|
2421
|
+
private postForm;
|
|
2362
2422
|
private request;
|
|
2363
2423
|
private buildHeaders;
|
|
2364
2424
|
private buildUrl;
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ const DEBUG = (() => {
|
|
|
4
4
|
const env = globalThis?.process?.env;
|
|
5
5
|
return env?.SAMSAR_SDK_DEBUG === '1';
|
|
6
6
|
})();
|
|
7
|
+
const DEFAULT_V2_STEP_MANUAL_STAGES = ['ai_video_generation'];
|
|
7
8
|
export class SamsarRequestError extends Error {
|
|
8
9
|
constructor(message, init) {
|
|
9
10
|
super(message);
|
|
@@ -40,6 +41,85 @@ function assertOptionalBoolean(value, fieldName, context = 'createVideoFromImage
|
|
|
40
41
|
throw new Error(`${fieldName} must be a boolean for ${context}`);
|
|
41
42
|
}
|
|
42
43
|
}
|
|
44
|
+
function getFirstDefinedInputValue(raw, aliases) {
|
|
45
|
+
for (const alias of aliases) {
|
|
46
|
+
if (Object.prototype.hasOwnProperty.call(raw, alias)) {
|
|
47
|
+
return raw[alias];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
function normalizeV2StepGenerationMode(value) {
|
|
53
|
+
if (typeof value !== 'string') {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
const normalized = value.trim().toLowerCase().replace(/\s+/g, '_');
|
|
57
|
+
if (['one_step', 'one-step', '1_step', '1-step', 'express', 'auto', 'automatic'].includes(normalized)) {
|
|
58
|
+
return 'one_step';
|
|
59
|
+
}
|
|
60
|
+
if (['two_step', 'two-step', '2_step', '2-step', 'manual'].includes(normalized)) {
|
|
61
|
+
return 'two_step';
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
function normalizeV2StepBoolean(value, fieldName) {
|
|
66
|
+
if (value === undefined) {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
if (typeof value === 'boolean') {
|
|
70
|
+
return value;
|
|
71
|
+
}
|
|
72
|
+
throw new Error(`${fieldName} must be a boolean for step video generation`);
|
|
73
|
+
}
|
|
74
|
+
function normalizeV2ManualStepStages(value) {
|
|
75
|
+
if (value === undefined) {
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
if (value === false || value === null) {
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
if (Array.isArray(value)) {
|
|
82
|
+
return value
|
|
83
|
+
.filter((stage) => typeof stage === 'string' && stage.trim().length > 0)
|
|
84
|
+
.map((stage) => stage.trim());
|
|
85
|
+
}
|
|
86
|
+
if (typeof value === 'string') {
|
|
87
|
+
return value
|
|
88
|
+
.split(',')
|
|
89
|
+
.map((stage) => stage.trim())
|
|
90
|
+
.filter(Boolean);
|
|
91
|
+
}
|
|
92
|
+
if (typeof value === 'object') {
|
|
93
|
+
return Object.entries(value)
|
|
94
|
+
.filter(([, enabled]) => enabled === true || enabled === 'true' || enabled === 1 || enabled === '1')
|
|
95
|
+
.map(([stage]) => stage.trim())
|
|
96
|
+
.filter(Boolean);
|
|
97
|
+
}
|
|
98
|
+
throw new Error('manual_step_stages must be an array, comma-separated string, stage map, null, or false');
|
|
99
|
+
}
|
|
100
|
+
function buildV2StepGenerationFields(input, options) {
|
|
101
|
+
const optionRecord = (options ?? {});
|
|
102
|
+
const stepMode = normalizeV2StepGenerationMode(optionRecord.stepMode);
|
|
103
|
+
const optionAutoRender = normalizeV2StepBoolean(getFirstDefinedInputValue(optionRecord, ['auto_render_full_video', 'autoRenderFullVideo']), 'auto_render_full_video');
|
|
104
|
+
const inputAutoRender = normalizeV2StepBoolean(getFirstDefinedInputValue(input, ['auto_render_full_video', 'autoRenderFullVideo']), 'auto_render_full_video');
|
|
105
|
+
const optionManualStages = normalizeV2ManualStepStages(getFirstDefinedInputValue(optionRecord, ['manual_step_stages', 'manualStepStages']));
|
|
106
|
+
const inputManualStages = normalizeV2ManualStepStages(getFirstDefinedInputValue(input, ['manual_step_stages', 'manualStepStages']));
|
|
107
|
+
let autoRenderFullVideo = stepMode ? stepMode === 'one_step' : optionAutoRender ?? inputAutoRender;
|
|
108
|
+
if (autoRenderFullVideo === undefined) {
|
|
109
|
+
autoRenderFullVideo = optionManualStages !== undefined || inputManualStages !== undefined
|
|
110
|
+
? false
|
|
111
|
+
: true;
|
|
112
|
+
}
|
|
113
|
+
const manualStepStages = autoRenderFullVideo
|
|
114
|
+
? []
|
|
115
|
+
: optionManualStages ?? inputManualStages ?? [...DEFAULT_V2_STEP_MANUAL_STAGES];
|
|
116
|
+
return {
|
|
117
|
+
auto_render_full_video: autoRenderFullVideo,
|
|
118
|
+
autoRenderFullVideo,
|
|
119
|
+
manual_step_stages: manualStepStages,
|
|
120
|
+
manualStepStages: manualStepStages,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
43
123
|
function normalizeCreateVideoFromTextInput(input) {
|
|
44
124
|
const raw = input;
|
|
45
125
|
const normalized = { ...input };
|
|
@@ -96,6 +176,17 @@ function normalizeCreateVideoFromImageListInput(input) {
|
|
|
96
176
|
['add_outro_focus_area', ['add_outro_focus_area', 'addOutroFocusArea']],
|
|
97
177
|
['outro_focust_area', ['outro_focust_area', 'outro_focus_area', 'outroFocustArea', 'outroFocusArea']],
|
|
98
178
|
['generate_outro_image', ['generate_outro_image', 'generateOutroImage']],
|
|
179
|
+
[
|
|
180
|
+
'express_cta_generation',
|
|
181
|
+
[
|
|
182
|
+
'express_cta_generation',
|
|
183
|
+
'expressCtaGeneration',
|
|
184
|
+
'auto_generate_cta_text',
|
|
185
|
+
'autoGenerateCtaText',
|
|
186
|
+
'generate_cta_texts',
|
|
187
|
+
'generateCtaTexts',
|
|
188
|
+
],
|
|
189
|
+
],
|
|
99
190
|
['cta_url', ['cta_url', 'ctaUrl']],
|
|
100
191
|
['cta_text_top', ['cta_text_top', 'ctaTextTop']],
|
|
101
192
|
['cta_text_bottom', ['cta_text_bottom', 'ctaTextBottom']],
|
|
@@ -117,12 +208,24 @@ function normalizeCreateVideoFromImageListInput(input) {
|
|
|
117
208
|
assertOptionalBoolean(normalized.add_outro_animation, 'add_outro_animation');
|
|
118
209
|
assertOptionalBoolean(normalized.add_outro_focus_area, 'add_outro_focus_area');
|
|
119
210
|
assertOptionalBoolean(normalized.generate_outro_image, 'generate_outro_image');
|
|
211
|
+
assertOptionalBoolean(normalized.express_cta_generation, 'express_cta_generation');
|
|
120
212
|
assertOptionalBoolean(normalized.add_footer_animation, 'add_footer_animation');
|
|
121
213
|
assertOptionalBoolean(normalized.limit_single_narrator, 'limit_single_narrator');
|
|
122
214
|
assertOptionalBoolean(normalized.add_narrator_avatar, 'add_narrator_avatar');
|
|
123
215
|
if (normalized.add_narrator_avatar === true) {
|
|
124
216
|
normalized.limit_single_narrator = true;
|
|
125
217
|
}
|
|
218
|
+
if (normalized.express_cta_generation === true) {
|
|
219
|
+
const ctaUrl = typeof normalized.cta_url === 'string' ? normalized.cta_url.trim() : '';
|
|
220
|
+
if (!ctaUrl) {
|
|
221
|
+
throw new Error('cta_url is required when express_cta_generation is true for createVideoFromImageList');
|
|
222
|
+
}
|
|
223
|
+
normalized.cta_url = ctaUrl;
|
|
224
|
+
normalized.generate_outro_image = true;
|
|
225
|
+
normalized.add_outro_animation = true;
|
|
226
|
+
normalized.add_outro_focus_area = true;
|
|
227
|
+
normalized.add_footer_animation = true;
|
|
228
|
+
}
|
|
126
229
|
if (normalized.generate_outro_image === true) {
|
|
127
230
|
const ctaUrl = typeof normalized.cta_url === 'string' ? normalized.cta_url.trim() : '';
|
|
128
231
|
if (!ctaUrl) {
|
|
@@ -606,8 +709,13 @@ export class SamsarClient {
|
|
|
606
709
|
}
|
|
607
710
|
async createV2StepVideoFromText(input, options) {
|
|
608
711
|
const normalizedInput = normalizeCreateVideoFromTextInput(input);
|
|
712
|
+
const stepGenerationFields = buildV2StepGenerationFields(normalizedInput, options);
|
|
609
713
|
return this.postV2('video/step/text_to_video', {
|
|
610
|
-
|
|
714
|
+
...stepGenerationFields,
|
|
715
|
+
input: {
|
|
716
|
+
...normalizedInput,
|
|
717
|
+
...stepGenerationFields,
|
|
718
|
+
},
|
|
611
719
|
webhookUrl: options?.webhookUrl,
|
|
612
720
|
}, options);
|
|
613
721
|
}
|
|
@@ -616,8 +724,13 @@ export class SamsarClient {
|
|
|
616
724
|
}
|
|
617
725
|
async createV2StepVideoFromImage(input, options) {
|
|
618
726
|
const normalizedInput = normalizeCreateV2StepImageToVideoInput(input);
|
|
727
|
+
const stepGenerationFields = buildV2StepGenerationFields(normalizedInput, options);
|
|
619
728
|
return this.postV2('video/step/image_to_video', {
|
|
620
|
-
|
|
729
|
+
...stepGenerationFields,
|
|
730
|
+
input: {
|
|
731
|
+
...normalizedInput,
|
|
732
|
+
...stepGenerationFields,
|
|
733
|
+
},
|
|
621
734
|
webhookUrl: options?.webhookUrl,
|
|
622
735
|
}, options);
|
|
623
736
|
}
|
|
@@ -679,6 +792,22 @@ export class SamsarClient {
|
|
|
679
792
|
},
|
|
680
793
|
}, options);
|
|
681
794
|
}
|
|
795
|
+
/**
|
|
796
|
+
* Assign a short SEO-friendly title to an image using the /v2 image route.
|
|
797
|
+
* Supports image_url/image_data JSON payloads or binary image/FormData uploads.
|
|
798
|
+
*/
|
|
799
|
+
async assignV2ImageTitle(payload, options) {
|
|
800
|
+
const body = buildAssignImageTitleBody(payload);
|
|
801
|
+
if (isFormDataBody(body)) {
|
|
802
|
+
return this.postForm(this.buildV2Url('image/assign_title'), body, options);
|
|
803
|
+
}
|
|
804
|
+
return this.postV2('image/assign_title', {
|
|
805
|
+
input: body,
|
|
806
|
+
}, options);
|
|
807
|
+
}
|
|
808
|
+
async assignV2TitleToImage(payload, options) {
|
|
809
|
+
return this.assignV2ImageTitle(payload, options);
|
|
810
|
+
}
|
|
682
811
|
async updateV2VideoOutroImage(input, options) {
|
|
683
812
|
const normalizedInput = normalizeUpdateVideoOutroImageInput(input, 'updateV2VideoOutroImage');
|
|
684
813
|
return this.postV2('update_outro_image', {
|
|
@@ -1645,6 +1774,20 @@ export class SamsarClient {
|
|
|
1645
1774
|
async enhanceImage(payload, options) {
|
|
1646
1775
|
return this.post('image/enhance', payload, options);
|
|
1647
1776
|
}
|
|
1777
|
+
/**
|
|
1778
|
+
* Assign a short SEO-friendly title to an image.
|
|
1779
|
+
* Supports image_url/image_data JSON payloads or binary image/FormData uploads.
|
|
1780
|
+
*/
|
|
1781
|
+
async assignImageTitle(payload, options) {
|
|
1782
|
+
const body = buildAssignImageTitleBody(payload);
|
|
1783
|
+
if (isFormDataBody(body)) {
|
|
1784
|
+
return this.postForm('image/assign_title', body, options);
|
|
1785
|
+
}
|
|
1786
|
+
return this.post('image/assign_title', body, options);
|
|
1787
|
+
}
|
|
1788
|
+
async assignTitleToImage(payload, options) {
|
|
1789
|
+
return this.assignImageTitle(payload, options);
|
|
1790
|
+
}
|
|
1648
1791
|
/**
|
|
1649
1792
|
* Add or extend a saved image list for the authenticated API key.
|
|
1650
1793
|
*/
|
|
@@ -2019,6 +2162,13 @@ export class SamsarClient {
|
|
|
2019
2162
|
body: JSON.stringify(body),
|
|
2020
2163
|
});
|
|
2021
2164
|
}
|
|
2165
|
+
async postForm(path, body, options) {
|
|
2166
|
+
return this.request(path, {
|
|
2167
|
+
...(options ?? {}),
|
|
2168
|
+
method: 'POST',
|
|
2169
|
+
body,
|
|
2170
|
+
});
|
|
2171
|
+
}
|
|
2022
2172
|
async request(path, options) {
|
|
2023
2173
|
const url = this.buildUrl(path, options.query);
|
|
2024
2174
|
const controller = new AbortController();
|
|
@@ -2108,7 +2258,7 @@ export class SamsarClient {
|
|
|
2108
2258
|
: this.apiKey
|
|
2109
2259
|
? `Bearer ${this.apiKey}`
|
|
2110
2260
|
: undefined,
|
|
2111
|
-
'Content-Type': options.body ? 'application/json' : undefined,
|
|
2261
|
+
'Content-Type': options.body && !isFormDataBody(options.body) ? 'application/json' : undefined,
|
|
2112
2262
|
'x-external-user-api-key': options.externalUserApiKey ?? this.externalUserApiKey,
|
|
2113
2263
|
'x-app-secret': resolvedAppKey ? resolvedAppSecret : undefined,
|
|
2114
2264
|
...this.defaultHeaders,
|
|
@@ -2175,6 +2325,104 @@ export class SamsarClient {
|
|
|
2175
2325
|
function trimTrailingSlash(url) {
|
|
2176
2326
|
return url.replace(/\/+$/, '');
|
|
2177
2327
|
}
|
|
2328
|
+
function buildAssignImageTitleBody(payload) {
|
|
2329
|
+
if (isFormDataBody(payload)) {
|
|
2330
|
+
return payload;
|
|
2331
|
+
}
|
|
2332
|
+
const input = (payload ?? {});
|
|
2333
|
+
const image = input.image ??
|
|
2334
|
+
input.file ??
|
|
2335
|
+
input.image_file ??
|
|
2336
|
+
input.imageFile;
|
|
2337
|
+
if (isBinaryAssignImageTitleInput(image)) {
|
|
2338
|
+
return buildAssignImageTitleFormData(input, image);
|
|
2339
|
+
}
|
|
2340
|
+
const stringImage = getTrimmedString(image);
|
|
2341
|
+
const imageData = stringImage?.startsWith('data:image/')
|
|
2342
|
+
? stringImage
|
|
2343
|
+
: getTrimmedString(input.image_data) ??
|
|
2344
|
+
getTrimmedString(input.imageData) ??
|
|
2345
|
+
getTrimmedString(input.image_data_url) ??
|
|
2346
|
+
getTrimmedString(input.imageDataUrl);
|
|
2347
|
+
const imageUrl = stringImage && !stringImage.startsWith('data:image/')
|
|
2348
|
+
? stringImage
|
|
2349
|
+
: getTrimmedString(input.image_url) ??
|
|
2350
|
+
getTrimmedString(input.imageUrl) ??
|
|
2351
|
+
getTrimmedString(input.url);
|
|
2352
|
+
const metadata = input.metadata ?? input.metadata_json ?? input.metadataJson;
|
|
2353
|
+
if (!imageData && !imageUrl) {
|
|
2354
|
+
throw new Error('image, image_data, or image_url is required for assignImageTitle');
|
|
2355
|
+
}
|
|
2356
|
+
return {
|
|
2357
|
+
...(imageData ? { image_data: imageData } : {}),
|
|
2358
|
+
...(imageUrl ? { image_url: imageUrl } : {}),
|
|
2359
|
+
...(metadata !== undefined ? { metadata } : {}),
|
|
2360
|
+
};
|
|
2361
|
+
}
|
|
2362
|
+
function buildAssignImageTitleFormData(input, image) {
|
|
2363
|
+
if (typeof FormData === 'undefined') {
|
|
2364
|
+
throw new Error('FormData is required for binary assignImageTitle uploads');
|
|
2365
|
+
}
|
|
2366
|
+
const metadata = input.metadata ?? input.metadata_json ?? input.metadataJson;
|
|
2367
|
+
const mimeType = getTrimmedString(input.mimeType) ?? getBlobType(image);
|
|
2368
|
+
if (!mimeType || !isSupportedAssignImageTitleMimeType(mimeType)) {
|
|
2369
|
+
throw new Error('mimeType must be image/png, image/jpeg, image/webp, or image/gif for binary assignImageTitle uploads');
|
|
2370
|
+
}
|
|
2371
|
+
const blob = toAssignImageTitleBlob(image, mimeType);
|
|
2372
|
+
const fileName = getTrimmedString(input.fileName) ??
|
|
2373
|
+
getTrimmedString(input.filename) ??
|
|
2374
|
+
`image.${mimeTypeToExtension(mimeType)}`;
|
|
2375
|
+
const formData = new FormData();
|
|
2376
|
+
formData.append('image', blob, fileName);
|
|
2377
|
+
if (metadata !== undefined) {
|
|
2378
|
+
formData.append('metadata', typeof metadata === 'string' ? metadata : JSON.stringify(metadata));
|
|
2379
|
+
}
|
|
2380
|
+
return formData;
|
|
2381
|
+
}
|
|
2382
|
+
function isBinaryAssignImageTitleInput(value) {
|
|
2383
|
+
return (isBlobValue(value) ||
|
|
2384
|
+
value instanceof ArrayBuffer ||
|
|
2385
|
+
value instanceof Uint8Array);
|
|
2386
|
+
}
|
|
2387
|
+
function toAssignImageTitleBlob(value, mimeType) {
|
|
2388
|
+
if (isBlobValue(value)) {
|
|
2389
|
+
if (value.type === mimeType) {
|
|
2390
|
+
return value;
|
|
2391
|
+
}
|
|
2392
|
+
return new Blob([value], { type: mimeType });
|
|
2393
|
+
}
|
|
2394
|
+
if (value instanceof Uint8Array) {
|
|
2395
|
+
const arrayBuffer = value.buffer.slice(value.byteOffset, value.byteOffset + value.byteLength);
|
|
2396
|
+
return new Blob([arrayBuffer], { type: mimeType });
|
|
2397
|
+
}
|
|
2398
|
+
return new Blob([value], { type: mimeType });
|
|
2399
|
+
}
|
|
2400
|
+
function getBlobType(value) {
|
|
2401
|
+
return isBlobValue(value) ? getTrimmedString(value.type) : undefined;
|
|
2402
|
+
}
|
|
2403
|
+
function isBlobValue(value) {
|
|
2404
|
+
return typeof Blob !== 'undefined' && value instanceof Blob;
|
|
2405
|
+
}
|
|
2406
|
+
function isFormDataBody(body) {
|
|
2407
|
+
return typeof FormData !== 'undefined' && body instanceof FormData;
|
|
2408
|
+
}
|
|
2409
|
+
function isSupportedAssignImageTitleMimeType(value) {
|
|
2410
|
+
const normalized = value.trim().toLowerCase();
|
|
2411
|
+
return ['image/png', 'image/jpeg', 'image/jpg', 'image/webp', 'image/gif'].includes(normalized);
|
|
2412
|
+
}
|
|
2413
|
+
function mimeTypeToExtension(value) {
|
|
2414
|
+
const normalized = value.trim().toLowerCase();
|
|
2415
|
+
if (normalized === 'image/jpeg' || normalized === 'image/jpg') {
|
|
2416
|
+
return 'jpg';
|
|
2417
|
+
}
|
|
2418
|
+
if (normalized === 'image/webp') {
|
|
2419
|
+
return 'webp';
|
|
2420
|
+
}
|
|
2421
|
+
if (normalized === 'image/gif') {
|
|
2422
|
+
return 'gif';
|
|
2423
|
+
}
|
|
2424
|
+
return 'png';
|
|
2425
|
+
}
|
|
2178
2426
|
function parseMaybeJson(value) {
|
|
2179
2427
|
try {
|
|
2180
2428
|
return JSON.parse(value);
|