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 +4 -2
- package/dist/index.js +14 -4
- package/dist/render.d.ts +69 -35
- package/dist/render.js +320 -407
- package/package.json +1 -1
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.
|
|
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
|
-
*
|
|
117
|
-
*
|
|
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
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
|
67
|
+
// Render Functions
|
|
56
68
|
// =============================================================================
|
|
57
69
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
123
|
-
|
|
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
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
-
|
|
183
|
-
|
|
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
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
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
|
-
|
|
237
|
-
|
|
238
|
-
|
|
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
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
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
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
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
|
-
|
|
291
|
-
|
|
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
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
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
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
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
|
-
|
|
349
|
-
|
|
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
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
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
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
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
|
|
415
|
-
|
|
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
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
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
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
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
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
})
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
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
|
-
|
|
485
|
-
|
|
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;
|