@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 +399 -44
- package/dist/index.d.cts +53 -20
- package/dist/index.d.ts +53 -20
- package/dist/index.js +400 -45
- package/package.json +1 -1
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 = "
|
|
62
|
+
style = "solo-female-3",
|
|
69
63
|
bgMusic,
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
["
|
|
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 (
|
|
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 =
|
|
150
|
+
includeWatermark = false,
|
|
135
151
|
logo,
|
|
136
|
-
timing =
|
|
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(
|
|
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
|
|
394
|
-
|
|
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
|
-
|
|
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
|
-
|
|
682
|
+
logoUrl = logoPath;
|
|
683
|
+
} else {
|
|
684
|
+
logoUrl = await this.uploadFileAndGetUrl(logoPath);
|
|
458
685
|
}
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
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?: '
|
|
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
|
-
|
|
15
|
-
|
|
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?: '
|
|
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?:
|
|
32
|
+
timing?: 0.25 | 0.5 | 1 | 2 | 4 | 8 | 10;
|
|
32
33
|
pollIntervalMs?: number;
|
|
33
|
-
|
|
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?: '
|
|
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
|
-
|
|
15
|
-
|
|
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?: '
|
|
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?:
|
|
32
|
+
timing?: 0.25 | 0.5 | 1 | 2 | 4 | 8 | 10;
|
|
32
33
|
pollIntervalMs?: number;
|
|
33
|
-
|
|
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 = "
|
|
25
|
+
style = "solo-female-3",
|
|
32
26
|
bgMusic,
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
["
|
|
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 (
|
|
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 =
|
|
113
|
+
includeWatermark = false,
|
|
98
114
|
logo,
|
|
99
|
-
timing =
|
|
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(
|
|
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
|
|
357
|
-
|
|
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
|
-
|
|
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
|
-
|
|
645
|
+
logoUrl = logoPath;
|
|
646
|
+
} else {
|
|
647
|
+
logoUrl = await this.uploadFileAndGetUrl(logoPath);
|
|
421
648
|
}
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
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];
|