@golpoai/sdk 0.1.10 → 1.0.1

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/dist/index.cjs CHANGED
@@ -57,33 +57,55 @@ var Golpo = class {
57
57
  async createPodcastJob(prompt, opts = {}) {
58
58
  const {
59
59
  uploads,
60
- concurrency = 8,
61
- addMusic = false,
62
60
  voiceInstructions,
63
- personality1,
64
- personality2,
65
- doResearch = false,
66
- ttsModel = "accurate",
67
61
  language,
68
- style = "conversational",
62
+ style = "solo-female-3",
69
63
  bgMusic,
70
- outputVolume = 1,
71
- no_voice_chunking = false
64
+ // Extended params
65
+ newScript,
66
+ timing = 1
67
+ // timing in minutes – default 1 if not provided
72
68
  } = opts;
69
+ const concurrency = 8;
73
70
  const fields = [
74
71
  ["prompt", prompt],
75
- ["add_music", String(addMusic)],
76
- ["do_research", String(doResearch)],
77
- ["tts_model", ttsModel],
78
- ["style", style],
79
- ["output_volume", String(outputVolume)]
72
+ ["style", style]
80
73
  ];
81
74
  if (voiceInstructions) fields.push(["voice_instructions", voiceInstructions]);
82
- if (personality1) fields.push(["personality_1", personality1]);
83
- if (personality2) fields.push(["personality_2", personality2]);
84
75
  if (language) fields.push(["language", language]);
85
76
  if (bgMusic) fields.push(["bg_music", bgMusic]);
86
- if (no_voice_chunking) fields.push(["no_voice_chunking", String(no_voice_chunking)]);
77
+ if (newScript != null) fields.push(["new_script", newScript]);
78
+ fields.push(["timing", String(timing)]);
79
+ fields.push(["video_type", "null"]);
80
+ const hasUploads = (() => {
81
+ if (!uploads) return false;
82
+ if (typeof uploads === "string") return uploads.trim().length > 0;
83
+ const iterator = uploads[Symbol.iterator]();
84
+ const first = iterator.next();
85
+ return !first.done;
86
+ })();
87
+ const request_payload = {
88
+ prompt,
89
+ video_voice: style,
90
+ video_duration: String(timing),
91
+ video_type: "null",
92
+ language,
93
+ voice_instructions: voiceInstructions,
94
+ new_script: newScript,
95
+ bg_music: bgMusic,
96
+ attached_documents: uploads,
97
+ source: "node-sdk"
98
+ };
99
+ try {
100
+ const result = await this.createVideoRecord({
101
+ topic: prompt,
102
+ requestPayload: request_payload
103
+ });
104
+ if (result && result.videoId) {
105
+ fields.push(["video_id", result.videoId]);
106
+ }
107
+ } catch (error) {
108
+ }
87
109
  const formData = await this.createFormData(fields, uploads, concurrency);
88
110
  const { data } = await this.http.post("/generate", formData, {
89
111
  timeout: 24e4,
@@ -120,50 +142,204 @@ var Golpo = class {
120
142
  const {
121
143
  uploads,
122
144
  pollIntervalMs = 2e3,
123
- concurrency = 8,
124
145
  voiceInstructions,
125
- personality1,
126
- doResearch = false,
127
- ttsModel = "accurate",
128
146
  language,
129
- style = "solo-female",
147
+ style = "solo-female-3",
130
148
  bgMusic = "engaging",
131
- bgVolume = 1.4,
132
- outputVolume = 1,
133
149
  videoType = "long",
134
- includeWatermark = true,
150
+ includeWatermark = false,
135
151
  logo,
136
- timing = "1"
152
+ timing = 1,
153
+ useColor = true,
154
+ // newly added backend parameters
155
+ newScript,
156
+ logoPlacement,
157
+ videoInstructions,
158
+ useLineart2Style,
159
+ audioClip,
160
+ isPublic,
161
+ useAsIs,
162
+ skipAnimation,
163
+ userImages,
164
+ userImagesDescriptions,
165
+ userVideos,
166
+ userVideosDescription,
167
+ userAudioInVideo,
168
+ use2Style,
169
+ // specificAnimation, // not yet supported by the Golpo 2.0 pipeline
170
+ imageStyle,
171
+ inputImages,
172
+ penStyle,
173
+ showPencilCursor,
174
+ pacing,
175
+ displayLanguage,
176
+ justReturnScript
137
177
  } = opts;
178
+ const concurrency = 8;
179
+ const request_payload = {
180
+ prompt,
181
+ video_voice: style,
182
+ video_duration: String(timing),
183
+ video_type: videoType,
184
+ language,
185
+ voice_instructions: voiceInstructions,
186
+ bg_music: bgMusic,
187
+ use_color: useColor,
188
+ direct_script: newScript,
189
+ script_mode: newScript ? true : false,
190
+ logo_placement: logoPlacement,
191
+ video_instructions: videoInstructions,
192
+ use_lineart_2_style: useLineart2Style,
193
+ audio_clip: audioClip,
194
+ is_public: isPublic,
195
+ use_as_is: useAsIs,
196
+ skip_animation: skipAnimation,
197
+ user_images: userImages,
198
+ user_images_descriptions: userImagesDescriptions,
199
+ user_videos: userVideos,
200
+ user_videos_description: userVideosDescription,
201
+ user_audio_in_video: userAudioInVideo,
202
+ use_2_0_style: use2Style,
203
+ // specific_animation: specificAnimation, // not yet supported by the pipeline
204
+ image_style: imageStyle,
205
+ input_images: inputImages,
206
+ pen_style: penStyle,
207
+ show_pencil_cursor: penStyle ? true : showPencilCursor ?? false,
208
+ pacing,
209
+ display_language: displayLanguage ?? null,
210
+ own_narration_video_mode: null,
211
+ own_narration_pip_position: null,
212
+ just_return_script: justReturnScript,
213
+ attached_documents: uploads,
214
+ source: "node-sdk"
215
+ };
138
216
  const fields = [
139
217
  ["prompt", prompt],
140
- ["do_research", String(doResearch)],
141
- ["tts_model", ttsModel],
142
- ["bg_volume", String(bgVolume)],
143
- ["output_volume", String(outputVolume)],
144
218
  ["style", style],
145
219
  ["video_type", videoType],
146
220
  ["include_watermark", String(includeWatermark)],
147
- ["timing", timing]
221
+ ["timing", String(timing)],
222
+ ["use_color", String(useColor)]
148
223
  ];
149
224
  if (language) fields.push(["language", language]);
150
225
  if (voiceInstructions) fields.push(["voice_instructions", voiceInstructions]);
151
- if (personality1) fields.push(["personality_1", personality1]);
152
226
  if (bgMusic) fields.push(["bg_music", bgMusic]);
227
+ try {
228
+ const result = await this.createVideoRecord({
229
+ topic: prompt,
230
+ requestPayload: request_payload
231
+ });
232
+ if (result && result.videoId) {
233
+ fields.push(["video_id", result.videoId]);
234
+ }
235
+ } catch (error) {
236
+ }
237
+ if (newScript) fields.push(["new_script", newScript]);
238
+ if (logoPlacement) fields.push(["logo_placement", logoPlacement]);
239
+ if (videoInstructions) fields.push(["video_instructions", videoInstructions]);
240
+ if (useLineart2Style) fields.push(["use_lineart_2_style", useLineart2Style]);
241
+ if (isPublic != null) fields.push(["is_public", String(isPublic)]);
242
+ if (useAsIs) {
243
+ const value = Array.isArray(useAsIs) ? JSON.stringify(useAsIs) : useAsIs;
244
+ fields.push(["use_as_is", value]);
245
+ }
246
+ if (skipAnimation) {
247
+ const value = Array.isArray(skipAnimation) ? JSON.stringify(skipAnimation) : skipAnimation;
248
+ fields.push(["skip_animation", value]);
249
+ }
250
+ if (userImagesDescriptions) {
251
+ const value = Array.isArray(userImagesDescriptions) ? JSON.stringify(userImagesDescriptions) : userImagesDescriptions;
252
+ fields.push(["user_images_descriptions", value]);
253
+ }
254
+ if (userVideosDescription) {
255
+ const value = Array.isArray(userVideosDescription) ? JSON.stringify(userVideosDescription) : userVideosDescription;
256
+ fields.push(["user_videos_description", value]);
257
+ }
258
+ if (userAudioInVideo) {
259
+ fields.push(["user_audio_in_video", userAudioInVideo]);
260
+ } else if (userVideos) {
261
+ fields.push(["user_audio_in_video", "[]"]);
262
+ }
263
+ if (use2Style != null) fields.push(["use_2_0_style", String(use2Style)]);
264
+ if (imageStyle) fields.push(["image_style", imageStyle]);
265
+ if (penStyle) fields.push(["pen_style", penStyle]);
266
+ const effectiveShowPencilCursor = penStyle ? true : showPencilCursor ?? false;
267
+ fields.push(["show_pencil_cursor", String(effectiveShowPencilCursor)]);
268
+ if (pacing) fields.push(["pacing", pacing]);
269
+ if (displayLanguage) fields.push(["display_language", displayLanguage]);
270
+ if (justReturnScript != null) fields.push(["just_return_script", String(justReturnScript)]);
153
271
  if (logo) {
154
272
  fields.push(["logo_path", logo]);
155
273
  }
156
- const formData = await this.createFormData(fields, uploads, concurrency, logo);
274
+ const formData = await this.createFormData(
275
+ fields,
276
+ uploads,
277
+ concurrency,
278
+ logo,
279
+ audioClip,
280
+ userImages,
281
+ userVideos,
282
+ inputImages
283
+ );
157
284
  const { data } = await this.http.post("/generate", formData, {
158
285
  timeout: 24e4,
159
286
  headers: formData.getHeaders()
160
287
  });
288
+ if (justReturnScript) {
289
+ const r2 = await this.pollUntilComplete(
290
+ data.job_id,
291
+ pollIntervalMs,
292
+ "/script-status"
293
+ );
294
+ return { url: "", script: r2.podcast_script, videoId: data.job_id };
295
+ }
161
296
  const r = await this.pollUntilComplete(
162
297
  data.job_id,
163
298
  pollIntervalMs
164
299
  );
165
300
  return { url: r.podcast_url, script: r.podcast_script, videoId: data.job_id };
166
301
  }
302
+ /**
303
+ * Private helper: Get user email from API key
304
+ * Backend resolves: api_key -> user_id -> user_credits.email
305
+ */
306
+ async getUserEmailFromApiKey() {
307
+ try {
308
+ const { data } = await this.http.get("/api/resolve-email-from-api-key");
309
+ return data.email || null;
310
+ } catch (error) {
311
+ return null;
312
+ }
313
+ }
314
+ async createVideoRecord(opts) {
315
+ const {
316
+ topic,
317
+ context = "",
318
+ scenes = 6,
319
+ pipeline = "color",
320
+ shareDocuments = false,
321
+ logoPlacement = null,
322
+ audioClipUrl = null,
323
+ requestPayload
324
+ } = opts;
325
+ const emailFromApiKey = await this.getUserEmailFromApiKey();
326
+ if (!emailFromApiKey) {
327
+ throw new Error("Email not found");
328
+ }
329
+ const body = {
330
+ topic,
331
+ context,
332
+ scenes,
333
+ pipeline,
334
+ user_email: emailFromApiKey,
335
+ share_documents: shareDocuments,
336
+ logo_placement: logoPlacement,
337
+ audio_clip_url: audioClipUrl,
338
+ request_payload: requestPayload ? JSON.stringify(requestPayload) : null
339
+ };
340
+ const { data } = await this.http.post("/api/create-video-record", body);
341
+ return { videoId: data.video_id };
342
+ }
167
343
  /**
168
344
  * Edit specific frames of a video and regenerate the video.
169
345
  * This method polls until completion and returns the final edited video URL.
@@ -387,14 +563,19 @@ var Golpo = class {
387
563
  /* ------------------------------------------------------------ *
388
564
  * INTERNAL HELPERS
389
565
  * ------------------------------------------------------------ */
390
- async pollUntilComplete(jobId, interval) {
566
+ async pollUntilComplete(jobId, interval, statusPath = "/status") {
391
567
  while (true) {
568
+ let data;
392
569
  try {
393
- const { data } = await this.http.get(`/status/${jobId}`);
394
- if (data.status === "completed") return data;
570
+ const res = await this.http.get(`${statusPath}/${jobId}`);
571
+ data = res.data;
395
572
  } catch (error) {
396
573
  console.warn(`Status check failed for job ${jobId}, retrying in ${interval}ms:`, error instanceof Error ? error.message : error);
397
574
  }
575
+ if (data?.status === "completed") return data;
576
+ if (data?.status === "failed") {
577
+ throw new Error(`Job ${jobId} failed: ${data.error || data.message || "unknown error"}`);
578
+ }
398
579
  await new Promise((r) => setTimeout(r, interval));
399
580
  }
400
581
  }
@@ -444,7 +625,50 @@ var Golpo = class {
444
625
  attempts++;
445
626
  }
446
627
  }
447
- async createFormData(fields, uploads, concurrency, logo) {
628
+ /**
629
+ * Upload a file using /upload-url endpoint and return the final URL.
630
+ * Replicates the _presign_and_upload_one logic from the backend.
631
+ */
632
+ async uploadFileAndGetUrl(filePath) {
633
+ const absPath = (0, import_path.resolve)(filePath.trim().replace(/^<|>$/g, ""));
634
+ try {
635
+ await import_fs.promises.access(absPath);
636
+ } catch (error) {
637
+ throw new Error(`File not found: ${absPath}. Original path: ${filePath}`);
638
+ }
639
+ const fileName = (0, import_path.basename)(absPath);
640
+ const fileData = await import_fs.promises.readFile(absPath);
641
+ const mimeType = (0, import_mime_types.lookup)(absPath) || "application/octet-stream";
642
+ const { data: presignInfo } = await this.http.post(
643
+ "/upload-url",
644
+ { filename: fileName },
645
+ { headers: { "Content-Type": "application/x-www-form-urlencoded" } }
646
+ );
647
+ let finalUrl;
648
+ if (presignInfo.url && presignInfo.fields) {
649
+ const uploadFormData = new import_form_data.default();
650
+ Object.entries(presignInfo.fields).forEach(([key, value]) => {
651
+ uploadFormData.append(key, value);
652
+ });
653
+ uploadFormData.append("file", fileData, { filename: fileName });
654
+ await import_axios.default.post(presignInfo.url, uploadFormData, {
655
+ headers: uploadFormData.getHeaders(),
656
+ timeout: 3e4
657
+ });
658
+ finalUrl = presignInfo.final_url || presignInfo.url.split("?")[0];
659
+ } else if (presignInfo.upload_url || presignInfo.url) {
660
+ const uploadUrl = presignInfo.upload_url || presignInfo.url;
661
+ await import_axios.default.put(uploadUrl, fileData, {
662
+ headers: { "Content-Type": mimeType },
663
+ timeout: 3e4
664
+ });
665
+ finalUrl = presignInfo.final_url || uploadUrl.split("?")[0];
666
+ } else {
667
+ throw new Error(`Unrecognized presign response: ${JSON.stringify(presignInfo)}`);
668
+ }
669
+ return finalUrl;
670
+ }
671
+ async createFormData(fields, uploads, concurrency, logo, audioClip, userImages, userVideos, inputImages) {
448
672
  const formData = new import_form_data.default();
449
673
  fields.forEach(([key, value]) => {
450
674
  if (key !== "logo_path") {
@@ -453,16 +677,147 @@ var Golpo = class {
453
677
  });
454
678
  if (logo) {
455
679
  const logoPath = logo.trim().replace(/^<|>$/g, "");
680
+ let logoUrl;
456
681
  if (URL_RE.test(logoPath)) {
457
- throw new Error("URL uploads not supported for logo - only local file paths are accepted");
682
+ logoUrl = logoPath;
683
+ } else {
684
+ logoUrl = await this.uploadFileAndGetUrl(logoPath);
458
685
  }
459
- const absLogoPath = (0, import_path.resolve)(logoPath);
460
- const logoFileName = (0, import_path.basename)(absLogoPath);
461
- const logoMimeType = (0, import_mime_types.lookup)(absLogoPath) || "application/octet-stream";
462
- formData.append("logo", (0, import_fs.createReadStream)(absLogoPath), {
463
- filename: logoFileName,
464
- contentType: logoMimeType
686
+ formData.append("logo", logoUrl);
687
+ }
688
+ if (audioClip) {
689
+ const audioList = Array.isArray(audioClip) ? audioClip : [audioClip];
690
+ const audioLocalFiles = [];
691
+ const audioUrls = [];
692
+ audioList.forEach((item) => {
693
+ const path = String(item).trim().replace(/^<|>$/g, "");
694
+ if (URL_RE.test(path)) {
695
+ audioUrls.push(path);
696
+ } else {
697
+ audioLocalFiles.push(path);
698
+ }
465
699
  });
700
+ if (audioLocalFiles.length > 0) {
701
+ const limit = (0, import_p_limit.default)(concurrency);
702
+ const uploadedUrls = await Promise.all(
703
+ audioLocalFiles.map(
704
+ (filePath) => limit(() => this.uploadFileAndGetUrl(filePath))
705
+ )
706
+ );
707
+ audioUrls.push(...uploadedUrls);
708
+ }
709
+ if (audioUrls.length > 0) {
710
+ formData.append("audio_clip", audioUrls[0]);
711
+ }
712
+ }
713
+ if (userImages) {
714
+ const imagesList = Array.isArray(userImages) ? userImages : [userImages];
715
+ const imageLocalFiles = [];
716
+ const imageUrls = [];
717
+ console.log("[SDK] Processing userImages:", imagesList);
718
+ imagesList.forEach((item) => {
719
+ const path = String(item).trim().replace(/^<|>$/g, "");
720
+ if (URL_RE.test(path)) {
721
+ console.log("[SDK] userImages item is URL:", path);
722
+ imageUrls.push(path);
723
+ } else {
724
+ console.log("[SDK] userImages item is local file:", path);
725
+ imageLocalFiles.push(path);
726
+ }
727
+ });
728
+ if (imageLocalFiles.length > 0) {
729
+ console.log("[SDK] Uploading", imageLocalFiles.length, "image file(s)");
730
+ const limit = (0, import_p_limit.default)(concurrency);
731
+ try {
732
+ const uploadedUrls = await Promise.all(
733
+ imageLocalFiles.map(
734
+ (filePath) => limit(async () => {
735
+ try {
736
+ console.log("[SDK] Uploading image file:", filePath);
737
+ const url = await this.uploadFileAndGetUrl(filePath);
738
+ console.log("[SDK] Image uploaded, got URL:", url);
739
+ return url;
740
+ } catch (error) {
741
+ console.error(`[SDK] Failed to upload image file ${filePath}:`, error);
742
+ throw error;
743
+ }
744
+ })
745
+ )
746
+ );
747
+ imageUrls.push(...uploadedUrls);
748
+ console.log("[SDK] All images uploaded, total URLs:", imageUrls.length);
749
+ } catch (error) {
750
+ console.error("[SDK] Error uploading image files:", error);
751
+ throw error;
752
+ }
753
+ }
754
+ if (imageUrls.length > 0) {
755
+ const jsonArray = JSON.stringify(imageUrls);
756
+ console.log("[SDK] Sending user_images as JSON:", jsonArray);
757
+ formData.append("user_images", jsonArray);
758
+ } else {
759
+ console.log("[SDK] No image URLs to send");
760
+ }
761
+ }
762
+ if (userVideos) {
763
+ const videosList = Array.isArray(userVideos) ? userVideos : [userVideos];
764
+ const videoLocalFiles = [];
765
+ const videoUrls = [];
766
+ videosList.forEach((item) => {
767
+ const path = String(item).trim().replace(/^<|>$/g, "");
768
+ if (URL_RE.test(path)) {
769
+ videoUrls.push(path);
770
+ } else {
771
+ videoLocalFiles.push(path);
772
+ }
773
+ });
774
+ if (videoLocalFiles.length > 0) {
775
+ const limit = (0, import_p_limit.default)(concurrency);
776
+ try {
777
+ const uploadedUrls = await Promise.all(
778
+ videoLocalFiles.map(
779
+ (filePath) => limit(async () => {
780
+ try {
781
+ return await this.uploadFileAndGetUrl(filePath);
782
+ } catch (error) {
783
+ console.error(`Failed to upload video file ${filePath}:`, error);
784
+ throw error;
785
+ }
786
+ })
787
+ )
788
+ );
789
+ videoUrls.push(...uploadedUrls);
790
+ } catch (error) {
791
+ console.error("Error uploading video files:", error);
792
+ throw error;
793
+ }
794
+ }
795
+ if (videoUrls.length > 0) {
796
+ formData.append("user_videos", JSON.stringify(videoUrls));
797
+ }
798
+ }
799
+ if (inputImages) {
800
+ const imagesList = Array.isArray(inputImages) ? inputImages : [inputImages];
801
+ const localFiles = [];
802
+ const imageUrls = [];
803
+ imagesList.forEach((item) => {
804
+ const path = String(item).trim().replace(/^<|>$/g, "");
805
+ if (URL_RE.test(path)) {
806
+ imageUrls.push(path);
807
+ } else {
808
+ localFiles.push(path);
809
+ }
810
+ });
811
+ if (localFiles.length > 0) {
812
+ const limit = (0, import_p_limit.default)(concurrency);
813
+ const uploadedUrls = await Promise.all(
814
+ localFiles.map((filePath) => limit(() => this.uploadFileAndGetUrl(filePath)))
815
+ );
816
+ imageUrls.push(...uploadedUrls);
817
+ }
818
+ if (imageUrls.length > 0) {
819
+ formData.append("input_images", JSON.stringify(imageUrls));
820
+ }
466
821
  }
467
822
  if (uploads) {
468
823
  const list = typeof uploads === "string" ? [uploads] : [...uploads];
package/dist/index.d.cts CHANGED
@@ -1,36 +1,58 @@
1
+ interface CreateVideoRecordOptions {
2
+ topic: string;
3
+ userEmail?: string;
4
+ context?: string;
5
+ scenes?: number;
6
+ pipeline?: string;
7
+ shareDocuments?: boolean;
8
+ logoPlacement?: string;
9
+ audioClipUrl?: string;
10
+ requestPayload?: any;
11
+ videoId?: string;
12
+ }
1
13
  interface CreatePodcastOptions {
2
14
  uploads?: string | Iterable<string>;
3
- addMusic?: boolean;
4
15
  voiceInstructions?: string;
5
- personality1?: string;
6
- personality2?: string;
7
- doResearch?: boolean;
8
- ttsModel?: 'accurate' | 'fast';
9
16
  language?: string;
10
- style?: 'conversational' | 'solo-male' | 'solo-female';
11
- bgMusic?: 'jazz' | 'lofi' | 'dramatic' | null;
12
- outputVolume?: number;
17
+ style?: 'solo-male-4' | 'solo-female-4' | 'solo-male-3' | 'solo-female-3';
18
+ bgMusic?: 'jazz' | 'lofi' | 'dramatic' | 'whimsical' | 'engaging' | 'hyper' | 'inspirational' | 'documentary' | null;
13
19
  pollIntervalMs?: number;
14
- concurrency?: number;
15
- no_voice_chunking?: boolean;
20
+ newScript?: string;
21
+ timing?: 0.25 | 0.5 | 1 | 2 | 4 | 8 | 10;
16
22
  }
17
23
  interface CreateVideoOptions {
18
24
  uploads?: string | Iterable<string>;
19
25
  voiceInstructions?: string;
20
- personality1?: string;
21
- doResearch?: boolean;
22
- ttsModel?: 'accurate' | 'fast';
23
26
  language?: string;
24
- style?: 'solo-male' | 'solo-female';
25
- bgMusic?: 'engaging' | 'lofi' | null;
26
- bgVolume?: number;
27
- outputVolume?: number;
27
+ style?: 'solo-male-3' | 'solo-female-3' | 'solo-male-4' | 'solo-female-4';
28
+ bgMusic?: 'jazz' | 'lofi' | 'dramatic' | 'whimsical' | 'engaging' | 'hyper' | 'inspirational' | 'documentary' | null;
28
29
  videoType?: 'short' | 'long';
29
30
  includeWatermark?: boolean;
30
31
  logo?: string;
31
- timing?: '1' | '2';
32
+ timing?: 0.25 | 0.5 | 1 | 2 | 4 | 8 | 10;
32
33
  pollIntervalMs?: number;
33
- concurrency?: number;
34
+ useColor?: boolean;
35
+ newScript?: string;
36
+ logoPlacement?: string;
37
+ videoInstructions?: string;
38
+ useLineart2Style?: string;
39
+ audioClip?: string | string[];
40
+ isPublic?: boolean;
41
+ useAsIs?: string | boolean[];
42
+ skipAnimation?: string | boolean[];
43
+ userImages?: string | string[];
44
+ userImagesDescriptions?: string | string[];
45
+ userVideos?: string | string[];
46
+ userVideosDescription?: string | string[];
47
+ userAudioInVideo?: string;
48
+ use2Style?: boolean;
49
+ imageStyle?: 'neon' | 'whiteboard' | 'modern_minimal' | 'playful' | 'technical' | 'editorial';
50
+ inputImages?: string | string[];
51
+ penStyle?: 'stylus' | 'marker' | 'pen';
52
+ showPencilCursor?: boolean;
53
+ pacing?: 'normal' | 'fast';
54
+ displayLanguage?: string;
55
+ justReturnScript?: boolean;
34
56
  }
35
57
  interface EditVideoOptions {
36
58
  frameIds: string[];
@@ -83,6 +105,12 @@ declare class Golpo {
83
105
  }>;
84
106
  createPodcast(prompt: string, opts?: CreatePodcastOptions): Promise<GenerationResult>;
85
107
  createVideo(prompt: string, opts?: CreateVideoOptions): Promise<GenerationResult>;
108
+ /**
109
+ * Private helper: Get user email from API key
110
+ * Backend resolves: api_key -> user_id -> user_credits.email
111
+ */
112
+ private getUserEmailFromApiKey;
113
+ private createVideoRecord;
86
114
  /**
87
115
  * Edit specific frames of a video and regenerate the video.
88
116
  * This method polls until completion and returns the final edited video URL.
@@ -132,7 +160,12 @@ declare class Golpo {
132
160
  combineVideos(opts: CombineVideosOptions): Promise<CombinedVideoResult>;
133
161
  private pollUntilComplete;
134
162
  private pollEditStatus;
163
+ /**
164
+ * Upload a file using /upload-url endpoint and return the final URL.
165
+ * Replicates the _presign_and_upload_one logic from the backend.
166
+ */
167
+ private uploadFileAndGetUrl;
135
168
  private createFormData;
136
169
  }
137
170
 
138
- export { type CombineVideosOptions, type CombinedVideoResult, type CreatePodcastOptions, type CreateVideoOptions, type EditVideoOptions, type GenerationResult, Golpo, type VideoEditResult, Golpo as default };
171
+ export { type CombineVideosOptions, type CombinedVideoResult, type CreatePodcastOptions, type CreateVideoOptions, type CreateVideoRecordOptions, type EditVideoOptions, type GenerationResult, Golpo, type VideoEditResult, Golpo as default };
package/dist/index.d.ts CHANGED
@@ -1,36 +1,58 @@
1
+ interface CreateVideoRecordOptions {
2
+ topic: string;
3
+ userEmail?: string;
4
+ context?: string;
5
+ scenes?: number;
6
+ pipeline?: string;
7
+ shareDocuments?: boolean;
8
+ logoPlacement?: string;
9
+ audioClipUrl?: string;
10
+ requestPayload?: any;
11
+ videoId?: string;
12
+ }
1
13
  interface CreatePodcastOptions {
2
14
  uploads?: string | Iterable<string>;
3
- addMusic?: boolean;
4
15
  voiceInstructions?: string;
5
- personality1?: string;
6
- personality2?: string;
7
- doResearch?: boolean;
8
- ttsModel?: 'accurate' | 'fast';
9
16
  language?: string;
10
- style?: 'conversational' | 'solo-male' | 'solo-female';
11
- bgMusic?: 'jazz' | 'lofi' | 'dramatic' | null;
12
- outputVolume?: number;
17
+ style?: 'solo-male-4' | 'solo-female-4' | 'solo-male-3' | 'solo-female-3';
18
+ bgMusic?: 'jazz' | 'lofi' | 'dramatic' | 'whimsical' | 'engaging' | 'hyper' | 'inspirational' | 'documentary' | null;
13
19
  pollIntervalMs?: number;
14
- concurrency?: number;
15
- no_voice_chunking?: boolean;
20
+ newScript?: string;
21
+ timing?: 0.25 | 0.5 | 1 | 2 | 4 | 8 | 10;
16
22
  }
17
23
  interface CreateVideoOptions {
18
24
  uploads?: string | Iterable<string>;
19
25
  voiceInstructions?: string;
20
- personality1?: string;
21
- doResearch?: boolean;
22
- ttsModel?: 'accurate' | 'fast';
23
26
  language?: string;
24
- style?: 'solo-male' | 'solo-female';
25
- bgMusic?: 'engaging' | 'lofi' | null;
26
- bgVolume?: number;
27
- outputVolume?: number;
27
+ style?: 'solo-male-3' | 'solo-female-3' | 'solo-male-4' | 'solo-female-4';
28
+ bgMusic?: 'jazz' | 'lofi' | 'dramatic' | 'whimsical' | 'engaging' | 'hyper' | 'inspirational' | 'documentary' | null;
28
29
  videoType?: 'short' | 'long';
29
30
  includeWatermark?: boolean;
30
31
  logo?: string;
31
- timing?: '1' | '2';
32
+ timing?: 0.25 | 0.5 | 1 | 2 | 4 | 8 | 10;
32
33
  pollIntervalMs?: number;
33
- concurrency?: number;
34
+ useColor?: boolean;
35
+ newScript?: string;
36
+ logoPlacement?: string;
37
+ videoInstructions?: string;
38
+ useLineart2Style?: string;
39
+ audioClip?: string | string[];
40
+ isPublic?: boolean;
41
+ useAsIs?: string | boolean[];
42
+ skipAnimation?: string | boolean[];
43
+ userImages?: string | string[];
44
+ userImagesDescriptions?: string | string[];
45
+ userVideos?: string | string[];
46
+ userVideosDescription?: string | string[];
47
+ userAudioInVideo?: string;
48
+ use2Style?: boolean;
49
+ imageStyle?: 'neon' | 'whiteboard' | 'modern_minimal' | 'playful' | 'technical' | 'editorial';
50
+ inputImages?: string | string[];
51
+ penStyle?: 'stylus' | 'marker' | 'pen';
52
+ showPencilCursor?: boolean;
53
+ pacing?: 'normal' | 'fast';
54
+ displayLanguage?: string;
55
+ justReturnScript?: boolean;
34
56
  }
35
57
  interface EditVideoOptions {
36
58
  frameIds: string[];
@@ -83,6 +105,12 @@ declare class Golpo {
83
105
  }>;
84
106
  createPodcast(prompt: string, opts?: CreatePodcastOptions): Promise<GenerationResult>;
85
107
  createVideo(prompt: string, opts?: CreateVideoOptions): Promise<GenerationResult>;
108
+ /**
109
+ * Private helper: Get user email from API key
110
+ * Backend resolves: api_key -> user_id -> user_credits.email
111
+ */
112
+ private getUserEmailFromApiKey;
113
+ private createVideoRecord;
86
114
  /**
87
115
  * Edit specific frames of a video and regenerate the video.
88
116
  * This method polls until completion and returns the final edited video URL.
@@ -132,7 +160,12 @@ declare class Golpo {
132
160
  combineVideos(opts: CombineVideosOptions): Promise<CombinedVideoResult>;
133
161
  private pollUntilComplete;
134
162
  private pollEditStatus;
163
+ /**
164
+ * Upload a file using /upload-url endpoint and return the final URL.
165
+ * Replicates the _presign_and_upload_one logic from the backend.
166
+ */
167
+ private uploadFileAndGetUrl;
135
168
  private createFormData;
136
169
  }
137
170
 
138
- export { type CombineVideosOptions, type CombinedVideoResult, type CreatePodcastOptions, type CreateVideoOptions, type EditVideoOptions, type GenerationResult, Golpo, type VideoEditResult, Golpo as default };
171
+ export { type CombineVideosOptions, type CombinedVideoResult, type CreatePodcastOptions, type CreateVideoOptions, type CreateVideoRecordOptions, type EditVideoOptions, type GenerationResult, Golpo, type VideoEditResult, Golpo as default };
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/Golpo.ts
2
2
  import axios from "axios";
3
- import { createReadStream } from "fs";
3
+ import { createReadStream, promises as fs } from "fs";
4
4
  import { basename, resolve } from "path";
5
5
  import { lookup as mimeLookup } from "mime-types";
6
6
  import pLimit from "p-limit";
@@ -20,33 +20,55 @@ var Golpo = class {
20
20
  async createPodcastJob(prompt, opts = {}) {
21
21
  const {
22
22
  uploads,
23
- concurrency = 8,
24
- addMusic = false,
25
23
  voiceInstructions,
26
- personality1,
27
- personality2,
28
- doResearch = false,
29
- ttsModel = "accurate",
30
24
  language,
31
- style = "conversational",
25
+ style = "solo-female-3",
32
26
  bgMusic,
33
- outputVolume = 1,
34
- no_voice_chunking = false
27
+ // Extended params
28
+ newScript,
29
+ timing = 1
30
+ // timing in minutes – default 1 if not provided
35
31
  } = opts;
32
+ const concurrency = 8;
36
33
  const fields = [
37
34
  ["prompt", prompt],
38
- ["add_music", String(addMusic)],
39
- ["do_research", String(doResearch)],
40
- ["tts_model", ttsModel],
41
- ["style", style],
42
- ["output_volume", String(outputVolume)]
35
+ ["style", style]
43
36
  ];
44
37
  if (voiceInstructions) fields.push(["voice_instructions", voiceInstructions]);
45
- if (personality1) fields.push(["personality_1", personality1]);
46
- if (personality2) fields.push(["personality_2", personality2]);
47
38
  if (language) fields.push(["language", language]);
48
39
  if (bgMusic) fields.push(["bg_music", bgMusic]);
49
- if (no_voice_chunking) fields.push(["no_voice_chunking", String(no_voice_chunking)]);
40
+ if (newScript != null) fields.push(["new_script", newScript]);
41
+ fields.push(["timing", String(timing)]);
42
+ fields.push(["video_type", "null"]);
43
+ const hasUploads = (() => {
44
+ if (!uploads) return false;
45
+ if (typeof uploads === "string") return uploads.trim().length > 0;
46
+ const iterator = uploads[Symbol.iterator]();
47
+ const first = iterator.next();
48
+ return !first.done;
49
+ })();
50
+ const request_payload = {
51
+ prompt,
52
+ video_voice: style,
53
+ video_duration: String(timing),
54
+ video_type: "null",
55
+ language,
56
+ voice_instructions: voiceInstructions,
57
+ new_script: newScript,
58
+ bg_music: bgMusic,
59
+ attached_documents: uploads,
60
+ source: "node-sdk"
61
+ };
62
+ try {
63
+ const result = await this.createVideoRecord({
64
+ topic: prompt,
65
+ requestPayload: request_payload
66
+ });
67
+ if (result && result.videoId) {
68
+ fields.push(["video_id", result.videoId]);
69
+ }
70
+ } catch (error) {
71
+ }
50
72
  const formData = await this.createFormData(fields, uploads, concurrency);
51
73
  const { data } = await this.http.post("/generate", formData, {
52
74
  timeout: 24e4,
@@ -83,50 +105,204 @@ var Golpo = class {
83
105
  const {
84
106
  uploads,
85
107
  pollIntervalMs = 2e3,
86
- concurrency = 8,
87
108
  voiceInstructions,
88
- personality1,
89
- doResearch = false,
90
- ttsModel = "accurate",
91
109
  language,
92
- style = "solo-female",
110
+ style = "solo-female-3",
93
111
  bgMusic = "engaging",
94
- bgVolume = 1.4,
95
- outputVolume = 1,
96
112
  videoType = "long",
97
- includeWatermark = true,
113
+ includeWatermark = false,
98
114
  logo,
99
- timing = "1"
115
+ timing = 1,
116
+ useColor = true,
117
+ // newly added backend parameters
118
+ newScript,
119
+ logoPlacement,
120
+ videoInstructions,
121
+ useLineart2Style,
122
+ audioClip,
123
+ isPublic,
124
+ useAsIs,
125
+ skipAnimation,
126
+ userImages,
127
+ userImagesDescriptions,
128
+ userVideos,
129
+ userVideosDescription,
130
+ userAudioInVideo,
131
+ use2Style,
132
+ // specificAnimation, // not yet supported by the Golpo 2.0 pipeline
133
+ imageStyle,
134
+ inputImages,
135
+ penStyle,
136
+ showPencilCursor,
137
+ pacing,
138
+ displayLanguage,
139
+ justReturnScript
100
140
  } = opts;
141
+ const concurrency = 8;
142
+ const request_payload = {
143
+ prompt,
144
+ video_voice: style,
145
+ video_duration: String(timing),
146
+ video_type: videoType,
147
+ language,
148
+ voice_instructions: voiceInstructions,
149
+ bg_music: bgMusic,
150
+ use_color: useColor,
151
+ direct_script: newScript,
152
+ script_mode: newScript ? true : false,
153
+ logo_placement: logoPlacement,
154
+ video_instructions: videoInstructions,
155
+ use_lineart_2_style: useLineart2Style,
156
+ audio_clip: audioClip,
157
+ is_public: isPublic,
158
+ use_as_is: useAsIs,
159
+ skip_animation: skipAnimation,
160
+ user_images: userImages,
161
+ user_images_descriptions: userImagesDescriptions,
162
+ user_videos: userVideos,
163
+ user_videos_description: userVideosDescription,
164
+ user_audio_in_video: userAudioInVideo,
165
+ use_2_0_style: use2Style,
166
+ // specific_animation: specificAnimation, // not yet supported by the pipeline
167
+ image_style: imageStyle,
168
+ input_images: inputImages,
169
+ pen_style: penStyle,
170
+ show_pencil_cursor: penStyle ? true : showPencilCursor ?? false,
171
+ pacing,
172
+ display_language: displayLanguage ?? null,
173
+ own_narration_video_mode: null,
174
+ own_narration_pip_position: null,
175
+ just_return_script: justReturnScript,
176
+ attached_documents: uploads,
177
+ source: "node-sdk"
178
+ };
101
179
  const fields = [
102
180
  ["prompt", prompt],
103
- ["do_research", String(doResearch)],
104
- ["tts_model", ttsModel],
105
- ["bg_volume", String(bgVolume)],
106
- ["output_volume", String(outputVolume)],
107
181
  ["style", style],
108
182
  ["video_type", videoType],
109
183
  ["include_watermark", String(includeWatermark)],
110
- ["timing", timing]
184
+ ["timing", String(timing)],
185
+ ["use_color", String(useColor)]
111
186
  ];
112
187
  if (language) fields.push(["language", language]);
113
188
  if (voiceInstructions) fields.push(["voice_instructions", voiceInstructions]);
114
- if (personality1) fields.push(["personality_1", personality1]);
115
189
  if (bgMusic) fields.push(["bg_music", bgMusic]);
190
+ try {
191
+ const result = await this.createVideoRecord({
192
+ topic: prompt,
193
+ requestPayload: request_payload
194
+ });
195
+ if (result && result.videoId) {
196
+ fields.push(["video_id", result.videoId]);
197
+ }
198
+ } catch (error) {
199
+ }
200
+ if (newScript) fields.push(["new_script", newScript]);
201
+ if (logoPlacement) fields.push(["logo_placement", logoPlacement]);
202
+ if (videoInstructions) fields.push(["video_instructions", videoInstructions]);
203
+ if (useLineart2Style) fields.push(["use_lineart_2_style", useLineart2Style]);
204
+ if (isPublic != null) fields.push(["is_public", String(isPublic)]);
205
+ if (useAsIs) {
206
+ const value = Array.isArray(useAsIs) ? JSON.stringify(useAsIs) : useAsIs;
207
+ fields.push(["use_as_is", value]);
208
+ }
209
+ if (skipAnimation) {
210
+ const value = Array.isArray(skipAnimation) ? JSON.stringify(skipAnimation) : skipAnimation;
211
+ fields.push(["skip_animation", value]);
212
+ }
213
+ if (userImagesDescriptions) {
214
+ const value = Array.isArray(userImagesDescriptions) ? JSON.stringify(userImagesDescriptions) : userImagesDescriptions;
215
+ fields.push(["user_images_descriptions", value]);
216
+ }
217
+ if (userVideosDescription) {
218
+ const value = Array.isArray(userVideosDescription) ? JSON.stringify(userVideosDescription) : userVideosDescription;
219
+ fields.push(["user_videos_description", value]);
220
+ }
221
+ if (userAudioInVideo) {
222
+ fields.push(["user_audio_in_video", userAudioInVideo]);
223
+ } else if (userVideos) {
224
+ fields.push(["user_audio_in_video", "[]"]);
225
+ }
226
+ if (use2Style != null) fields.push(["use_2_0_style", String(use2Style)]);
227
+ if (imageStyle) fields.push(["image_style", imageStyle]);
228
+ if (penStyle) fields.push(["pen_style", penStyle]);
229
+ const effectiveShowPencilCursor = penStyle ? true : showPencilCursor ?? false;
230
+ fields.push(["show_pencil_cursor", String(effectiveShowPencilCursor)]);
231
+ if (pacing) fields.push(["pacing", pacing]);
232
+ if (displayLanguage) fields.push(["display_language", displayLanguage]);
233
+ if (justReturnScript != null) fields.push(["just_return_script", String(justReturnScript)]);
116
234
  if (logo) {
117
235
  fields.push(["logo_path", logo]);
118
236
  }
119
- const formData = await this.createFormData(fields, uploads, concurrency, logo);
237
+ const formData = await this.createFormData(
238
+ fields,
239
+ uploads,
240
+ concurrency,
241
+ logo,
242
+ audioClip,
243
+ userImages,
244
+ userVideos,
245
+ inputImages
246
+ );
120
247
  const { data } = await this.http.post("/generate", formData, {
121
248
  timeout: 24e4,
122
249
  headers: formData.getHeaders()
123
250
  });
251
+ if (justReturnScript) {
252
+ const r2 = await this.pollUntilComplete(
253
+ data.job_id,
254
+ pollIntervalMs,
255
+ "/script-status"
256
+ );
257
+ return { url: "", script: r2.podcast_script, videoId: data.job_id };
258
+ }
124
259
  const r = await this.pollUntilComplete(
125
260
  data.job_id,
126
261
  pollIntervalMs
127
262
  );
128
263
  return { url: r.podcast_url, script: r.podcast_script, videoId: data.job_id };
129
264
  }
265
+ /**
266
+ * Private helper: Get user email from API key
267
+ * Backend resolves: api_key -> user_id -> user_credits.email
268
+ */
269
+ async getUserEmailFromApiKey() {
270
+ try {
271
+ const { data } = await this.http.get("/api/resolve-email-from-api-key");
272
+ return data.email || null;
273
+ } catch (error) {
274
+ return null;
275
+ }
276
+ }
277
+ async createVideoRecord(opts) {
278
+ const {
279
+ topic,
280
+ context = "",
281
+ scenes = 6,
282
+ pipeline = "color",
283
+ shareDocuments = false,
284
+ logoPlacement = null,
285
+ audioClipUrl = null,
286
+ requestPayload
287
+ } = opts;
288
+ const emailFromApiKey = await this.getUserEmailFromApiKey();
289
+ if (!emailFromApiKey) {
290
+ throw new Error("Email not found");
291
+ }
292
+ const body = {
293
+ topic,
294
+ context,
295
+ scenes,
296
+ pipeline,
297
+ user_email: emailFromApiKey,
298
+ share_documents: shareDocuments,
299
+ logo_placement: logoPlacement,
300
+ audio_clip_url: audioClipUrl,
301
+ request_payload: requestPayload ? JSON.stringify(requestPayload) : null
302
+ };
303
+ const { data } = await this.http.post("/api/create-video-record", body);
304
+ return { videoId: data.video_id };
305
+ }
130
306
  /**
131
307
  * Edit specific frames of a video and regenerate the video.
132
308
  * This method polls until completion and returns the final edited video URL.
@@ -350,14 +526,19 @@ var Golpo = class {
350
526
  /* ------------------------------------------------------------ *
351
527
  * INTERNAL HELPERS
352
528
  * ------------------------------------------------------------ */
353
- async pollUntilComplete(jobId, interval) {
529
+ async pollUntilComplete(jobId, interval, statusPath = "/status") {
354
530
  while (true) {
531
+ let data;
355
532
  try {
356
- const { data } = await this.http.get(`/status/${jobId}`);
357
- if (data.status === "completed") return data;
533
+ const res = await this.http.get(`${statusPath}/${jobId}`);
534
+ data = res.data;
358
535
  } catch (error) {
359
536
  console.warn(`Status check failed for job ${jobId}, retrying in ${interval}ms:`, error instanceof Error ? error.message : error);
360
537
  }
538
+ if (data?.status === "completed") return data;
539
+ if (data?.status === "failed") {
540
+ throw new Error(`Job ${jobId} failed: ${data.error || data.message || "unknown error"}`);
541
+ }
361
542
  await new Promise((r) => setTimeout(r, interval));
362
543
  }
363
544
  }
@@ -407,7 +588,50 @@ var Golpo = class {
407
588
  attempts++;
408
589
  }
409
590
  }
410
- async createFormData(fields, uploads, concurrency, logo) {
591
+ /**
592
+ * Upload a file using /upload-url endpoint and return the final URL.
593
+ * Replicates the _presign_and_upload_one logic from the backend.
594
+ */
595
+ async uploadFileAndGetUrl(filePath) {
596
+ const absPath = resolve(filePath.trim().replace(/^<|>$/g, ""));
597
+ try {
598
+ await fs.access(absPath);
599
+ } catch (error) {
600
+ throw new Error(`File not found: ${absPath}. Original path: ${filePath}`);
601
+ }
602
+ const fileName = basename(absPath);
603
+ const fileData = await fs.readFile(absPath);
604
+ const mimeType = mimeLookup(absPath) || "application/octet-stream";
605
+ const { data: presignInfo } = await this.http.post(
606
+ "/upload-url",
607
+ { filename: fileName },
608
+ { headers: { "Content-Type": "application/x-www-form-urlencoded" } }
609
+ );
610
+ let finalUrl;
611
+ if (presignInfo.url && presignInfo.fields) {
612
+ const uploadFormData = new FormData();
613
+ Object.entries(presignInfo.fields).forEach(([key, value]) => {
614
+ uploadFormData.append(key, value);
615
+ });
616
+ uploadFormData.append("file", fileData, { filename: fileName });
617
+ await axios.post(presignInfo.url, uploadFormData, {
618
+ headers: uploadFormData.getHeaders(),
619
+ timeout: 3e4
620
+ });
621
+ finalUrl = presignInfo.final_url || presignInfo.url.split("?")[0];
622
+ } else if (presignInfo.upload_url || presignInfo.url) {
623
+ const uploadUrl = presignInfo.upload_url || presignInfo.url;
624
+ await axios.put(uploadUrl, fileData, {
625
+ headers: { "Content-Type": mimeType },
626
+ timeout: 3e4
627
+ });
628
+ finalUrl = presignInfo.final_url || uploadUrl.split("?")[0];
629
+ } else {
630
+ throw new Error(`Unrecognized presign response: ${JSON.stringify(presignInfo)}`);
631
+ }
632
+ return finalUrl;
633
+ }
634
+ async createFormData(fields, uploads, concurrency, logo, audioClip, userImages, userVideos, inputImages) {
411
635
  const formData = new FormData();
412
636
  fields.forEach(([key, value]) => {
413
637
  if (key !== "logo_path") {
@@ -416,16 +640,147 @@ var Golpo = class {
416
640
  });
417
641
  if (logo) {
418
642
  const logoPath = logo.trim().replace(/^<|>$/g, "");
643
+ let logoUrl;
419
644
  if (URL_RE.test(logoPath)) {
420
- throw new Error("URL uploads not supported for logo - only local file paths are accepted");
645
+ logoUrl = logoPath;
646
+ } else {
647
+ logoUrl = await this.uploadFileAndGetUrl(logoPath);
421
648
  }
422
- const absLogoPath = resolve(logoPath);
423
- const logoFileName = basename(absLogoPath);
424
- const logoMimeType = mimeLookup(absLogoPath) || "application/octet-stream";
425
- formData.append("logo", createReadStream(absLogoPath), {
426
- filename: logoFileName,
427
- contentType: logoMimeType
649
+ formData.append("logo", logoUrl);
650
+ }
651
+ if (audioClip) {
652
+ const audioList = Array.isArray(audioClip) ? audioClip : [audioClip];
653
+ const audioLocalFiles = [];
654
+ const audioUrls = [];
655
+ audioList.forEach((item) => {
656
+ const path = String(item).trim().replace(/^<|>$/g, "");
657
+ if (URL_RE.test(path)) {
658
+ audioUrls.push(path);
659
+ } else {
660
+ audioLocalFiles.push(path);
661
+ }
428
662
  });
663
+ if (audioLocalFiles.length > 0) {
664
+ const limit = pLimit(concurrency);
665
+ const uploadedUrls = await Promise.all(
666
+ audioLocalFiles.map(
667
+ (filePath) => limit(() => this.uploadFileAndGetUrl(filePath))
668
+ )
669
+ );
670
+ audioUrls.push(...uploadedUrls);
671
+ }
672
+ if (audioUrls.length > 0) {
673
+ formData.append("audio_clip", audioUrls[0]);
674
+ }
675
+ }
676
+ if (userImages) {
677
+ const imagesList = Array.isArray(userImages) ? userImages : [userImages];
678
+ const imageLocalFiles = [];
679
+ const imageUrls = [];
680
+ console.log("[SDK] Processing userImages:", imagesList);
681
+ imagesList.forEach((item) => {
682
+ const path = String(item).trim().replace(/^<|>$/g, "");
683
+ if (URL_RE.test(path)) {
684
+ console.log("[SDK] userImages item is URL:", path);
685
+ imageUrls.push(path);
686
+ } else {
687
+ console.log("[SDK] userImages item is local file:", path);
688
+ imageLocalFiles.push(path);
689
+ }
690
+ });
691
+ if (imageLocalFiles.length > 0) {
692
+ console.log("[SDK] Uploading", imageLocalFiles.length, "image file(s)");
693
+ const limit = pLimit(concurrency);
694
+ try {
695
+ const uploadedUrls = await Promise.all(
696
+ imageLocalFiles.map(
697
+ (filePath) => limit(async () => {
698
+ try {
699
+ console.log("[SDK] Uploading image file:", filePath);
700
+ const url = await this.uploadFileAndGetUrl(filePath);
701
+ console.log("[SDK] Image uploaded, got URL:", url);
702
+ return url;
703
+ } catch (error) {
704
+ console.error(`[SDK] Failed to upload image file ${filePath}:`, error);
705
+ throw error;
706
+ }
707
+ })
708
+ )
709
+ );
710
+ imageUrls.push(...uploadedUrls);
711
+ console.log("[SDK] All images uploaded, total URLs:", imageUrls.length);
712
+ } catch (error) {
713
+ console.error("[SDK] Error uploading image files:", error);
714
+ throw error;
715
+ }
716
+ }
717
+ if (imageUrls.length > 0) {
718
+ const jsonArray = JSON.stringify(imageUrls);
719
+ console.log("[SDK] Sending user_images as JSON:", jsonArray);
720
+ formData.append("user_images", jsonArray);
721
+ } else {
722
+ console.log("[SDK] No image URLs to send");
723
+ }
724
+ }
725
+ if (userVideos) {
726
+ const videosList = Array.isArray(userVideos) ? userVideos : [userVideos];
727
+ const videoLocalFiles = [];
728
+ const videoUrls = [];
729
+ videosList.forEach((item) => {
730
+ const path = String(item).trim().replace(/^<|>$/g, "");
731
+ if (URL_RE.test(path)) {
732
+ videoUrls.push(path);
733
+ } else {
734
+ videoLocalFiles.push(path);
735
+ }
736
+ });
737
+ if (videoLocalFiles.length > 0) {
738
+ const limit = pLimit(concurrency);
739
+ try {
740
+ const uploadedUrls = await Promise.all(
741
+ videoLocalFiles.map(
742
+ (filePath) => limit(async () => {
743
+ try {
744
+ return await this.uploadFileAndGetUrl(filePath);
745
+ } catch (error) {
746
+ console.error(`Failed to upload video file ${filePath}:`, error);
747
+ throw error;
748
+ }
749
+ })
750
+ )
751
+ );
752
+ videoUrls.push(...uploadedUrls);
753
+ } catch (error) {
754
+ console.error("Error uploading video files:", error);
755
+ throw error;
756
+ }
757
+ }
758
+ if (videoUrls.length > 0) {
759
+ formData.append("user_videos", JSON.stringify(videoUrls));
760
+ }
761
+ }
762
+ if (inputImages) {
763
+ const imagesList = Array.isArray(inputImages) ? inputImages : [inputImages];
764
+ const localFiles = [];
765
+ const imageUrls = [];
766
+ imagesList.forEach((item) => {
767
+ const path = String(item).trim().replace(/^<|>$/g, "");
768
+ if (URL_RE.test(path)) {
769
+ imageUrls.push(path);
770
+ } else {
771
+ localFiles.push(path);
772
+ }
773
+ });
774
+ if (localFiles.length > 0) {
775
+ const limit = pLimit(concurrency);
776
+ const uploadedUrls = await Promise.all(
777
+ localFiles.map((filePath) => limit(() => this.uploadFileAndGetUrl(filePath)))
778
+ );
779
+ imageUrls.push(...uploadedUrls);
780
+ }
781
+ if (imageUrls.length > 0) {
782
+ formData.append("input_images", JSON.stringify(imageUrls));
783
+ }
429
784
  }
430
785
  if (uploads) {
431
786
  const list = typeof uploads === "string" ? [uploads] : [...uploads];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@golpoai/sdk",
3
- "version": "0.1.10",
3
+ "version": "1.0.1",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {