ugcinc 4.5.89 → 4.5.91

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/README.md CHANGED
@@ -87,7 +87,6 @@ For the full endpoint reference, all method signatures, data structures, and wor
87
87
 
88
88
  - Docs: https://docs.ugc.inc
89
89
  - Website: https://ugc.inc
90
- - npm package: https://www.npmjs.com/package/ugcinc
91
90
 
92
91
  ## License
93
92
 
@@ -33,18 +33,23 @@ const definition = (0, types_1.defineNode)({
33
33
  const editorConfig = config?.videoEditor;
34
34
  const inputs = [];
35
35
  const addedIds = new Set();
36
+ const addInputPort = (port) => {
37
+ if (addedIds.has(port.id))
38
+ return;
39
+ addedIds.add(port.id);
40
+ inputs.push(port);
41
+ };
36
42
  if (editorConfig) {
37
43
  editorConfig.channels.forEach(channel => {
38
44
  channel.segments.forEach(segment => {
39
45
  // inputRef exists on all segments except 'text'
40
46
  if (segment.type !== 'text') {
41
47
  const isVar = !('isVariable' in segment) || segment.isVariable !== false;
42
- if (segment.inputRef && !addedIds.has(segment.inputRef) && isVar) {
43
- addedIds.add(segment.inputRef);
48
+ if (segment.inputRef && isVar) {
44
49
  const inputType = segment.type === 'video' || segment.type === 'video-sequence' ? 'video'
45
50
  : segment.type === 'audio' ? ['audio', 'social_audio']
46
51
  : 'image';
47
- inputs.push({
52
+ addInputPort({
48
53
  id: segment.inputRef,
49
54
  type: inputType,
50
55
  isArray: segment.type === 'image-sequence' || segment.type === 'video-sequence',
@@ -54,9 +59,8 @@ const definition = (0, types_1.defineNode)({
54
59
  }
55
60
  // textInputRef only exists on 'text' segments
56
61
  if (segment.type === 'text') {
57
- if (segment.textInputRef && !addedIds.has(segment.textInputRef) && segment.textIsVariable !== false) {
58
- addedIds.add(segment.textInputRef);
59
- inputs.push({
62
+ if (segment.textInputRef && segment.textIsVariable !== false) {
63
+ addInputPort({
60
64
  id: segment.textInputRef,
61
65
  type: 'text',
62
66
  isArray: false,
@@ -64,6 +68,22 @@ const definition = (0, types_1.defineNode)({
64
68
  });
65
69
  }
66
70
  }
71
+ if (segment.startTrimInputRef) {
72
+ addInputPort({
73
+ id: segment.startTrimInputRef,
74
+ type: 'number',
75
+ isArray: false,
76
+ required: false,
77
+ });
78
+ }
79
+ if (segment.endTrimInputRef) {
80
+ addInputPort({
81
+ id: segment.endTrimInputRef,
82
+ type: 'number',
83
+ isArray: false,
84
+ required: false,
85
+ });
86
+ }
67
87
  });
68
88
  });
69
89
  }
@@ -152,6 +172,28 @@ function omit(obj, keys) {
152
172
  }
153
173
  return result;
154
174
  }
175
+ function getVideoSourceDurationMs({ inputValueDuration, staticMediaDuration, }) {
176
+ const duration = inputValueDuration ?? staticMediaDuration ?? undefined;
177
+ return typeof duration === 'number' && duration > 0 ? duration : undefined;
178
+ }
179
+ function getTrimmedVideoWindowMs({ sourceDurationMs, startTrim, endTrim, }) {
180
+ const safeStartTrim = Math.max(0, startTrim ?? 0);
181
+ const safeEndTrim = typeof endTrim === 'number' && endTrim > safeStartTrim
182
+ ? Math.min(endTrim, sourceDurationMs)
183
+ : sourceDurationMs;
184
+ return Math.max(1, safeEndTrim - safeStartTrim);
185
+ }
186
+ function getFitSegmentSpeed({ sourceDurationMs, targetDurationMs, startTrim, endTrim, }) {
187
+ if (!sourceDurationMs || !targetDurationMs || targetDurationMs <= 0) {
188
+ return undefined;
189
+ }
190
+ const sourceWindowMs = getTrimmedVideoWindowMs({
191
+ sourceDurationMs,
192
+ startTrim,
193
+ endTrim,
194
+ });
195
+ return Math.max(0.01, sourceWindowMs / targetDurationMs);
196
+ }
155
197
  /**
156
198
  * Ensure all segments have explicit zIndex values for proper layering in the renderer.
157
199
  */
@@ -192,7 +234,25 @@ function transformSegment(segment, inputs) {
192
234
  if (pick?.url) {
193
235
  const keysToOmit = ['inputRef', 'timeMode', 'isVariable', 'staticMedia'];
194
236
  if (segment.type === 'video') {
195
- result = { ...omit(segment, [...keysToOmit]), type: 'video', source: pick.url };
237
+ const sourceDurationMs = getVideoSourceDurationMs({
238
+ staticMediaDuration: pick.duration,
239
+ });
240
+ const fittedSpeed = segment.speedMode === 'fit-segment'
241
+ ? getFitSegmentSpeed({
242
+ sourceDurationMs,
243
+ targetDurationMs: segment.duration?.type === 'absolute' ? segment.duration.value : undefined,
244
+ startTrim: segment.startTrim,
245
+ endTrim: segment.endTrim,
246
+ })
247
+ : undefined;
248
+ result = {
249
+ ...omit(segment, [...keysToOmit]),
250
+ type: 'video',
251
+ source: pick.url,
252
+ sourceDurationMs,
253
+ speed: fittedSpeed ?? segment.speed,
254
+ loop: segment.speedMode === 'fit-segment' ? false : segment.loop,
255
+ };
196
256
  }
197
257
  else if (segment.type === 'audio') {
198
258
  result = { ...omit(segment, [...keysToOmit]), type: 'audio', source: pick.url };
@@ -256,7 +316,25 @@ function transformSegment(segment, inputs) {
256
316
  const source = mediaValue.url;
257
317
  const keysToOmit = ['inputRef', 'timeMode'];
258
318
  if (segment.type === 'video') {
259
- result = { ...omit(segment, [...keysToOmit]), type: 'video', source };
319
+ const sourceDurationMs = getVideoSourceDurationMs({
320
+ inputValueDuration: 'duration' in mediaValue ? mediaValue.duration : undefined,
321
+ });
322
+ const fittedSpeed = segment.speedMode === 'fit-segment'
323
+ ? getFitSegmentSpeed({
324
+ sourceDurationMs,
325
+ targetDurationMs: segment.duration?.type === 'absolute' ? segment.duration.value : undefined,
326
+ startTrim: segment.startTrim,
327
+ endTrim: segment.endTrim,
328
+ })
329
+ : undefined;
330
+ result = {
331
+ ...omit(segment, [...keysToOmit]),
332
+ type: 'video',
333
+ source,
334
+ sourceDurationMs,
335
+ speed: fittedSpeed ?? segment.speed,
336
+ loop: segment.speedMode === 'fit-segment' ? false : segment.loop,
337
+ };
260
338
  }
261
339
  else if (segment.type === 'audio') {
262
340
  result = { ...omit(segment, [...keysToOmit]), type: 'audio', source };
@@ -312,7 +390,7 @@ function transformSegment(segment, inputs) {
312
390
  }
313
391
  }
314
392
  // Remove UI-only fields
315
- const uiOnlyKeys = ['timeMode', 'startTrimInputRef', 'endTrimInputRef', 'isVariable', 'staticMedia', 'textIsVariable'];
393
+ const uiOnlyKeys = ['timeMode', 'startTrimInputRef', 'endTrimInputRef', 'isVariable', 'staticMedia', 'textIsVariable', 'speedMode'];
316
394
  const resultWithUiFields = result;
317
395
  for (const key of uiOnlyKeys) {
318
396
  if (key in resultWithUiFields) {
@@ -16,6 +16,7 @@ export interface StaticMediaItem {
16
16
  name: string | null;
17
17
  url: string | null;
18
18
  type: string | null;
19
+ duration?: number | null;
19
20
  }
20
21
  /**
21
22
  * Dimension preset keys for image editor
@@ -75,9 +75,14 @@ export interface VideoEditorVideoSegment extends VideoEditorVisualSegment {
75
75
  isVariable?: boolean;
76
76
  /** UI-only: static media items when isVariable is false */
77
77
  staticMedia?: StaticMediaItem[];
78
+ /** UI-only: how playback speed should be resolved */
79
+ speedMode?: 'manual' | 'fit-segment';
78
80
  fit?: FitMode;
79
81
  speed?: number;
82
+ /** Natural duration of the source video in milliseconds */
83
+ sourceDurationMs?: number;
80
84
  volume?: number;
85
+ loop?: boolean;
81
86
  borderRadius?: number | BorderRadiusConfig;
82
87
  }
83
88
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ugcinc",
3
- "version": "4.5.89",
3
+ "version": "4.5.91",
4
4
  "description": "TypeScript/JavaScript client for the UGC Inc API",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -31,9 +31,14 @@
31
31
  "ugcinc",
32
32
  "api",
33
33
  "client",
34
+ "ugc",
34
35
  "tiktok",
36
+ "instagram",
37
+ "clawbot",
35
38
  "automation",
39
+ "social-automation",
36
40
  "social-media",
41
+ "social-publishing",
37
42
  "content-management",
38
43
  "video-rendering"
39
44
  ],