ugcinc 4.1.49 → 4.1.50

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.d.ts CHANGED
@@ -9,10 +9,12 @@ export { TasksClient } from './tasks';
9
9
  export { PostsClient } from './posts';
10
10
  export { StatsClient } from './stats';
11
11
  export { OrganizationClient } from './org';
12
- export { RenderClient } from './render';
13
12
  export { AutomationsClient } from './automations';
14
13
  export { MediaClient } from './media';
15
14
  export { CommentsClient } from './comments';
15
+ export { submitImageRenderJob, submitVideoRenderJob, getRenderJobStatus, submitDeduplicationJob, submitScreenshotAnimationRenderJob, submitAutoCaptionRenderJob, submitInstagramDmRenderJob, submitIMessageDmRenderJob,
16
+ /** @deprecated Use the exported functions directly */
17
+ RenderClient, } from './render';
16
18
  export { areTypesCompatible, computePortsForNode, computePortsWithPreviews, computeAllNodePorts, computeAllNodePortsWithPreviews, getInputPreviewValue, getAllNodes, getNodeByType, getOutputSchema, createNode, deriveConnections, addConnection, removeConnection, removeNodeConnections, cleanupStaleConnections, getConnectedSource, getForEachContext, checkCrossContextViolation, validateWorkflow, getErrorNodeIds, getPortErrorsForNode, hasMissingTriggerError, hasMissingTerminalError, getWorkflowOutputSchema, resolveNodePreview, computeAllNodePreviews, computePreviewMap, updatePreviewMapForConnection, removePreviewForConnection, getPreviewValue, shuffleNodePreview, type PreviewMap, type NodePreviewOutputs, type Connection, generateNodeName, extractWorkflowConfig, type WorkflowTemplateData, } from './automations/graph-controller';
17
19
  export { nodeDefinitions, internalNodeTypes, isAsyncExecutor, formatPortType, isPortType } from './automations/types';
18
20
  export { getNodeDefinition } from './automations/nodes';
package/dist/index.js CHANGED
@@ -5,8 +5,8 @@
5
5
  * Official TypeScript/JavaScript client for the UGC Inc API
6
6
  */
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.isEditModel = exports.getNodeDefinition = exports.isPortType = exports.formatPortType = exports.isAsyncExecutor = exports.internalNodeTypes = exports.nodeDefinitions = exports.extractWorkflowConfig = exports.generateNodeName = exports.shuffleNodePreview = exports.getPreviewValue = exports.removePreviewForConnection = exports.updatePreviewMapForConnection = exports.computePreviewMap = exports.computeAllNodePreviews = exports.resolveNodePreview = exports.getWorkflowOutputSchema = exports.hasMissingTerminalError = exports.hasMissingTriggerError = exports.getPortErrorsForNode = exports.getErrorNodeIds = exports.validateWorkflow = exports.checkCrossContextViolation = exports.getForEachContext = exports.getConnectedSource = exports.cleanupStaleConnections = exports.removeNodeConnections = exports.removeConnection = exports.addConnection = exports.deriveConnections = exports.createNode = exports.getOutputSchema = exports.getNodeByType = exports.getAllNodes = exports.getInputPreviewValue = exports.computeAllNodePortsWithPreviews = exports.computeAllNodePorts = exports.computePortsWithPreviews = exports.computePortsForNode = exports.areTypesCompatible = exports.CommentsClient = exports.MediaClient = exports.AutomationsClient = exports.RenderClient = exports.OrganizationClient = exports.StatsClient = exports.PostsClient = exports.TasksClient = exports.AccountsClient = exports.UGCClient = void 0;
9
- exports.prepareVideoComposerInput = exports.LLMProviders = exports.IfLogicOperators = exports.indexExpressionToIndexes = exports.nodeConfigToCaptionStyle = exports.prepareImageComposerInput = exports.processTemplate = exports.extractTemplateVariables = exports.PortIdSchema = exports.normalizeToPortId = exports.portIdToTitle = exports.isValidPortId = exports.portId = exports.selectFromPool = exports.getModelDurations = exports.getModelAspectRatios = exports.ALL_VIDEO_MODELS = exports.isImageToVideoModel = exports.IMAGE_ASPECT_RATIOS = exports.ALL_IMAGE_MODELS = void 0;
8
+ exports.generateNodeName = exports.shuffleNodePreview = exports.getPreviewValue = exports.removePreviewForConnection = exports.updatePreviewMapForConnection = exports.computePreviewMap = exports.computeAllNodePreviews = exports.resolveNodePreview = exports.getWorkflowOutputSchema = exports.hasMissingTerminalError = exports.hasMissingTriggerError = exports.getPortErrorsForNode = exports.getErrorNodeIds = exports.validateWorkflow = exports.checkCrossContextViolation = exports.getForEachContext = exports.getConnectedSource = exports.cleanupStaleConnections = exports.removeNodeConnections = exports.removeConnection = exports.addConnection = exports.deriveConnections = exports.createNode = exports.getOutputSchema = exports.getNodeByType = exports.getAllNodes = exports.getInputPreviewValue = exports.computeAllNodePortsWithPreviews = exports.computeAllNodePorts = exports.computePortsWithPreviews = exports.computePortsForNode = exports.areTypesCompatible = exports.RenderClient = exports.submitIMessageDmRenderJob = exports.submitInstagramDmRenderJob = exports.submitAutoCaptionRenderJob = exports.submitScreenshotAnimationRenderJob = exports.submitDeduplicationJob = exports.getRenderJobStatus = exports.submitVideoRenderJob = exports.submitImageRenderJob = exports.CommentsClient = exports.MediaClient = exports.AutomationsClient = exports.OrganizationClient = exports.StatsClient = exports.PostsClient = exports.TasksClient = exports.AccountsClient = exports.UGCClient = void 0;
9
+ exports.prepareVideoComposerInput = exports.LLMProviders = exports.IfLogicOperators = exports.indexExpressionToIndexes = exports.nodeConfigToCaptionStyle = exports.prepareImageComposerInput = exports.processTemplate = exports.extractTemplateVariables = exports.PortIdSchema = exports.normalizeToPortId = exports.portIdToTitle = exports.isValidPortId = exports.portId = exports.selectFromPool = exports.getModelDurations = exports.getModelAspectRatios = exports.ALL_VIDEO_MODELS = exports.isImageToVideoModel = exports.IMAGE_ASPECT_RATIOS = exports.ALL_IMAGE_MODELS = exports.isEditModel = exports.getNodeDefinition = exports.isPortType = exports.formatPortType = exports.isAsyncExecutor = exports.internalNodeTypes = exports.nodeDefinitions = exports.extractWorkflowConfig = void 0;
10
10
  // =============================================================================
11
11
  // Client Exports
12
12
  // =============================================================================
@@ -22,14 +22,24 @@ var stats_1 = require("./stats");
22
22
  Object.defineProperty(exports, "StatsClient", { enumerable: true, get: function () { return stats_1.StatsClient; } });
23
23
  var org_1 = require("./org");
24
24
  Object.defineProperty(exports, "OrganizationClient", { enumerable: true, get: function () { return org_1.OrganizationClient; } });
25
- var render_1 = require("./render");
26
- Object.defineProperty(exports, "RenderClient", { enumerable: true, get: function () { return render_1.RenderClient; } });
27
25
  var automations_1 = require("./automations");
28
26
  Object.defineProperty(exports, "AutomationsClient", { enumerable: true, get: function () { return automations_1.AutomationsClient; } });
29
27
  var media_1 = require("./media");
30
28
  Object.defineProperty(exports, "MediaClient", { enumerable: true, get: function () { return media_1.MediaClient; } });
31
29
  var comments_1 = require("./comments");
32
30
  Object.defineProperty(exports, "CommentsClient", { enumerable: true, get: function () { return comments_1.CommentsClient; } });
31
+ // Render functions (use these directly instead of RenderClient)
32
+ var render_1 = require("./render");
33
+ Object.defineProperty(exports, "submitImageRenderJob", { enumerable: true, get: function () { return render_1.submitImageRenderJob; } });
34
+ Object.defineProperty(exports, "submitVideoRenderJob", { enumerable: true, get: function () { return render_1.submitVideoRenderJob; } });
35
+ Object.defineProperty(exports, "getRenderJobStatus", { enumerable: true, get: function () { return render_1.getRenderJobStatus; } });
36
+ Object.defineProperty(exports, "submitDeduplicationJob", { enumerable: true, get: function () { return render_1.submitDeduplicationJob; } });
37
+ Object.defineProperty(exports, "submitScreenshotAnimationRenderJob", { enumerable: true, get: function () { return render_1.submitScreenshotAnimationRenderJob; } });
38
+ Object.defineProperty(exports, "submitAutoCaptionRenderJob", { enumerable: true, get: function () { return render_1.submitAutoCaptionRenderJob; } });
39
+ Object.defineProperty(exports, "submitInstagramDmRenderJob", { enumerable: true, get: function () { return render_1.submitInstagramDmRenderJob; } });
40
+ Object.defineProperty(exports, "submitIMessageDmRenderJob", { enumerable: true, get: function () { return render_1.submitIMessageDmRenderJob; } });
41
+ /** @deprecated Use the exported functions directly */
42
+ Object.defineProperty(exports, "RenderClient", { enumerable: true, get: function () { return render_1.RenderClient; } });
33
43
  // =============================================================================
34
44
  // Graph Controller Exports
35
45
  // =============================================================================
package/dist/render.d.ts CHANGED
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Render functions for submitting jobs to the Modal renderer.
3
+ * These functions call Modal endpoints directly, not the UGC Inc API.
4
+ */
1
5
  import type { ApiResponse } from './types';
2
6
  import type { VideoEditorNodeConfig, DeduplicationInput } from './render/types';
3
7
  import type { CaptionWord, CaptionStyle } from './render/types/caption';
@@ -130,47 +134,55 @@ export interface SubmitIMessageDmRenderJobParams {
130
134
  messageHeaderTimestampText?: string;
131
135
  }
132
136
  /**
133
- * Client for rendering operations
134
- * Note: This calls Modal endpoints directly, not the UGC Inc API
137
+ * Submit an image render job to the Modal renderer.
138
+ * Uses the element-based format for single-source-of-truth rendering.
139
+ */
140
+ export declare function submitImageRenderJob(params: SubmitImageRenderJobParams): Promise<ApiResponse<RenderJobResponse>>;
141
+ /**
142
+ * Submit a video render job to the Modal renderer.
143
+ */
144
+ export declare function submitVideoRenderJob(params: SubmitVideoRenderJobParams): Promise<ApiResponse<RenderJobResponse>>;
145
+ /**
146
+ * Get render job status from the Modal renderer.
147
+ */
148
+ export declare function getRenderJobStatus(jobId: string): Promise<ApiResponse<RenderJobStatus>>;
149
+ /**
150
+ * Submit a deduplication job to the Modal renderer.
151
+ * Applies hash-breaking, metadata injection, and trace removal to a video.
152
+ */
153
+ export declare function submitDeduplicationJob(params: SubmitDeduplicationJobParams): Promise<ApiResponse<RenderJobResponse>>;
154
+ /**
155
+ * Submit a screenshot animation render job to the Modal renderer.
156
+ * Renders an iPhone screenshot animation video from a source image.
157
+ */
158
+ export declare function submitScreenshotAnimationRenderJob(params: SubmitScreenshotAnimationRenderJobParams): Promise<ApiResponse<RenderJobResponse>>;
159
+ /**
160
+ * Submit an auto-caption render job to the Modal renderer.
161
+ * Renders a video with captions overlaid.
162
+ */
163
+ export declare function submitAutoCaptionRenderJob(params: SubmitAutoCaptionRenderJobParams): Promise<ApiResponse<RenderJobResponse>>;
164
+ /**
165
+ * Submit an Instagram DM render job to the Modal renderer.
166
+ * Renders a fake Instagram DM conversation image.
167
+ */
168
+ export declare function submitInstagramDmRenderJob(params: SubmitInstagramDmRenderJobParams): Promise<ApiResponse<RenderJobResponse>>;
169
+ /**
170
+ * Submit an iMessage DM render job to the Modal renderer.
171
+ * Renders a fake iMessage conversation image.
172
+ */
173
+ export declare function submitIMessageDmRenderJob(params: SubmitIMessageDmRenderJobParams): Promise<ApiResponse<RenderJobResponse>>;
174
+ /**
175
+ * @deprecated Use the exported functions directly instead of instantiating RenderClient.
176
+ * Example: `import { submitVideoRenderJob } from 'ugcinc'` instead of `new RenderClient().submitVideoRenderJob`
135
177
  */
136
178
  export declare class RenderClient {
137
- /**
138
- * Submit an image render job to the Modal renderer
139
- * Uses the new element-based format for single-source-of-truth rendering
140
- */
141
- submitImageRenderJob(params: SubmitImageRenderJobParams): Promise<ApiResponse<RenderJobResponse>>;
142
- /**
143
- * Submit a render job to the Modal renderer
144
- */
145
- submitVideoRenderJob(params: SubmitVideoRenderJobParams): Promise<ApiResponse<RenderJobResponse>>;
146
- /**
147
- * Get render job status from the Modal renderer
148
- */
149
- getRenderJobStatus(jobId: string): Promise<ApiResponse<RenderJobStatus>>;
150
- /**
151
- * Submit a deduplication job to the Modal renderer
152
- * Applies hash-breaking, metadata injection, and trace removal to a video
153
- */
154
- submitDeduplicationJob(params: SubmitDeduplicationJobParams): Promise<ApiResponse<RenderJobResponse>>;
155
- /**
156
- * Submit a screenshot animation render job to the Modal renderer
157
- * Renders an iPhone screenshot animation video from a source image
158
- */
159
- submitScreenshotAnimationRenderJob(params: SubmitScreenshotAnimationRenderJobParams): Promise<ApiResponse<RenderJobResponse>>;
160
- /**
161
- * Submit an auto-caption render job to the Modal renderer
162
- * Renders a video with captions overlaid
163
- */
164
- submitAutoCaptionRenderJob(params: SubmitAutoCaptionRenderJobParams): Promise<ApiResponse<RenderJobResponse>>;
165
- /**
166
- * Submit an Instagram DM render job to the Modal renderer
167
- * Renders a fake Instagram DM conversation image
168
- */
169
- submitInstagramDmRenderJob(params: SubmitInstagramDmRenderJobParams): Promise<ApiResponse<RenderJobResponse>>;
170
- /**
171
- * Submit an iMessage DM render job to the Modal renderer
172
- * Renders a fake iMessage conversation image
173
- */
174
- submitIMessageDmRenderJob(params: SubmitIMessageDmRenderJobParams): Promise<ApiResponse<RenderJobResponse>>;
179
+ submitImageRenderJob: typeof submitImageRenderJob;
180
+ submitVideoRenderJob: typeof submitVideoRenderJob;
181
+ getRenderJobStatus: typeof getRenderJobStatus;
182
+ submitDeduplicationJob: typeof submitDeduplicationJob;
183
+ submitScreenshotAnimationRenderJob: typeof submitScreenshotAnimationRenderJob;
184
+ submitAutoCaptionRenderJob: typeof submitAutoCaptionRenderJob;
185
+ submitInstagramDmRenderJob: typeof submitInstagramDmRenderJob;
186
+ submitIMessageDmRenderJob: typeof submitIMessageDmRenderJob;
175
187
  }
176
188
  export {};
package/dist/render.js CHANGED
@@ -1,6 +1,18 @@
1
1
  "use strict";
2
+ /**
3
+ * Render functions for submitting jobs to the Modal renderer.
4
+ * These functions call Modal endpoints directly, not the UGC Inc API.
5
+ */
2
6
  Object.defineProperty(exports, "__esModule", { value: true });
3
7
  exports.RenderClient = void 0;
8
+ exports.submitImageRenderJob = submitImageRenderJob;
9
+ exports.submitVideoRenderJob = submitVideoRenderJob;
10
+ exports.getRenderJobStatus = getRenderJobStatus;
11
+ exports.submitDeduplicationJob = submitDeduplicationJob;
12
+ exports.submitScreenshotAnimationRenderJob = submitScreenshotAnimationRenderJob;
13
+ exports.submitAutoCaptionRenderJob = submitAutoCaptionRenderJob;
14
+ exports.submitInstagramDmRenderJob = submitInstagramDmRenderJob;
15
+ exports.submitIMessageDmRenderJob = submitIMessageDmRenderJob;
4
16
  // Modal renderer endpoints
5
17
  const RENDER_SUBMIT_URL = "https://aidangollan--ugcinc-render-renderer-submit-job.modal.run";
6
18
  const RENDER_STATUS_URL = "https://aidangollan--ugcinc-render-renderer-get-status.modal.run";
@@ -52,507 +64,345 @@ function transformToIMessageMessages(messages, imageAttachmentUrl) {
52
64
  }));
53
65
  }
54
66
  // =============================================================================
55
- // Render Client
67
+ // Render Functions
56
68
  // =============================================================================
57
69
  /**
58
- * Client for rendering operations
59
- * Note: This calls Modal endpoints directly, not the UGC Inc API
70
+ * Submit an image render job to the Modal renderer.
71
+ * Uses the element-based format for single-source-of-truth rendering.
60
72
  */
61
- class RenderClient {
62
- /**
63
- * Submit an image render job to the Modal renderer
64
- * Uses the new element-based format for single-source-of-truth rendering
65
- */
66
- async submitImageRenderJob(params) {
67
- try {
68
- const response = await fetch(RENDER_SUBMIT_URL, {
69
- method: 'POST',
70
- headers: {
71
- 'Content-Type': 'application/json',
73
+ async function submitImageRenderJob(params) {
74
+ try {
75
+ const response = await fetch(RENDER_SUBMIT_URL, {
76
+ method: 'POST',
77
+ headers: { 'Content-Type': 'application/json' },
78
+ body: JSON.stringify({
79
+ config: {
80
+ width: params.config.width,
81
+ height: params.config.height,
82
+ elements: params.config.elements,
83
+ backgroundType: params.config.backgroundType ?? 'image',
84
+ backgroundColor: params.config.backgroundColor,
85
+ backgroundFit: params.config.backgroundFit ?? 'cover',
86
+ dynamicCrop: params.config.dynamicCrop,
87
+ imageUrls: params.imageUrls,
88
+ textValues: params.textValues,
72
89
  },
73
- body: JSON.stringify({
74
- config: {
75
- // New element-based format
76
- width: params.config.width,
77
- height: params.config.height,
78
- elements: params.config.elements,
79
- backgroundType: params.config.backgroundType ?? 'image',
80
- backgroundColor: params.config.backgroundColor,
81
- backgroundFit: params.config.backgroundFit ?? 'cover',
82
- dynamicCrop: params.config.dynamicCrop,
83
- // Inject imageUrls and textValues as sources for the renderer
84
- imageUrls: params.imageUrls,
85
- textValues: params.textValues,
86
- },
87
- output_type: params.output_type ?? 'image',
88
- image_format: params.image_format ?? 'png',
89
- // CapCut metadata injection options
90
- inject_capcut_metadata: params.injectCapcutMetadata ?? false,
91
- capcut_os: params.capcutOs ?? 'ios',
92
- capcut_region: params.capcutRegion ?? 'US',
93
- })
94
- });
95
- const text = await response.text();
96
- let data;
97
- try {
98
- data = JSON.parse(text);
99
- }
100
- catch (jsonError) {
101
- console.error('[Render] JSON parse error:', text.substring(0, 200));
102
- return {
103
- ok: false,
104
- code: response.status || 500,
105
- message: `Modal endpoint error: ${text.substring(0, 100)}`,
106
- };
107
- }
108
- if (data.status === 'error' || !response.ok) {
109
- return {
110
- ok: false,
111
- code: response.status,
112
- message: data.error ?? data.message ?? 'Failed to submit render job',
113
- };
114
- }
115
- return {
116
- ok: true,
117
- code: 200,
118
- message: "Render job submitted",
119
- data,
120
- };
90
+ output_type: params.output_type ?? 'image',
91
+ image_format: params.image_format ?? 'png',
92
+ inject_capcut_metadata: params.injectCapcutMetadata ?? false,
93
+ capcut_os: params.capcutOs ?? 'ios',
94
+ capcut_region: params.capcutRegion ?? 'US',
95
+ })
96
+ });
97
+ const text = await response.text();
98
+ let data;
99
+ try {
100
+ data = JSON.parse(text);
101
+ }
102
+ catch {
103
+ return { ok: false, code: response.status || 500, message: `Modal endpoint error: ${text.substring(0, 100)}` };
121
104
  }
122
- catch (error) {
123
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
124
- console.error('[Render] Submit error:', error);
125
- return {
126
- ok: false,
127
- code: 500,
128
- message: `Failed to submit render job: ${errorMessage}`,
129
- };
105
+ if (data.status === 'error' || !response.ok) {
106
+ return { ok: false, code: response.status, message: data.error ?? data.message ?? 'Failed to submit render job' };
130
107
  }
108
+ return { ok: true, code: 200, message: "Render job submitted", data };
131
109
  }
132
- /**
133
- * Submit a render job to the Modal renderer
134
- */
135
- async submitVideoRenderJob(params) {
110
+ catch (error) {
111
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
112
+ return { ok: false, code: 500, message: `Failed to submit render job: ${errorMessage}` };
113
+ }
114
+ }
115
+ /**
116
+ * Submit a video render job to the Modal renderer.
117
+ */
118
+ async function submitVideoRenderJob(params) {
119
+ try {
120
+ const response = await fetch(RENDER_SUBMIT_URL, {
121
+ method: 'POST',
122
+ headers: { 'Content-Type': 'application/json' },
123
+ body: JSON.stringify({
124
+ config: params.config,
125
+ sources: params.sources,
126
+ output_type: params.output_type,
127
+ image_format: params.image_format,
128
+ video_codec: params.video_codec,
129
+ inject_capcut_metadata: params.injectCapcutMetadata ?? false,
130
+ capcut_os: params.capcutOs ?? 'ios',
131
+ capcut_region: params.capcutRegion ?? 'US',
132
+ reencode_h265: params.reencodeH265 ?? false,
133
+ })
134
+ });
135
+ const text = await response.text();
136
+ let data;
136
137
  try {
137
- const response = await fetch(RENDER_SUBMIT_URL, {
138
- method: 'POST',
139
- headers: {
140
- 'Content-Type': 'application/json',
141
- },
142
- body: JSON.stringify({
143
- config: params.config,
144
- sources: params.sources,
145
- output_type: params.output_type,
146
- image_format: params.image_format,
147
- video_codec: params.video_codec,
148
- // CapCut metadata injection options
149
- inject_capcut_metadata: params.injectCapcutMetadata ?? false,
150
- capcut_os: params.capcutOs ?? 'ios',
151
- capcut_region: params.capcutRegion ?? 'US',
152
- reencode_h265: params.reencodeH265 ?? false,
153
- })
154
- });
155
- const text = await response.text();
156
- let data;
157
- try {
158
- data = JSON.parse(text);
159
- }
160
- catch (jsonError) {
161
- console.error('[Render] JSON parse error:', text.substring(0, 200));
162
- return {
163
- ok: false,
164
- code: response.status || 500,
165
- message: `Modal endpoint error: ${text.substring(0, 100)}`,
166
- };
167
- }
168
- if (data.status === 'error' || !response.ok) {
169
- return {
170
- ok: false,
171
- code: response.status,
172
- message: data.error ?? data.message ?? 'Failed to submit render job',
173
- };
174
- }
175
- return {
176
- ok: true,
177
- code: 200,
178
- message: "Render job submitted",
179
- data,
180
- };
138
+ data = JSON.parse(text);
139
+ }
140
+ catch {
141
+ return { ok: false, code: response.status || 500, message: `Modal endpoint error: ${text.substring(0, 100)}` };
181
142
  }
182
- catch (error) {
183
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
184
- console.error('[Render] Submit error:', error);
185
- return {
186
- ok: false,
187
- code: 500,
188
- message: `Failed to submit render job: ${errorMessage}`,
189
- };
143
+ if (data.status === 'error' || !response.ok) {
144
+ return { ok: false, code: response.status, message: data.error ?? data.message ?? 'Failed to submit render job' };
190
145
  }
146
+ return { ok: true, code: 200, message: "Render job submitted", data };
191
147
  }
192
- /**
193
- * Get render job status from the Modal renderer
194
- */
195
- async getRenderJobStatus(jobId) {
148
+ catch (error) {
149
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
150
+ return { ok: false, code: 500, message: `Failed to submit render job: ${errorMessage}` };
151
+ }
152
+ }
153
+ /**
154
+ * Get render job status from the Modal renderer.
155
+ */
156
+ async function getRenderJobStatus(jobId) {
157
+ try {
158
+ const response = await fetch(RENDER_STATUS_URL, {
159
+ method: 'POST',
160
+ headers: { 'Content-Type': 'application/json' },
161
+ body: JSON.stringify({ job_id: jobId }),
162
+ });
163
+ const text = await response.text();
164
+ let data;
196
165
  try {
197
- const response = await fetch(RENDER_STATUS_URL, {
198
- method: 'POST',
199
- headers: {
200
- 'Content-Type': 'application/json',
201
- },
202
- body: JSON.stringify({ job_id: jobId }),
203
- });
204
- const text = await response.text();
205
- let data;
206
- try {
207
- data = JSON.parse(text);
208
- }
209
- catch (jsonError) {
210
- console.error('[Render] JSON parse error:', text.substring(0, 200));
211
- return {
212
- ok: false,
213
- code: response.status || 500,
214
- message: `Modal endpoint error: ${text.substring(0, 100)}`,
215
- };
216
- }
217
- if (data.status === 'error' || !response.ok) {
218
- return {
219
- ok: false,
220
- code: response.status,
221
- message: data.error ?? data.message ?? 'Failed to get job status',
222
- };
223
- }
224
- // Modal returns Vercel Blob URL as download_url (fast CDN access)
225
- // Fallback to download endpoint if not provided
226
- if (data.status === 'completed' && !data.download_url) {
227
- data.download_url = `https://aidangollan--ugcinc-render-renderer-download.modal.run?job_id=${jobId}`;
228
- }
229
- return {
230
- ok: true,
231
- code: 200,
232
- message: "Job status retrieved",
233
- data,
234
- };
166
+ data = JSON.parse(text);
167
+ }
168
+ catch {
169
+ return { ok: false, code: response.status || 500, message: `Modal endpoint error: ${text.substring(0, 100)}` };
170
+ }
171
+ if (data.status === 'error' || !response.ok) {
172
+ return { ok: false, code: response.status, message: data.error ?? data.message ?? 'Failed to get job status' };
235
173
  }
236
- catch (error) {
237
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
238
- console.error('[Render] Status error:', error);
239
- return {
240
- ok: false,
241
- code: 500,
242
- message: `Failed to get job status: ${errorMessage}`,
243
- };
174
+ // Fallback to download endpoint if Modal didn't provide a URL
175
+ if (data.status === 'completed' && !data.download_url) {
176
+ data.download_url = `https://aidangollan--ugcinc-render-renderer-download.modal.run?job_id=${jobId}`;
244
177
  }
178
+ return { ok: true, code: 200, message: "Job status retrieved", data };
245
179
  }
246
- /**
247
- * Submit a deduplication job to the Modal renderer
248
- * Applies hash-breaking, metadata injection, and trace removal to a video
249
- */
250
- async submitDeduplicationJob(params) {
180
+ catch (error) {
181
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
182
+ return { ok: false, code: 500, message: `Failed to get job status: ${errorMessage}` };
183
+ }
184
+ }
185
+ /**
186
+ * Submit a deduplication job to the Modal renderer.
187
+ * Applies hash-breaking, metadata injection, and trace removal to a video.
188
+ */
189
+ async function submitDeduplicationJob(params) {
190
+ try {
191
+ const response = await fetch(RENDER_SUBMIT_URL, {
192
+ method: 'POST',
193
+ headers: { 'Content-Type': 'application/json' },
194
+ body: JSON.stringify({
195
+ video_url: params.video_url,
196
+ deduplication: params.deduplication,
197
+ output_type: 'video',
198
+ })
199
+ });
200
+ const text = await response.text();
201
+ let data;
251
202
  try {
252
- const response = await fetch(RENDER_SUBMIT_URL, {
253
- method: 'POST',
254
- headers: {
255
- 'Content-Type': 'application/json',
256
- },
257
- body: JSON.stringify({
258
- video_url: params.video_url,
259
- deduplication: params.deduplication,
260
- output_type: 'video',
261
- })
262
- });
263
- const text = await response.text();
264
- let data;
265
- try {
266
- data = JSON.parse(text);
267
- }
268
- catch (jsonError) {
269
- console.error('[Render] JSON parse error:', text.substring(0, 200));
270
- return {
271
- ok: false,
272
- code: response.status || 500,
273
- message: `Modal endpoint error: ${text.substring(0, 100)}`,
274
- };
275
- }
276
- if (data.status === 'error' || !response.ok) {
277
- return {
278
- ok: false,
279
- code: response.status,
280
- message: data.error ?? data.message ?? 'Failed to submit deduplication job',
281
- };
282
- }
283
- return {
284
- ok: true,
285
- code: 200,
286
- message: "Deduplication job submitted",
287
- data,
288
- };
203
+ data = JSON.parse(text);
204
+ }
205
+ catch {
206
+ return { ok: false, code: response.status || 500, message: `Modal endpoint error: ${text.substring(0, 100)}` };
289
207
  }
290
- catch (error) {
291
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
292
- console.error('[Render] Submit deduplication error:', error);
293
- return {
294
- ok: false,
295
- code: 500,
296
- message: `Failed to submit deduplication job: ${errorMessage}`,
297
- };
208
+ if (data.status === 'error' || !response.ok) {
209
+ return { ok: false, code: response.status, message: data.error ?? data.message ?? 'Failed to submit deduplication job' };
298
210
  }
211
+ return { ok: true, code: 200, message: "Deduplication job submitted", data };
299
212
  }
300
- /**
301
- * Submit a screenshot animation render job to the Modal renderer
302
- * Renders an iPhone screenshot animation video from a source image
303
- */
304
- async submitScreenshotAnimationRenderJob(params) {
305
- try {
306
- const response = await fetch(RENDER_SUBMIT_URL, {
307
- method: 'POST',
308
- headers: {
309
- 'Content-Type': 'application/json',
213
+ catch (error) {
214
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
215
+ return { ok: false, code: 500, message: `Failed to submit deduplication job: ${errorMessage}` };
216
+ }
217
+ }
218
+ /**
219
+ * Submit a screenshot animation render job to the Modal renderer.
220
+ * Renders an iPhone screenshot animation video from a source image.
221
+ */
222
+ async function submitScreenshotAnimationRenderJob(params) {
223
+ try {
224
+ const response = await fetch(RENDER_SUBMIT_URL, {
225
+ method: 'POST',
226
+ headers: { 'Content-Type': 'application/json' },
227
+ body: JSON.stringify({
228
+ config: {
229
+ _compositionType: 'screenshot-animation',
230
+ imageUrl: params.imageUrl,
231
+ holdDurationMs: params.holdDurationMs,
310
232
  },
311
- body: JSON.stringify({
312
- config: {
313
- _compositionType: 'screenshot-animation',
314
- imageUrl: params.imageUrl,
315
- holdDurationMs: params.holdDurationMs,
316
- },
317
- output_type: 'video',
318
- video_codec: 'h264',
319
- })
320
- });
321
- const text = await response.text();
322
- let data;
323
- try {
324
- data = JSON.parse(text);
325
- }
326
- catch (jsonError) {
327
- console.error('[Render] JSON parse error:', text.substring(0, 200));
328
- return {
329
- ok: false,
330
- code: response.status || 500,
331
- message: `Modal endpoint error: ${text.substring(0, 100)}`,
332
- };
333
- }
334
- if (data.status === 'error' || !response.ok) {
335
- return {
336
- ok: false,
337
- code: response.status,
338
- message: data.error ?? data.message ?? 'Failed to submit screenshot animation job',
339
- };
340
- }
341
- return {
342
- ok: true,
343
- code: 200,
344
- message: "Screenshot animation job submitted",
345
- data,
346
- };
233
+ output_type: 'video',
234
+ video_codec: 'h264',
235
+ })
236
+ });
237
+ const text = await response.text();
238
+ let data;
239
+ try {
240
+ data = JSON.parse(text);
241
+ }
242
+ catch {
243
+ return { ok: false, code: response.status || 500, message: `Modal endpoint error: ${text.substring(0, 100)}` };
347
244
  }
348
- catch (error) {
349
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
350
- console.error('[Render] Submit screenshot animation error:', error);
351
- return {
352
- ok: false,
353
- code: 500,
354
- message: `Failed to submit screenshot animation job: ${errorMessage}`,
355
- };
245
+ if (data.status === 'error' || !response.ok) {
246
+ return { ok: false, code: response.status, message: data.error ?? data.message ?? 'Failed to submit screenshot animation job' };
356
247
  }
248
+ return { ok: true, code: 200, message: "Screenshot animation job submitted", data };
357
249
  }
358
- /**
359
- * Submit an auto-caption render job to the Modal renderer
360
- * Renders a video with captions overlaid
361
- */
362
- async submitAutoCaptionRenderJob(params) {
363
- try {
364
- const response = await fetch(RENDER_SUBMIT_URL, {
365
- method: 'POST',
366
- headers: {
367
- 'Content-Type': 'application/json',
250
+ catch (error) {
251
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
252
+ return { ok: false, code: 500, message: `Failed to submit screenshot animation job: ${errorMessage}` };
253
+ }
254
+ }
255
+ /**
256
+ * Submit an auto-caption render job to the Modal renderer.
257
+ * Renders a video with captions overlaid.
258
+ */
259
+ async function submitAutoCaptionRenderJob(params) {
260
+ try {
261
+ const response = await fetch(RENDER_SUBMIT_URL, {
262
+ method: 'POST',
263
+ headers: { 'Content-Type': 'application/json' },
264
+ body: JSON.stringify({
265
+ config: {
266
+ _compositionType: 'auto-caption',
267
+ videoUrl: params.videoUrl,
268
+ width: params.width,
269
+ height: params.height,
270
+ durationMs: params.durationMs,
271
+ fps: params.fps,
272
+ captions: params.captions,
273
+ style: params.style,
368
274
  },
369
- body: JSON.stringify({
370
- config: {
371
- _compositionType: 'auto-caption',
372
- videoUrl: params.videoUrl,
373
- width: params.width,
374
- height: params.height,
375
- durationMs: params.durationMs,
376
- fps: params.fps,
377
- captions: params.captions,
378
- style: params.style,
379
- },
380
- output_type: 'video',
381
- video_codec: 'h264',
382
- })
383
- });
384
- const text = await response.text();
385
- let data;
386
- try {
387
- data = JSON.parse(text);
388
- }
389
- catch (jsonError) {
390
- console.error('[Render] JSON parse error:', text.substring(0, 200));
391
- return {
392
- ok: false,
393
- code: response.status || 500,
394
- message: `Modal endpoint error: ${text.substring(0, 100)}`,
395
- };
396
- }
397
- if (data.status === 'error' || !response.ok) {
398
- return {
399
- ok: false,
400
- code: response.status,
401
- message: data.error ?? data.message ?? 'Failed to submit auto-caption job',
402
- };
403
- }
404
- return {
405
- ok: true,
406
- code: 200,
407
- message: "Auto-caption job submitted",
408
- data,
409
- };
275
+ output_type: 'video',
276
+ video_codec: 'h264',
277
+ })
278
+ });
279
+ const text = await response.text();
280
+ let data;
281
+ try {
282
+ data = JSON.parse(text);
410
283
  }
411
- catch (error) {
412
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
413
- console.error('[Render] Submit auto-caption error:', error);
414
- return {
415
- ok: false,
416
- code: 500,
417
- message: `Failed to submit auto-caption job: ${errorMessage}`,
418
- };
284
+ catch {
285
+ return { ok: false, code: response.status || 500, message: `Modal endpoint error: ${text.substring(0, 100)}` };
419
286
  }
287
+ if (data.status === 'error' || !response.ok) {
288
+ return { ok: false, code: response.status, message: data.error ?? data.message ?? 'Failed to submit auto-caption job' };
289
+ }
290
+ return { ok: true, code: 200, message: "Auto-caption job submitted", data };
420
291
  }
421
- /**
422
- * Submit an Instagram DM render job to the Modal renderer
423
- * Renders a fake Instagram DM conversation image
424
- */
425
- async submitInstagramDmRenderJob(params) {
426
- try {
427
- // Transform CreateDmMessage[] to internal format
428
- const transformedMessages = transformToInstagramMessages(params.messages, params.imageAttachmentUrl);
429
- const hasStoryReply = params.messages.some(msg => msg.hasImage);
430
- const response = await fetch(RENDER_SUBMIT_URL, {
431
- method: 'POST',
432
- headers: {
433
- 'Content-Type': 'application/json',
434
- },
435
- body: JSON.stringify({
436
- config: {
437
- _compositionType: 'instagram-dm',
438
- input: {
439
- receiverUsername: params.receiverUsername,
440
- receiverProfilePicUrl: params.receiverProfilePicUrl,
441
- theme: params.theme,
442
- messages: transformedMessages,
443
- storyImageUrl: hasStoryReply ? params.imageAttachmentUrl : undefined,
444
- },
292
+ catch (error) {
293
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
294
+ return { ok: false, code: 500, message: `Failed to submit auto-caption job: ${errorMessage}` };
295
+ }
296
+ }
297
+ /**
298
+ * Submit an Instagram DM render job to the Modal renderer.
299
+ * Renders a fake Instagram DM conversation image.
300
+ */
301
+ async function submitInstagramDmRenderJob(params) {
302
+ try {
303
+ const transformedMessages = transformToInstagramMessages(params.messages, params.imageAttachmentUrl);
304
+ const hasStoryReply = params.messages.some(msg => msg.hasImage);
305
+ const response = await fetch(RENDER_SUBMIT_URL, {
306
+ method: 'POST',
307
+ headers: { 'Content-Type': 'application/json' },
308
+ body: JSON.stringify({
309
+ config: {
310
+ _compositionType: 'instagram-dm',
311
+ input: {
312
+ receiverUsername: params.receiverUsername,
313
+ receiverProfilePicUrl: params.receiverProfilePicUrl,
314
+ theme: params.theme,
315
+ messages: transformedMessages,
316
+ storyImageUrl: hasStoryReply ? params.imageAttachmentUrl : undefined,
445
317
  },
446
- output_type: 'image',
447
- image_format: 'png',
448
- })
449
- });
450
- const text = await response.text();
451
- let data;
452
- try {
453
- data = JSON.parse(text);
454
- }
455
- catch (jsonError) {
456
- console.error('[Render] JSON parse error:', text.substring(0, 200));
457
- return {
458
- ok: false,
459
- code: response.status || 500,
460
- message: `Modal endpoint error: ${text.substring(0, 100)}`,
461
- };
462
- }
463
- if (data.status === 'error' || !response.ok) {
464
- return {
465
- ok: false,
466
- code: response.status,
467
- message: data.error ?? data.message ?? 'Failed to submit Instagram DM job',
468
- };
469
- }
470
- return {
471
- ok: true,
472
- code: 200,
473
- message: "Instagram DM job submitted",
474
- data,
475
- };
318
+ },
319
+ output_type: 'image',
320
+ image_format: 'png',
321
+ })
322
+ });
323
+ const text = await response.text();
324
+ let data;
325
+ try {
326
+ data = JSON.parse(text);
327
+ }
328
+ catch {
329
+ return { ok: false, code: response.status || 500, message: `Modal endpoint error: ${text.substring(0, 100)}` };
476
330
  }
477
- catch (error) {
478
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
479
- console.error('[Render] Submit Instagram DM error:', error);
480
- return {
481
- ok: false,
482
- code: 500,
483
- message: `Failed to submit Instagram DM job: ${errorMessage}`,
484
- };
331
+ if (data.status === 'error' || !response.ok) {
332
+ return { ok: false, code: response.status, message: data.error ?? data.message ?? 'Failed to submit Instagram DM job' };
485
333
  }
334
+ return { ok: true, code: 200, message: "Instagram DM job submitted", data };
486
335
  }
487
- /**
488
- * Submit an iMessage DM render job to the Modal renderer
489
- * Renders a fake iMessage conversation image
490
- */
491
- async submitIMessageDmRenderJob(params) {
492
- try {
493
- // Transform CreateDmMessage[] to internal format
494
- const transformedMessages = transformToIMessageMessages(params.messages, params.imageAttachmentUrl);
495
- const response = await fetch(RENDER_SUBMIT_URL, {
496
- method: 'POST',
497
- headers: {
498
- 'Content-Type': 'application/json',
499
- },
500
- body: JSON.stringify({
501
- config: {
502
- _compositionType: 'imessage-dm',
503
- input: {
504
- username: params.username,
505
- profilePicUrl: params.profilePicUrl,
506
- theme: params.theme,
507
- time: params.time,
508
- messages: transformedMessages,
509
- senderBubbleColor: params.senderBubbleColor,
510
- showReadReceipt: params.showReadReceipt,
511
- readReceiptText: params.readReceiptText,
512
- unreadBadgeText: params.unreadBadgeText,
513
- messageHeaderTimestampText: params.messageHeaderTimestampText,
514
- },
336
+ catch (error) {
337
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
338
+ return { ok: false, code: 500, message: `Failed to submit Instagram DM job: ${errorMessage}` };
339
+ }
340
+ }
341
+ /**
342
+ * Submit an iMessage DM render job to the Modal renderer.
343
+ * Renders a fake iMessage conversation image.
344
+ */
345
+ async function submitIMessageDmRenderJob(params) {
346
+ try {
347
+ const transformedMessages = transformToIMessageMessages(params.messages, params.imageAttachmentUrl);
348
+ const response = await fetch(RENDER_SUBMIT_URL, {
349
+ method: 'POST',
350
+ headers: { 'Content-Type': 'application/json' },
351
+ body: JSON.stringify({
352
+ config: {
353
+ _compositionType: 'imessage-dm',
354
+ input: {
355
+ username: params.username,
356
+ profilePicUrl: params.profilePicUrl,
357
+ theme: params.theme,
358
+ time: params.time,
359
+ messages: transformedMessages,
360
+ senderBubbleColor: params.senderBubbleColor,
361
+ showReadReceipt: params.showReadReceipt,
362
+ readReceiptText: params.readReceiptText,
363
+ unreadBadgeText: params.unreadBadgeText,
364
+ messageHeaderTimestampText: params.messageHeaderTimestampText,
515
365
  },
516
- output_type: 'image',
517
- image_format: 'png',
518
- })
519
- });
520
- const text = await response.text();
521
- let data;
522
- try {
523
- data = JSON.parse(text);
524
- }
525
- catch (jsonError) {
526
- console.error('[Render] JSON parse error:', text.substring(0, 200));
527
- return {
528
- ok: false,
529
- code: response.status || 500,
530
- message: `Modal endpoint error: ${text.substring(0, 100)}`,
531
- };
532
- }
533
- if (data.status === 'error' || !response.ok) {
534
- return {
535
- ok: false,
536
- code: response.status,
537
- message: data.error ?? data.message ?? 'Failed to submit iMessage DM job',
538
- };
539
- }
540
- return {
541
- ok: true,
542
- code: 200,
543
- message: "iMessage DM job submitted",
544
- data,
545
- };
366
+ },
367
+ output_type: 'image',
368
+ image_format: 'png',
369
+ })
370
+ });
371
+ const text = await response.text();
372
+ let data;
373
+ try {
374
+ data = JSON.parse(text);
375
+ }
376
+ catch {
377
+ return { ok: false, code: response.status || 500, message: `Modal endpoint error: ${text.substring(0, 100)}` };
546
378
  }
547
- catch (error) {
548
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
549
- console.error('[Render] Submit iMessage DM error:', error);
550
- return {
551
- ok: false,
552
- code: 500,
553
- message: `Failed to submit iMessage DM job: ${errorMessage}`,
554
- };
379
+ if (data.status === 'error' || !response.ok) {
380
+ return { ok: false, code: response.status, message: data.error ?? data.message ?? 'Failed to submit iMessage DM job' };
555
381
  }
382
+ return { ok: true, code: 200, message: "iMessage DM job submitted", data };
383
+ }
384
+ catch (error) {
385
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
386
+ return { ok: false, code: 500, message: `Failed to submit iMessage DM job: ${errorMessage}` };
387
+ }
388
+ }
389
+ // =============================================================================
390
+ // Deprecated: RenderClient class (use functions directly instead)
391
+ // =============================================================================
392
+ /**
393
+ * @deprecated Use the exported functions directly instead of instantiating RenderClient.
394
+ * Example: `import { submitVideoRenderJob } from 'ugcinc'` instead of `new RenderClient().submitVideoRenderJob`
395
+ */
396
+ class RenderClient {
397
+ constructor() {
398
+ this.submitImageRenderJob = submitImageRenderJob;
399
+ this.submitVideoRenderJob = submitVideoRenderJob;
400
+ this.getRenderJobStatus = getRenderJobStatus;
401
+ this.submitDeduplicationJob = submitDeduplicationJob;
402
+ this.submitScreenshotAnimationRenderJob = submitScreenshotAnimationRenderJob;
403
+ this.submitAutoCaptionRenderJob = submitAutoCaptionRenderJob;
404
+ this.submitInstagramDmRenderJob = submitInstagramDmRenderJob;
405
+ this.submitIMessageDmRenderJob = submitIMessageDmRenderJob;
556
406
  }
557
407
  }
558
408
  exports.RenderClient = RenderClient;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ugcinc",
3
- "version": "4.1.49",
3
+ "version": "4.1.50",
4
4
  "description": "TypeScript/JavaScript client for the UGC Inc API",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",