ugcinc 4.1.49 → 4.1.51
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 +1 -1
- package/dist/index.js +12 -4
- package/dist/render.d.ts +40 -42
- package/dist/render.js +298 -469
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -9,10 +9,10 @@ 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, } from './render';
|
|
16
16
|
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
17
|
export { nodeDefinitions, internalNodeTypes, isAsyncExecutor, formatPortType, isPortType } from './automations/types';
|
|
18
18
|
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.
|
|
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.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.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 = void 0;
|
|
10
10
|
// =============================================================================
|
|
11
11
|
// Client Exports
|
|
12
12
|
// =============================================================================
|
|
@@ -22,14 +22,22 @@ 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
|
|
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; } });
|
|
33
41
|
// =============================================================================
|
|
34
42
|
// Graph Controller Exports
|
|
35
43
|
// =============================================================================
|
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,41 @@ export interface SubmitIMessageDmRenderJobParams {
|
|
|
130
134
|
messageHeaderTimestampText?: string;
|
|
131
135
|
}
|
|
132
136
|
/**
|
|
133
|
-
*
|
|
134
|
-
*
|
|
137
|
+
* Submit an image render job to the Modal renderer.
|
|
138
|
+
* Uses the element-based format for single-source-of-truth rendering.
|
|
135
139
|
*/
|
|
136
|
-
export declare
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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>>;
|
|
175
|
-
}
|
|
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>>;
|
|
176
174
|
export {};
|
package/dist/render.js
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
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
|
-
exports.
|
|
7
|
+
exports.submitImageRenderJob = submitImageRenderJob;
|
|
8
|
+
exports.submitVideoRenderJob = submitVideoRenderJob;
|
|
9
|
+
exports.getRenderJobStatus = getRenderJobStatus;
|
|
10
|
+
exports.submitDeduplicationJob = submitDeduplicationJob;
|
|
11
|
+
exports.submitScreenshotAnimationRenderJob = submitScreenshotAnimationRenderJob;
|
|
12
|
+
exports.submitAutoCaptionRenderJob = submitAutoCaptionRenderJob;
|
|
13
|
+
exports.submitInstagramDmRenderJob = submitInstagramDmRenderJob;
|
|
14
|
+
exports.submitIMessageDmRenderJob = submitIMessageDmRenderJob;
|
|
4
15
|
// Modal renderer endpoints
|
|
5
16
|
const RENDER_SUBMIT_URL = "https://aidangollan--ugcinc-render-renderer-submit-job.modal.run";
|
|
6
17
|
const RENDER_STATUS_URL = "https://aidangollan--ugcinc-render-renderer-get-status.modal.run";
|
|
@@ -52,507 +63,325 @@ function transformToIMessageMessages(messages, imageAttachmentUrl) {
|
|
|
52
63
|
}));
|
|
53
64
|
}
|
|
54
65
|
// =============================================================================
|
|
55
|
-
// Render
|
|
66
|
+
// Render Functions
|
|
56
67
|
// =============================================================================
|
|
57
68
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
69
|
+
* Submit an image render job to the Modal renderer.
|
|
70
|
+
* Uses the element-based format for single-source-of-truth rendering.
|
|
60
71
|
*/
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
+
async function submitImageRenderJob(params) {
|
|
73
|
+
try {
|
|
74
|
+
const response = await fetch(RENDER_SUBMIT_URL, {
|
|
75
|
+
method: 'POST',
|
|
76
|
+
headers: { 'Content-Type': 'application/json' },
|
|
77
|
+
body: JSON.stringify({
|
|
78
|
+
config: {
|
|
79
|
+
width: params.config.width,
|
|
80
|
+
height: params.config.height,
|
|
81
|
+
elements: params.config.elements,
|
|
82
|
+
backgroundType: params.config.backgroundType ?? 'image',
|
|
83
|
+
backgroundColor: params.config.backgroundColor,
|
|
84
|
+
backgroundFit: params.config.backgroundFit ?? 'cover',
|
|
85
|
+
dynamicCrop: params.config.dynamicCrop,
|
|
86
|
+
imageUrls: params.imageUrls,
|
|
87
|
+
textValues: params.textValues,
|
|
72
88
|
},
|
|
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
|
-
};
|
|
89
|
+
output_type: params.output_type ?? 'image',
|
|
90
|
+
image_format: params.image_format ?? 'png',
|
|
91
|
+
inject_capcut_metadata: params.injectCapcutMetadata ?? false,
|
|
92
|
+
capcut_os: params.capcutOs ?? 'ios',
|
|
93
|
+
capcut_region: params.capcutRegion ?? 'US',
|
|
94
|
+
})
|
|
95
|
+
});
|
|
96
|
+
const text = await response.text();
|
|
97
|
+
let data;
|
|
98
|
+
try {
|
|
99
|
+
data = JSON.parse(text);
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return { ok: false, code: response.status || 500, message: `Modal endpoint error: ${text.substring(0, 100)}` };
|
|
121
103
|
}
|
|
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
|
-
};
|
|
104
|
+
if (data.status === 'error' || !response.ok) {
|
|
105
|
+
return { ok: false, code: response.status, message: data.error ?? data.message ?? 'Failed to submit render job' };
|
|
130
106
|
}
|
|
107
|
+
return { ok: true, code: 200, message: "Render job submitted", data };
|
|
131
108
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
109
|
+
catch (error) {
|
|
110
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
111
|
+
return { ok: false, code: 500, message: `Failed to submit render job: ${errorMessage}` };
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Submit a video render job to the Modal renderer.
|
|
116
|
+
*/
|
|
117
|
+
async function submitVideoRenderJob(params) {
|
|
118
|
+
try {
|
|
119
|
+
const response = await fetch(RENDER_SUBMIT_URL, {
|
|
120
|
+
method: 'POST',
|
|
121
|
+
headers: { 'Content-Type': 'application/json' },
|
|
122
|
+
body: JSON.stringify({
|
|
123
|
+
config: params.config,
|
|
124
|
+
sources: params.sources,
|
|
125
|
+
output_type: params.output_type,
|
|
126
|
+
image_format: params.image_format,
|
|
127
|
+
video_codec: params.video_codec,
|
|
128
|
+
inject_capcut_metadata: params.injectCapcutMetadata ?? false,
|
|
129
|
+
capcut_os: params.capcutOs ?? 'ios',
|
|
130
|
+
capcut_region: params.capcutRegion ?? 'US',
|
|
131
|
+
reencode_h265: params.reencodeH265 ?? false,
|
|
132
|
+
})
|
|
133
|
+
});
|
|
134
|
+
const text = await response.text();
|
|
135
|
+
let data;
|
|
136
136
|
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
|
-
};
|
|
137
|
+
data = JSON.parse(text);
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
return { ok: false, code: response.status || 500, message: `Modal endpoint error: ${text.substring(0, 100)}` };
|
|
181
141
|
}
|
|
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
|
-
};
|
|
142
|
+
if (data.status === 'error' || !response.ok) {
|
|
143
|
+
return { ok: false, code: response.status, message: data.error ?? data.message ?? 'Failed to submit render job' };
|
|
190
144
|
}
|
|
145
|
+
return { ok: true, code: 200, message: "Render job submitted", data };
|
|
191
146
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
147
|
+
catch (error) {
|
|
148
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
149
|
+
return { ok: false, code: 500, message: `Failed to submit render job: ${errorMessage}` };
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Get render job status from the Modal renderer.
|
|
154
|
+
*/
|
|
155
|
+
async function getRenderJobStatus(jobId) {
|
|
156
|
+
try {
|
|
157
|
+
const response = await fetch(RENDER_STATUS_URL, {
|
|
158
|
+
method: 'POST',
|
|
159
|
+
headers: { 'Content-Type': 'application/json' },
|
|
160
|
+
body: JSON.stringify({ job_id: jobId }),
|
|
161
|
+
});
|
|
162
|
+
const text = await response.text();
|
|
163
|
+
let data;
|
|
196
164
|
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
|
-
};
|
|
165
|
+
data = JSON.parse(text);
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
return { ok: false, code: response.status || 500, message: `Modal endpoint error: ${text.substring(0, 100)}` };
|
|
169
|
+
}
|
|
170
|
+
if (data.status === 'error' || !response.ok) {
|
|
171
|
+
return { ok: false, code: response.status, message: data.error ?? data.message ?? 'Failed to get job status' };
|
|
235
172
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
return {
|
|
240
|
-
ok: false,
|
|
241
|
-
code: 500,
|
|
242
|
-
message: `Failed to get job status: ${errorMessage}`,
|
|
243
|
-
};
|
|
173
|
+
// Fallback to download endpoint if Modal didn't provide a URL
|
|
174
|
+
if (data.status === 'completed' && !data.download_url) {
|
|
175
|
+
data.download_url = `https://aidangollan--ugcinc-render-renderer-download.modal.run?job_id=${jobId}`;
|
|
244
176
|
}
|
|
177
|
+
return { ok: true, code: 200, message: "Job status retrieved", data };
|
|
245
178
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
179
|
+
catch (error) {
|
|
180
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
181
|
+
return { ok: false, code: 500, message: `Failed to get job status: ${errorMessage}` };
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Submit a deduplication job to the Modal renderer.
|
|
186
|
+
* Applies hash-breaking, metadata injection, and trace removal to a video.
|
|
187
|
+
*/
|
|
188
|
+
async function submitDeduplicationJob(params) {
|
|
189
|
+
try {
|
|
190
|
+
const response = await fetch(RENDER_SUBMIT_URL, {
|
|
191
|
+
method: 'POST',
|
|
192
|
+
headers: { 'Content-Type': 'application/json' },
|
|
193
|
+
body: JSON.stringify({
|
|
194
|
+
video_url: params.video_url,
|
|
195
|
+
deduplication: params.deduplication,
|
|
196
|
+
output_type: 'video',
|
|
197
|
+
})
|
|
198
|
+
});
|
|
199
|
+
const text = await response.text();
|
|
200
|
+
let data;
|
|
251
201
|
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
|
-
};
|
|
202
|
+
data = JSON.parse(text);
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
return { ok: false, code: response.status || 500, message: `Modal endpoint error: ${text.substring(0, 100)}` };
|
|
289
206
|
}
|
|
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
|
-
};
|
|
207
|
+
if (data.status === 'error' || !response.ok) {
|
|
208
|
+
return { ok: false, code: response.status, message: data.error ?? data.message ?? 'Failed to submit deduplication job' };
|
|
298
209
|
}
|
|
210
|
+
return { ok: true, code: 200, message: "Deduplication job submitted", data };
|
|
299
211
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
212
|
+
catch (error) {
|
|
213
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
214
|
+
return { ok: false, code: 500, message: `Failed to submit deduplication job: ${errorMessage}` };
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Submit a screenshot animation render job to the Modal renderer.
|
|
219
|
+
* Renders an iPhone screenshot animation video from a source image.
|
|
220
|
+
*/
|
|
221
|
+
async function submitScreenshotAnimationRenderJob(params) {
|
|
222
|
+
try {
|
|
223
|
+
const response = await fetch(RENDER_SUBMIT_URL, {
|
|
224
|
+
method: 'POST',
|
|
225
|
+
headers: { 'Content-Type': 'application/json' },
|
|
226
|
+
body: JSON.stringify({
|
|
227
|
+
config: {
|
|
228
|
+
_compositionType: 'screenshot-animation',
|
|
229
|
+
imageUrl: params.imageUrl,
|
|
230
|
+
holdDurationMs: params.holdDurationMs,
|
|
310
231
|
},
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
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
|
-
};
|
|
232
|
+
output_type: 'video',
|
|
233
|
+
video_codec: 'h264',
|
|
234
|
+
})
|
|
235
|
+
});
|
|
236
|
+
const text = await response.text();
|
|
237
|
+
let data;
|
|
238
|
+
try {
|
|
239
|
+
data = JSON.parse(text);
|
|
347
240
|
}
|
|
348
|
-
catch
|
|
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
|
-
};
|
|
241
|
+
catch {
|
|
242
|
+
return { ok: false, code: response.status || 500, message: `Modal endpoint error: ${text.substring(0, 100)}` };
|
|
356
243
|
}
|
|
244
|
+
if (data.status === 'error' || !response.ok) {
|
|
245
|
+
return { ok: false, code: response.status, message: data.error ?? data.message ?? 'Failed to submit screenshot animation job' };
|
|
246
|
+
}
|
|
247
|
+
return { ok: true, code: 200, message: "Screenshot animation job submitted", data };
|
|
357
248
|
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
249
|
+
catch (error) {
|
|
250
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
251
|
+
return { ok: false, code: 500, message: `Failed to submit screenshot animation job: ${errorMessage}` };
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Submit an auto-caption render job to the Modal renderer.
|
|
256
|
+
* Renders a video with captions overlaid.
|
|
257
|
+
*/
|
|
258
|
+
async function submitAutoCaptionRenderJob(params) {
|
|
259
|
+
try {
|
|
260
|
+
const response = await fetch(RENDER_SUBMIT_URL, {
|
|
261
|
+
method: 'POST',
|
|
262
|
+
headers: { 'Content-Type': 'application/json' },
|
|
263
|
+
body: JSON.stringify({
|
|
264
|
+
config: {
|
|
265
|
+
_compositionType: 'auto-caption',
|
|
266
|
+
videoUrl: params.videoUrl,
|
|
267
|
+
width: params.width,
|
|
268
|
+
height: params.height,
|
|
269
|
+
durationMs: params.durationMs,
|
|
270
|
+
fps: params.fps,
|
|
271
|
+
captions: params.captions,
|
|
272
|
+
style: params.style,
|
|
368
273
|
},
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
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
|
-
};
|
|
274
|
+
output_type: 'video',
|
|
275
|
+
video_codec: 'h264',
|
|
276
|
+
})
|
|
277
|
+
});
|
|
278
|
+
const text = await response.text();
|
|
279
|
+
let data;
|
|
280
|
+
try {
|
|
281
|
+
data = JSON.parse(text);
|
|
410
282
|
}
|
|
411
|
-
catch
|
|
412
|
-
|
|
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
|
-
};
|
|
283
|
+
catch {
|
|
284
|
+
return { ok: false, code: response.status || 500, message: `Modal endpoint error: ${text.substring(0, 100)}` };
|
|
419
285
|
}
|
|
286
|
+
if (data.status === 'error' || !response.ok) {
|
|
287
|
+
return { ok: false, code: response.status, message: data.error ?? data.message ?? 'Failed to submit auto-caption job' };
|
|
288
|
+
}
|
|
289
|
+
return { ok: true, code: 200, message: "Auto-caption job submitted", data };
|
|
420
290
|
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
291
|
+
catch (error) {
|
|
292
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
293
|
+
return { ok: false, code: 500, message: `Failed to submit auto-caption job: ${errorMessage}` };
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Submit an Instagram DM render job to the Modal renderer.
|
|
298
|
+
* Renders a fake Instagram DM conversation image.
|
|
299
|
+
*/
|
|
300
|
+
async function submitInstagramDmRenderJob(params) {
|
|
301
|
+
try {
|
|
302
|
+
const transformedMessages = transformToInstagramMessages(params.messages, params.imageAttachmentUrl);
|
|
303
|
+
const hasStoryReply = params.messages.some(msg => msg.hasImage);
|
|
304
|
+
const response = await fetch(RENDER_SUBMIT_URL, {
|
|
305
|
+
method: 'POST',
|
|
306
|
+
headers: { 'Content-Type': 'application/json' },
|
|
307
|
+
body: JSON.stringify({
|
|
308
|
+
config: {
|
|
309
|
+
_compositionType: 'instagram-dm',
|
|
310
|
+
input: {
|
|
311
|
+
receiverUsername: params.receiverUsername,
|
|
312
|
+
receiverProfilePicUrl: params.receiverProfilePicUrl,
|
|
313
|
+
theme: params.theme,
|
|
314
|
+
messages: transformedMessages,
|
|
315
|
+
storyImageUrl: hasStoryReply ? params.imageAttachmentUrl : undefined,
|
|
445
316
|
},
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
})
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
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
|
-
};
|
|
317
|
+
},
|
|
318
|
+
output_type: 'image',
|
|
319
|
+
image_format: 'png',
|
|
320
|
+
})
|
|
321
|
+
});
|
|
322
|
+
const text = await response.text();
|
|
323
|
+
let data;
|
|
324
|
+
try {
|
|
325
|
+
data = JSON.parse(text);
|
|
476
326
|
}
|
|
477
|
-
catch
|
|
478
|
-
|
|
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
|
-
};
|
|
327
|
+
catch {
|
|
328
|
+
return { ok: false, code: response.status || 500, message: `Modal endpoint error: ${text.substring(0, 100)}` };
|
|
485
329
|
}
|
|
330
|
+
if (data.status === 'error' || !response.ok) {
|
|
331
|
+
return { ok: false, code: response.status, message: data.error ?? data.message ?? 'Failed to submit Instagram DM job' };
|
|
332
|
+
}
|
|
333
|
+
return { ok: true, code: 200, message: "Instagram DM job submitted", data };
|
|
486
334
|
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
335
|
+
catch (error) {
|
|
336
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
337
|
+
return { ok: false, code: 500, message: `Failed to submit Instagram DM job: ${errorMessage}` };
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Submit an iMessage DM render job to the Modal renderer.
|
|
342
|
+
* Renders a fake iMessage conversation image.
|
|
343
|
+
*/
|
|
344
|
+
async function submitIMessageDmRenderJob(params) {
|
|
345
|
+
try {
|
|
346
|
+
const transformedMessages = transformToIMessageMessages(params.messages, params.imageAttachmentUrl);
|
|
347
|
+
const response = await fetch(RENDER_SUBMIT_URL, {
|
|
348
|
+
method: 'POST',
|
|
349
|
+
headers: { 'Content-Type': 'application/json' },
|
|
350
|
+
body: JSON.stringify({
|
|
351
|
+
config: {
|
|
352
|
+
_compositionType: 'imessage-dm',
|
|
353
|
+
input: {
|
|
354
|
+
username: params.username,
|
|
355
|
+
profilePicUrl: params.profilePicUrl,
|
|
356
|
+
theme: params.theme,
|
|
357
|
+
time: params.time,
|
|
358
|
+
messages: transformedMessages,
|
|
359
|
+
senderBubbleColor: params.senderBubbleColor,
|
|
360
|
+
showReadReceipt: params.showReadReceipt,
|
|
361
|
+
readReceiptText: params.readReceiptText,
|
|
362
|
+
unreadBadgeText: params.unreadBadgeText,
|
|
363
|
+
messageHeaderTimestampText: params.messageHeaderTimestampText,
|
|
515
364
|
},
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
})
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
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
|
-
};
|
|
365
|
+
},
|
|
366
|
+
output_type: 'image',
|
|
367
|
+
image_format: 'png',
|
|
368
|
+
})
|
|
369
|
+
});
|
|
370
|
+
const text = await response.text();
|
|
371
|
+
let data;
|
|
372
|
+
try {
|
|
373
|
+
data = JSON.parse(text);
|
|
374
|
+
}
|
|
375
|
+
catch {
|
|
376
|
+
return { ok: false, code: response.status || 500, message: `Modal endpoint error: ${text.substring(0, 100)}` };
|
|
546
377
|
}
|
|
547
|
-
|
|
548
|
-
|
|
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
|
-
};
|
|
378
|
+
if (data.status === 'error' || !response.ok) {
|
|
379
|
+
return { ok: false, code: response.status, message: data.error ?? data.message ?? 'Failed to submit iMessage DM job' };
|
|
555
380
|
}
|
|
381
|
+
return { ok: true, code: 200, message: "iMessage DM job submitted", data };
|
|
382
|
+
}
|
|
383
|
+
catch (error) {
|
|
384
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
385
|
+
return { ok: false, code: 500, message: `Failed to submit iMessage DM job: ${errorMessage}` };
|
|
556
386
|
}
|
|
557
387
|
}
|
|
558
|
-
exports.RenderClient = RenderClient;
|