ugcinc 4.1.48 → 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';
@@ -31,7 +33,7 @@ export type { RefreshStatsParams, RefreshStatsError, RefreshStatsResponse, Daily
31
33
  export type { ApiKey, DeleteApiKeyParams, EditApiKeyParams } from './org';
32
34
  export type { UserMedia, MediaUse, SocialAudio, Media, GetMediaParams, GetSocialAudioParams, UploadMediaParams, UploadMediaResponse, MediaTagUpdate, UpdateMediaTagsParams, MediaTagUpdateResult, UpdateMediaTagsResponse, UpdateMediaTagParams, DeleteMediaParams, DeleteMediaResponse, CreateSocialAudioParams, ImportTextParams, ImportTextResponse, CreateMediaFromUrlParams, GetMediaUseParams, GetMediaUseResponse, FilterMediaParams, FilterMediaResponse, GetUploadTokenParams, UploadTokenResponse, } from './media';
33
35
  export type { CommentStatus, Comment, CreateCommentParams, CreateCommentResponse, GetCommentsParams, } from './comments';
34
- export type { RenderJobResponse, RenderJobStatus, SubmitImageRenderJobParams, SubmitVideoRenderJobParams, SubmitScreenshotAnimationRenderJobParams, SubmitInstagramDmRenderJobParams, SubmitIMessageDmRenderJobParams, IgDmMessage, ImDmMessage, RenderVideoEditorConfig, } from './render';
36
+ export type { RenderJobResponse, RenderJobStatus, SubmitImageRenderJobParams, SubmitVideoRenderJobParams, SubmitScreenshotAnimationRenderJobParams, SubmitAutoCaptionRenderJobParams, SubmitInstagramDmRenderJobParams, SubmitIMessageDmRenderJobParams, IgDmMessage, ImDmMessage, RenderVideoEditorConfig, } from './render';
35
37
  export type { VideoEditorNodeConfig, VideoEditorChannel, VideoEditorSegment, VideoEditorVideoSegment, VideoEditorAudioSegment, VideoEditorImageSegment, VideoEditorTextSegment, VideoEditorImageSequenceSegment, VideoEditorVideoSequenceSegment, TimeValue, TimeMode, SegmentTimelinePosition, DeduplicationLevel, DeduplicationInput, ImageEditorElement, DimensionPresetKey, } from './render/types';
36
38
  export type { ImageEditorNodeConfig, ImageComposerNodeConfig, ImageComposerRenderInput } from './automations/nodes/image-composer';
37
39
  export { prepareImageComposerInput } from './automations/nodes/image-composer';
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,5 +1,10 @@
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';
7
+ import type { CaptionWord, CaptionStyle } from './render/types/caption';
3
8
  import type { ImageEditorNodeConfig } from './automations/nodes/image-composer';
4
9
  import type { CreateDmMessage } from './automations/nodes/create-dm';
5
10
  export interface RenderJobResponse {
@@ -61,6 +66,22 @@ export interface SubmitScreenshotAnimationRenderJobParams {
61
66
  /** Duration to hold at end after animation completes (ms) - default 500 */
62
67
  holdDurationMs?: number;
63
68
  }
69
+ export interface SubmitAutoCaptionRenderJobParams {
70
+ /** Video source URL */
71
+ videoUrl: string;
72
+ /** Output width in pixels */
73
+ width: number;
74
+ /** Output height in pixels */
75
+ height: number;
76
+ /** Video duration in milliseconds */
77
+ durationMs: number;
78
+ /** Frames per second */
79
+ fps: number;
80
+ /** Caption words with timing from transcription */
81
+ captions: CaptionWord[];
82
+ /** Caption styling configuration */
83
+ style: CaptionStyle;
84
+ }
64
85
  interface IgDmMessageInternal {
65
86
  text: string;
66
87
  sender: 'user' | 'recipient';
@@ -113,42 +134,55 @@ export interface SubmitIMessageDmRenderJobParams {
113
134
  messageHeaderTimestampText?: string;
114
135
  }
115
136
  /**
116
- * Client for rendering operations
117
- * 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`
118
177
  */
119
178
  export declare class RenderClient {
120
- /**
121
- * Submit an image render job to the Modal renderer
122
- * Uses the new element-based format for single-source-of-truth rendering
123
- */
124
- submitImageRenderJob(params: SubmitImageRenderJobParams): Promise<ApiResponse<RenderJobResponse>>;
125
- /**
126
- * Submit a render job to the Modal renderer
127
- */
128
- submitVideoRenderJob(params: SubmitVideoRenderJobParams): Promise<ApiResponse<RenderJobResponse>>;
129
- /**
130
- * Get render job status from the Modal renderer
131
- */
132
- getRenderJobStatus(jobId: string): Promise<ApiResponse<RenderJobStatus>>;
133
- /**
134
- * Submit a deduplication job to the Modal renderer
135
- * Applies hash-breaking, metadata injection, and trace removal to a video
136
- */
137
- submitDeduplicationJob(params: SubmitDeduplicationJobParams): Promise<ApiResponse<RenderJobResponse>>;
138
- /**
139
- * Submit a screenshot animation render job to the Modal renderer
140
- * Renders an iPhone screenshot animation video from a source image
141
- */
142
- submitScreenshotAnimationRenderJob(params: SubmitScreenshotAnimationRenderJobParams): Promise<ApiResponse<RenderJobResponse>>;
143
- /**
144
- * Submit an Instagram DM render job to the Modal renderer
145
- * Renders a fake Instagram DM conversation image
146
- */
147
- submitInstagramDmRenderJob(params: SubmitInstagramDmRenderJobParams): Promise<ApiResponse<RenderJobResponse>>;
148
- /**
149
- * Submit an iMessage DM render job to the Modal renderer
150
- * Renders a fake iMessage conversation image
151
- */
152
- 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;
153
187
  }
154
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,444 +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 Instagram DM render job to the Modal renderer
360
- * Renders a fake Instagram DM conversation image
361
- */
362
- async submitInstagramDmRenderJob(params) {
363
- try {
364
- // Transform CreateDmMessage[] to internal format
365
- const transformedMessages = transformToInstagramMessages(params.messages, params.imageAttachmentUrl);
366
- const hasStoryReply = params.messages.some(msg => msg.hasImage);
367
- const response = await fetch(RENDER_SUBMIT_URL, {
368
- method: 'POST',
369
- headers: {
370
- '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,
371
274
  },
372
- body: JSON.stringify({
373
- config: {
374
- _compositionType: 'instagram-dm',
375
- input: {
376
- receiverUsername: params.receiverUsername,
377
- receiverProfilePicUrl: params.receiverProfilePicUrl,
378
- theme: params.theme,
379
- messages: transformedMessages,
380
- storyImageUrl: hasStoryReply ? params.imageAttachmentUrl : undefined,
381
- },
382
- },
383
- output_type: 'image',
384
- image_format: 'png',
385
- })
386
- });
387
- const text = await response.text();
388
- let data;
389
- try {
390
- data = JSON.parse(text);
391
- }
392
- catch (jsonError) {
393
- console.error('[Render] JSON parse error:', text.substring(0, 200));
394
- return {
395
- ok: false,
396
- code: response.status || 500,
397
- message: `Modal endpoint error: ${text.substring(0, 100)}`,
398
- };
399
- }
400
- if (data.status === 'error' || !response.ok) {
401
- return {
402
- ok: false,
403
- code: response.status,
404
- message: data.error ?? data.message ?? 'Failed to submit Instagram DM job',
405
- };
406
- }
407
- return {
408
- ok: true,
409
- code: 200,
410
- message: "Instagram DM job submitted",
411
- data,
412
- };
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);
413
283
  }
414
- catch (error) {
415
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
416
- console.error('[Render] Submit Instagram DM error:', error);
417
- return {
418
- ok: false,
419
- code: 500,
420
- message: `Failed to submit Instagram DM job: ${errorMessage}`,
421
- };
284
+ catch {
285
+ return { ok: false, code: response.status || 500, message: `Modal endpoint error: ${text.substring(0, 100)}` };
422
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 };
423
291
  }
424
- /**
425
- * Submit an iMessage DM render job to the Modal renderer
426
- * Renders a fake iMessage conversation image
427
- */
428
- async submitIMessageDmRenderJob(params) {
429
- try {
430
- // Transform CreateDmMessage[] to internal format
431
- const transformedMessages = transformToIMessageMessages(params.messages, params.imageAttachmentUrl);
432
- const response = await fetch(RENDER_SUBMIT_URL, {
433
- method: 'POST',
434
- headers: {
435
- 'Content-Type': 'application/json',
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,
317
+ },
436
318
  },
437
- body: JSON.stringify({
438
- config: {
439
- _compositionType: 'imessage-dm',
440
- input: {
441
- username: params.username,
442
- profilePicUrl: params.profilePicUrl,
443
- theme: params.theme,
444
- time: params.time,
445
- messages: transformedMessages,
446
- senderBubbleColor: params.senderBubbleColor,
447
- showReadReceipt: params.showReadReceipt,
448
- readReceiptText: params.readReceiptText,
449
- unreadBadgeText: params.unreadBadgeText,
450
- messageHeaderTimestampText: params.messageHeaderTimestampText,
451
- },
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)}` };
330
+ }
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' };
333
+ }
334
+ return { ok: true, code: 200, message: "Instagram DM job submitted", data };
335
+ }
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,
452
365
  },
453
- output_type: 'image',
454
- image_format: 'png',
455
- })
456
- });
457
- const text = await response.text();
458
- let data;
459
- try {
460
- data = JSON.parse(text);
461
- }
462
- catch (jsonError) {
463
- console.error('[Render] JSON parse error:', text.substring(0, 200));
464
- return {
465
- ok: false,
466
- code: response.status || 500,
467
- message: `Modal endpoint error: ${text.substring(0, 100)}`,
468
- };
469
- }
470
- if (data.status === 'error' || !response.ok) {
471
- return {
472
- ok: false,
473
- code: response.status,
474
- message: data.error ?? data.message ?? 'Failed to submit iMessage DM job',
475
- };
476
- }
477
- return {
478
- ok: true,
479
- code: 200,
480
- message: "iMessage DM job submitted",
481
- data,
482
- };
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)}` };
483
378
  }
484
- catch (error) {
485
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
486
- console.error('[Render] Submit iMessage DM error:', error);
487
- return {
488
- ok: false,
489
- code: 500,
490
- message: `Failed to submit iMessage DM job: ${errorMessage}`,
491
- };
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' };
492
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;
493
406
  }
494
407
  }
495
408
  exports.RenderClient = RenderClient;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ugcinc",
3
- "version": "4.1.48",
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",