ugcinc 4.5.58 → 4.5.60
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/posts.d.ts +1 -0
- package/dist/render.js +71 -8
- package/package.json +2 -2
package/dist/posts.d.ts
CHANGED
package/dist/render.js
CHANGED
|
@@ -17,6 +17,69 @@ const RENDER_BASE_URL = "https://render.ugc.inc";
|
|
|
17
17
|
const RENDER_SUBMIT_URL = `${RENDER_BASE_URL}/submit-job`;
|
|
18
18
|
const RENDER_STATUS_URL = `${RENDER_BASE_URL}/get-status`;
|
|
19
19
|
// =============================================================================
|
|
20
|
+
// Rate Limiting & Retry (Modal workspace limit: 200 req/s)
|
|
21
|
+
// =============================================================================
|
|
22
|
+
const MODAL_RATE_LIMIT = 150; // per second, safety margin below 200
|
|
23
|
+
const MAX_RETRIES = 3;
|
|
24
|
+
// Sliding window: timestamps of recent requests within the last second
|
|
25
|
+
const requestTimestamps = [];
|
|
26
|
+
function acquireRateSlot() {
|
|
27
|
+
const now = Date.now();
|
|
28
|
+
// Evict timestamps older than 1 second
|
|
29
|
+
while (requestTimestamps.length > 0 && requestTimestamps[0] < now - 1000) {
|
|
30
|
+
requestTimestamps.shift();
|
|
31
|
+
}
|
|
32
|
+
if (requestTimestamps.length < MODAL_RATE_LIMIT) {
|
|
33
|
+
requestTimestamps.push(now);
|
|
34
|
+
return 0; // no wait needed
|
|
35
|
+
}
|
|
36
|
+
// Calculate ms until the oldest request exits the window
|
|
37
|
+
return 1000 - (now - requestTimestamps[0]) + 10;
|
|
38
|
+
}
|
|
39
|
+
async function waitForRateSlot() {
|
|
40
|
+
let waitMs = acquireRateSlot();
|
|
41
|
+
while (waitMs > 0) {
|
|
42
|
+
await new Promise(resolve => setTimeout(resolve, waitMs));
|
|
43
|
+
waitMs = acquireRateSlot();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Fetch wrapper for all Modal API calls.
|
|
48
|
+
* Applies rate limiting (150 req/s) and retries on 429/5xx with exponential backoff.
|
|
49
|
+
*/
|
|
50
|
+
async function modalFetch(url, options) {
|
|
51
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
52
|
+
await waitForRateSlot();
|
|
53
|
+
let response;
|
|
54
|
+
try {
|
|
55
|
+
response = await fetch(url, options);
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
// Network error — retry with backoff
|
|
59
|
+
if (attempt < MAX_RETRIES) {
|
|
60
|
+
const backoff = Math.pow(2, attempt) * 500 + Math.random() * 500;
|
|
61
|
+
await new Promise(resolve => setTimeout(resolve, backoff));
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
// Success or non-retryable client error (400, 401, 403, 404, etc.)
|
|
67
|
+
if (response.ok || (response.status >= 400 && response.status < 500 && response.status !== 429)) {
|
|
68
|
+
return response;
|
|
69
|
+
}
|
|
70
|
+
// Retryable: 429 (rate limit) or 5xx (server error)
|
|
71
|
+
if (attempt < MAX_RETRIES) {
|
|
72
|
+
const backoff = Math.pow(2, attempt) * 500 + Math.random() * 500;
|
|
73
|
+
await new Promise(resolve => setTimeout(resolve, backoff));
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
return response; // Return error response on final attempt
|
|
77
|
+
}
|
|
78
|
+
// Unreachable, but satisfies TypeScript
|
|
79
|
+
await waitForRateSlot();
|
|
80
|
+
return fetch(url, options);
|
|
81
|
+
}
|
|
82
|
+
// =============================================================================
|
|
20
83
|
// DM Message Transformation Helpers
|
|
21
84
|
// =============================================================================
|
|
22
85
|
/**
|
|
@@ -72,7 +135,7 @@ function transformToIMessageMessages(messages, imageAttachmentUrl) {
|
|
|
72
135
|
*/
|
|
73
136
|
async function submitImageRenderJob(params) {
|
|
74
137
|
try {
|
|
75
|
-
const response = await
|
|
138
|
+
const response = await modalFetch(RENDER_SUBMIT_URL, {
|
|
76
139
|
method: 'POST',
|
|
77
140
|
headers: { 'Content-Type': 'application/json' },
|
|
78
141
|
body: JSON.stringify({
|
|
@@ -117,7 +180,7 @@ async function submitImageRenderJob(params) {
|
|
|
117
180
|
*/
|
|
118
181
|
async function submitVideoRenderJob(params) {
|
|
119
182
|
try {
|
|
120
|
-
const response = await
|
|
183
|
+
const response = await modalFetch(RENDER_SUBMIT_URL, {
|
|
121
184
|
method: 'POST',
|
|
122
185
|
headers: { 'Content-Type': 'application/json' },
|
|
123
186
|
body: JSON.stringify({
|
|
@@ -155,7 +218,7 @@ async function submitVideoRenderJob(params) {
|
|
|
155
218
|
*/
|
|
156
219
|
async function getRenderJobStatus(jobId) {
|
|
157
220
|
try {
|
|
158
|
-
const response = await
|
|
221
|
+
const response = await modalFetch(RENDER_STATUS_URL, {
|
|
159
222
|
method: 'POST',
|
|
160
223
|
headers: { 'Content-Type': 'application/json' },
|
|
161
224
|
body: JSON.stringify({ job_id: jobId }),
|
|
@@ -188,7 +251,7 @@ async function getRenderJobStatus(jobId) {
|
|
|
188
251
|
*/
|
|
189
252
|
async function submitDeduplicationJob(params) {
|
|
190
253
|
try {
|
|
191
|
-
const response = await
|
|
254
|
+
const response = await modalFetch(RENDER_SUBMIT_URL, {
|
|
192
255
|
method: 'POST',
|
|
193
256
|
headers: { 'Content-Type': 'application/json' },
|
|
194
257
|
body: JSON.stringify({
|
|
@@ -221,7 +284,7 @@ async function submitDeduplicationJob(params) {
|
|
|
221
284
|
*/
|
|
222
285
|
async function submitScreenshotAnimationRenderJob(params) {
|
|
223
286
|
try {
|
|
224
|
-
const response = await
|
|
287
|
+
const response = await modalFetch(RENDER_SUBMIT_URL, {
|
|
225
288
|
method: 'POST',
|
|
226
289
|
headers: { 'Content-Type': 'application/json' },
|
|
227
290
|
body: JSON.stringify({
|
|
@@ -258,7 +321,7 @@ async function submitScreenshotAnimationRenderJob(params) {
|
|
|
258
321
|
*/
|
|
259
322
|
async function submitAutoCaptionRenderJob(params) {
|
|
260
323
|
try {
|
|
261
|
-
const response = await
|
|
324
|
+
const response = await modalFetch(RENDER_SUBMIT_URL, {
|
|
262
325
|
method: 'POST',
|
|
263
326
|
headers: { 'Content-Type': 'application/json' },
|
|
264
327
|
body: JSON.stringify({
|
|
@@ -302,7 +365,7 @@ async function submitInstagramDmRenderJob(params) {
|
|
|
302
365
|
try {
|
|
303
366
|
const transformedMessages = transformToInstagramMessages(params.messages, params.imageAttachmentUrl);
|
|
304
367
|
const hasStoryReply = params.messages.some(msg => msg.hasImage);
|
|
305
|
-
const response = await
|
|
368
|
+
const response = await modalFetch(RENDER_SUBMIT_URL, {
|
|
306
369
|
method: 'POST',
|
|
307
370
|
headers: { 'Content-Type': 'application/json' },
|
|
308
371
|
body: JSON.stringify({
|
|
@@ -346,7 +409,7 @@ async function submitInstagramDmRenderJob(params) {
|
|
|
346
409
|
async function submitIMessageDmRenderJob(params) {
|
|
347
410
|
try {
|
|
348
411
|
const transformedMessages = transformToIMessageMessages(params.messages, params.imageAttachmentUrl);
|
|
349
|
-
const response = await
|
|
412
|
+
const response = await modalFetch(RENDER_SUBMIT_URL, {
|
|
350
413
|
method: 'POST',
|
|
351
414
|
headers: { 'Content-Type': 'application/json' },
|
|
352
415
|
body: JSON.stringify({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ugcinc",
|
|
3
|
-
"version": "4.5.
|
|
3
|
+
"version": "4.5.60",
|
|
4
4
|
"description": "TypeScript/JavaScript client for the UGC Inc API",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"typescript": "^5.0.0"
|
|
57
57
|
},
|
|
58
58
|
"bin": {
|
|
59
|
-
"ugcinc": "
|
|
59
|
+
"ugcinc": "dist/cli.js"
|
|
60
60
|
},
|
|
61
61
|
"files": [
|
|
62
62
|
"dist",
|