ugcinc 4.5.19 → 4.5.21

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.
@@ -45,11 +45,15 @@ interface VideoModelOption {
45
45
  /** Reference-to-video variant, if one exists */
46
46
  refId?: VideoGenerationReferenceModel;
47
47
  }
48
+ /** A single media item in a selection pool */
49
+ interface VideoMediaItem {
50
+ id: string;
51
+ url: string;
52
+ }
48
53
  /** Static or variable reference image input */
49
54
  interface ReferenceImageInput {
50
55
  isVariable: boolean;
51
- mediaId: string | null;
52
- mediaUrl: string | null;
56
+ mediaItems: VideoMediaItem[];
53
57
  }
54
58
  declare const ALL_VIDEO_MODELS: VideoModelOption[];
55
59
  /** @deprecated Use getImageToVideoModelId instead — I2V mode is auto-detected from image input */
@@ -76,16 +80,13 @@ declare const definition: import("./types").NodeDefinition<"generate-video", "ge
76
80
  duration: number;
77
81
  prompt: string;
78
82
  promptIsVariable: boolean;
79
- imageIsVariable: boolean;
80
- imageMediaId: string | null;
81
- imageMediaUrl: string | null;
82
83
  inputMode: VideoInputMode;
84
+ imageIsVariable: boolean;
85
+ imageMediaItems: VideoMediaItem[];
83
86
  firstFrameIsVariable: boolean;
84
- firstFrameMediaId: string | null;
85
- firstFrameMediaUrl: string | null;
87
+ firstFrameMediaItems: VideoMediaItem[];
86
88
  lastFrameIsVariable: boolean;
87
- lastFrameMediaId: string | null;
88
- lastFrameMediaUrl: string | null;
89
+ lastFrameMediaItems: VideoMediaItem[];
89
90
  referenceImages: ReferenceImageInput[];
90
91
  outputMode: OutputMode;
91
92
  selectionMode: SelectionMode | null;
@@ -96,5 +97,5 @@ declare const _default: typeof definition & {
96
97
  };
97
98
  export default _default;
98
99
  export { isImageToVideoModel, getImageToVideoModelId, hasImageToVideoVariant, getFirstLastFrameModelId, hasFirstLastFrameVariant, getReferenceModelId, hasReferenceVariant, getAvailableInputModes, ALL_VIDEO_MODELS, getModelAspectRatios, getModelDurations, };
99
- export type { VideoGenerationTextToVideoModel, VideoGenerationImageToVideoModel, VideoGenerationFirstLastFrameModel, VideoGenerationReferenceModel, VideoGenerationModel, VideoInputMode, VideoModelProvider, VideoModelTier, VideoAspectRatioOption, VideoDurationOption, VideoModelOption, ReferenceImageInput, };
100
+ export type { VideoGenerationTextToVideoModel, VideoGenerationImageToVideoModel, VideoGenerationFirstLastFrameModel, VideoGenerationReferenceModel, VideoGenerationModel, VideoInputMode, VideoMediaItem, VideoModelProvider, VideoModelTier, VideoAspectRatioOption, VideoDurationOption, VideoModelOption, ReferenceImageInput, };
100
101
  export type GenerateVideoNodeConfig = typeof definition.defaults;
@@ -127,17 +127,15 @@ const definition = (0, types_1.defineNode)({
127
127
  duration: 5,
128
128
  prompt: '',
129
129
  promptIsVariable: false,
130
- imageIsVariable: false,
131
- imageMediaId: null,
132
- imageMediaUrl: null,
133
130
  inputMode: 'none',
131
+ // Image-to-video
132
+ imageIsVariable: false,
133
+ imageMediaItems: [],
134
134
  // First-last-frame
135
135
  firstFrameIsVariable: false,
136
- firstFrameMediaId: null,
137
- firstFrameMediaUrl: null,
136
+ firstFrameMediaItems: [],
138
137
  lastFrameIsVariable: false,
139
- lastFrameMediaId: null,
140
- lastFrameMediaUrl: null,
138
+ lastFrameMediaItems: [],
141
139
  // Reference
142
140
  referenceImages: [],
143
141
  outputMode: 'per-input',
@@ -175,16 +175,13 @@ export declare const nodeDefinitions: {
175
175
  duration: number;
176
176
  prompt: string;
177
177
  promptIsVariable: boolean;
178
- imageIsVariable: boolean;
179
- imageMediaId: string | null;
180
- imageMediaUrl: string | null;
181
178
  inputMode: import("./generate-video").VideoInputMode;
179
+ imageIsVariable: boolean;
180
+ imageMediaItems: import("./generate-video").VideoMediaItem[];
182
181
  firstFrameIsVariable: boolean;
183
- firstFrameMediaId: string | null;
184
- firstFrameMediaUrl: string | null;
182
+ firstFrameMediaItems: import("./generate-video").VideoMediaItem[];
185
183
  lastFrameIsVariable: boolean;
186
- lastFrameMediaId: string | null;
187
- lastFrameMediaUrl: string | null;
184
+ lastFrameMediaItems: import("./generate-video").VideoMediaItem[];
188
185
  referenceImages: import("./generate-video").ReferenceImageInput[];
189
186
  outputMode: import("./types").OutputMode;
190
187
  selectionMode: import("./types").SelectionMode | null;
@@ -37,7 +37,8 @@ const definition = (0, types_1.defineNode)({
37
37
  channel.segments.forEach(segment => {
38
38
  // inputRef exists on all segments except 'text'
39
39
  if (segment.type !== 'text') {
40
- if (segment.inputRef && !addedIds.has(segment.inputRef)) {
40
+ const isVar = !('isVariable' in segment) || segment.isVariable !== false;
41
+ if (segment.inputRef && !addedIds.has(segment.inputRef) && isVar) {
41
42
  addedIds.add(segment.inputRef);
42
43
  const inputType = segment.type === 'video' || segment.type === 'video-sequence' ? 'video'
43
44
  : segment.type === 'audio' ? 'audio'
@@ -52,7 +53,7 @@ const definition = (0, types_1.defineNode)({
52
53
  }
53
54
  // textInputRef only exists on 'text' segments
54
55
  if (segment.type === 'text') {
55
- if (segment.textInputRef && !addedIds.has(segment.textInputRef)) {
56
+ if (segment.textInputRef && !addedIds.has(segment.textInputRef) && segment.textIsVariable !== false) {
56
57
  addedIds.add(segment.textInputRef);
57
58
  inputs.push({
58
59
  id: segment.textInputRef,
@@ -87,8 +88,13 @@ const definition = (0, types_1.defineNode)({
87
88
  for (const channel of editorConfig.channels) {
88
89
  for (const segment of channel.segments) {
89
90
  if (segment.type !== 'text') {
90
- // Media segments with inputRef
91
- if (segment.inputRef) {
91
+ // Media segments - static or variable
92
+ if ('isVariable' in segment && segment.isVariable === false) {
93
+ const url = segment.staticMedia?.[0]?.url;
94
+ if (url)
95
+ sources[segment.id] = url;
96
+ }
97
+ else if (segment.inputRef) {
92
98
  const url = editorConfig.previewUrls?.[segment.inputRef];
93
99
  if (url) {
94
100
  sources[segment.id] = url;
@@ -96,8 +102,12 @@ const definition = (0, types_1.defineNode)({
96
102
  }
97
103
  }
98
104
  else {
99
- // Text segments with textInputRef
100
- if (segment.textInputRef) {
105
+ // Text segments - static or variable
106
+ if (segment.textIsVariable === false) {
107
+ if (segment.text)
108
+ textContent[segment.id] = segment.text;
109
+ }
110
+ else if (segment.textInputRef) {
101
111
  const text = editorConfig.previewTextValues?.[segment.textInputRef];
102
112
  if (text) {
103
113
  textContent[segment.id] = text;
@@ -173,82 +183,109 @@ function transformSegment(segment, inputs) {
173
183
  let result = { ...segment };
174
184
  // Handle inputRef for media segments
175
185
  if ('inputRef' in segment && segment.inputRef) {
176
- const inputKey = segment.inputRef;
177
- const value = inputs[inputKey];
178
- if (value === undefined || value === null) {
179
- if (segment.required === false) {
180
- return null; // Optional segment with missing input
181
- }
182
- throw new Error(`Required segment input "${inputKey}" was not provided`);
183
- }
184
- // Image sequence segments expect array of media values
185
- if (segment.type === 'image-sequence') {
186
- if (!Array.isArray(value)) {
187
- throw new Error(`Image sequence expects ImageValue[] for "${inputKey}", got ${typeof value}`);
188
- }
189
- let items = value.map(v => v.url);
190
- if (segment.itemCountMode === 'static' && segment.staticItemCount !== undefined) {
191
- items = items.slice(0, segment.staticItemCount);
192
- }
193
- const keysToOmit = ['inputRef', 'timeMode', 'itemCountMode', 'staticItemCount'];
194
- const transformed = {
195
- ...omit(segment, [...keysToOmit]),
196
- type: 'image-sequence',
197
- source: '',
198
- items,
199
- };
200
- result = transformed;
201
- }
202
- else if (segment.type === 'video-sequence') {
203
- if (!Array.isArray(value)) {
204
- throw new Error(`Video sequence expects VideoValue[] for "${inputKey}", got ${typeof value}`);
205
- }
206
- let items = value.map(v => v.url);
207
- if (segment.itemCountMode === 'static' && segment.staticItemCount !== undefined) {
208
- items = items.slice(0, segment.staticItemCount);
186
+ if ('isVariable' in segment && segment.isVariable === false) {
187
+ // Static: pick random from staticMedia
188
+ const items = segment.staticMedia;
189
+ if (items?.length) {
190
+ const pick = items[Math.floor(Math.random() * items.length)];
191
+ if (pick?.url) {
192
+ const keysToOmit = ['inputRef', 'timeMode', 'isVariable', 'staticMedia'];
193
+ if (segment.type === 'video') {
194
+ result = { ...omit(segment, [...keysToOmit]), type: 'video', source: pick.url };
195
+ }
196
+ else if (segment.type === 'audio') {
197
+ result = { ...omit(segment, [...keysToOmit]), type: 'audio', source: pick.url };
198
+ }
199
+ else if (segment.type === 'image') {
200
+ result = { ...omit(segment, [...keysToOmit]), type: 'image', source: pick.url };
201
+ }
202
+ }
209
203
  }
210
- const keysToOmit = ['inputRef', 'timeMode', 'itemCountMode', 'staticItemCount'];
211
- const transformed = {
212
- ...omit(segment, [...keysToOmit]),
213
- type: 'video-sequence',
214
- source: '',
215
- items,
216
- };
217
- result = transformed;
218
204
  }
219
205
  else {
220
- // Single media segment (image/video/audio) - extract .url from value object
221
- const mediaValue = value;
222
- if (!mediaValue || typeof mediaValue !== 'object' || typeof mediaValue.url !== 'string') {
223
- throw new Error(`Segment expects media value with url for "${inputKey}", got ${typeof value}`);
206
+ const inputKey = segment.inputRef;
207
+ const value = inputs[inputKey];
208
+ if (value === undefined || value === null) {
209
+ if (segment.required === false) {
210
+ return null; // Optional segment with missing input
211
+ }
212
+ throw new Error(`Required segment input "${inputKey}" was not provided`);
224
213
  }
225
- const source = mediaValue.url;
226
- const keysToOmit = ['inputRef', 'timeMode'];
227
- if (segment.type === 'video') {
228
- result = { ...omit(segment, [...keysToOmit]), type: 'video', source };
214
+ // Image sequence segments expect array of media values
215
+ if (segment.type === 'image-sequence') {
216
+ if (!Array.isArray(value)) {
217
+ throw new Error(`Image sequence expects ImageValue[] for "${inputKey}", got ${typeof value}`);
218
+ }
219
+ let items = value.map(v => v.url);
220
+ if (segment.itemCountMode === 'static' && segment.staticItemCount !== undefined) {
221
+ items = items.slice(0, segment.staticItemCount);
222
+ }
223
+ const keysToOmit = ['inputRef', 'timeMode', 'itemCountMode', 'staticItemCount'];
224
+ const transformed = {
225
+ ...omit(segment, [...keysToOmit]),
226
+ type: 'image-sequence',
227
+ source: '',
228
+ items,
229
+ };
230
+ result = transformed;
229
231
  }
230
- else if (segment.type === 'audio') {
231
- result = { ...omit(segment, [...keysToOmit]), type: 'audio', source };
232
+ else if (segment.type === 'video-sequence') {
233
+ if (!Array.isArray(value)) {
234
+ throw new Error(`Video sequence expects VideoValue[] for "${inputKey}", got ${typeof value}`);
235
+ }
236
+ let items = value.map(v => v.url);
237
+ if (segment.itemCountMode === 'static' && segment.staticItemCount !== undefined) {
238
+ items = items.slice(0, segment.staticItemCount);
239
+ }
240
+ const keysToOmit = ['inputRef', 'timeMode', 'itemCountMode', 'staticItemCount'];
241
+ const transformed = {
242
+ ...omit(segment, [...keysToOmit]),
243
+ type: 'video-sequence',
244
+ source: '',
245
+ items,
246
+ };
247
+ result = transformed;
232
248
  }
233
- else if (segment.type === 'image') {
234
- result = { ...omit(segment, [...keysToOmit]), type: 'image', source };
249
+ else {
250
+ // Single media segment (image/video/audio) - extract .url from value object
251
+ const mediaValue = value;
252
+ if (!mediaValue || typeof mediaValue !== 'object' || typeof mediaValue.url !== 'string') {
253
+ throw new Error(`Segment expects media value with url for "${inputKey}", got ${typeof value}`);
254
+ }
255
+ const source = mediaValue.url;
256
+ const keysToOmit = ['inputRef', 'timeMode'];
257
+ if (segment.type === 'video') {
258
+ result = { ...omit(segment, [...keysToOmit]), type: 'video', source };
259
+ }
260
+ else if (segment.type === 'audio') {
261
+ result = { ...omit(segment, [...keysToOmit]), type: 'audio', source };
262
+ }
263
+ else if (segment.type === 'image') {
264
+ result = { ...omit(segment, [...keysToOmit]), type: 'image', source };
265
+ }
235
266
  }
236
267
  }
237
268
  }
238
269
  // Handle textInputRef for text segments
239
- if (segment.type === 'text' && segment.textInputRef) {
240
- const inputKey = segment.textInputRef;
241
- const value = inputs[inputKey];
242
- if (value === undefined || value === null) {
243
- if (segment.required === false) {
244
- return null;
245
- }
246
- throw new Error(`Required text segment input "${inputKey}" was not provided`);
270
+ if (segment.type === 'text') {
271
+ if (segment.textIsVariable === false) {
272
+ // Static text: use segment.text directly
273
+ result = { ...omit(segment, ['textInputRef', 'timeMode', 'textIsVariable']), type: 'text', source: '', text: segment.text ?? '' };
247
274
  }
248
- if (typeof value !== 'string') {
249
- throw new Error(`Text segment expects string for "${inputKey}", got ${typeof value}`);
275
+ else if (segment.textInputRef) {
276
+ const inputKey = segment.textInputRef;
277
+ const value = inputs[inputKey];
278
+ if (value === undefined || value === null) {
279
+ if (segment.required === false) {
280
+ return null;
281
+ }
282
+ throw new Error(`Required text segment input "${inputKey}" was not provided`);
283
+ }
284
+ if (typeof value !== 'string') {
285
+ throw new Error(`Text segment expects string for "${inputKey}", got ${typeof value}`);
286
+ }
287
+ result = { ...omit(segment, ['textInputRef', 'timeMode']), type: 'text', source: '', text: value };
250
288
  }
251
- result = { ...omit(segment, ['textInputRef', 'timeMode']), type: 'text', source: '', text: value };
252
289
  }
253
290
  // Handle trim inputRefs
254
291
  if (segment.startTrimInputRef) {
@@ -274,7 +311,7 @@ function transformSegment(segment, inputs) {
274
311
  }
275
312
  }
276
313
  // Remove UI-only fields
277
- const uiOnlyKeys = ['timeMode', 'startTrimInputRef', 'endTrimInputRef'];
314
+ const uiOnlyKeys = ['timeMode', 'startTrimInputRef', 'endTrimInputRef', 'isVariable', 'staticMedia', 'textIsVariable'];
278
315
  const resultWithUiFields = result;
279
316
  for (const key of uiOnlyKeys) {
280
317
  if (key in resultWithUiFields) {
@@ -11,7 +11,7 @@
11
11
  * - previewUrls/previewTextValues: Only for UI preview
12
12
  */
13
13
  import type { FitMode, BorderRadiusConfig, TimeValue, TextStyleProperties } from './base';
14
- import type { DimensionPresetKey } from './element';
14
+ import type { DimensionPresetKey, StaticMediaItem } from './element';
15
15
  /**
16
16
  * UI helper for timing mode - not sent to renderer
17
17
  */
@@ -71,6 +71,10 @@ export interface VideoEditorVideoSegment extends VideoEditorVisualSegment {
71
71
  inputRef?: string;
72
72
  /** Actual source URL (set after inputRef is resolved) */
73
73
  source?: string;
74
+ /** UI-only: whether this segment uses a variable input (default: true for backwards compat) */
75
+ isVariable?: boolean;
76
+ /** UI-only: static media items when isVariable is false */
77
+ staticMedia?: StaticMediaItem[];
74
78
  fit?: FitMode;
75
79
  speed?: number;
76
80
  volume?: number;
@@ -85,6 +89,10 @@ export interface VideoEditorAudioSegment extends VideoEditorBaseSegment {
85
89
  inputRef?: string;
86
90
  /** Actual source URL (set after inputRef is resolved) */
87
91
  source?: string;
92
+ /** UI-only: whether this segment uses a variable input (default: true for backwards compat) */
93
+ isVariable?: boolean;
94
+ /** UI-only: static media items when isVariable is false */
95
+ staticMedia?: StaticMediaItem[];
88
96
  volume?: number;
89
97
  }
90
98
  /**
@@ -96,6 +104,10 @@ export interface VideoEditorImageSegment extends VideoEditorVisualSegment {
96
104
  inputRef?: string;
97
105
  /** Actual source URL (set after inputRef is resolved) */
98
106
  source?: string;
107
+ /** UI-only: whether this segment uses a variable input (default: true for backwards compat) */
108
+ isVariable?: boolean;
109
+ /** UI-only: static media items when isVariable is false */
110
+ staticMedia?: StaticMediaItem[];
99
111
  fit?: FitMode;
100
112
  loop?: boolean;
101
113
  borderRadius?: number | BorderRadiusConfig;
@@ -112,6 +124,8 @@ export interface VideoEditorTextSegment extends VideoEditorVisualSegment, TextSt
112
124
  textInputRef?: string;
113
125
  /** Actual text content (set after textInputRef is resolved) */
114
126
  text?: string;
127
+ /** UI-only: whether this segment uses a variable text input (default: true for backwards compat) */
128
+ textIsVariable?: boolean;
115
129
  }
116
130
  /**
117
131
  * Image sequence segment (UI) - plays an array of images sequentially
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ugcinc",
3
- "version": "4.5.19",
3
+ "version": "4.5.21",
4
4
  "description": "TypeScript/JavaScript client for the UGC Inc API",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",