ugcinc-render 1.3.14 → 1.4.0
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.mts +72 -1
- package/dist/index.d.ts +72 -1
- package/dist/index.js +117 -0
- package/dist/index.mjs +106 -0
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -457,6 +457,15 @@ interface VideoEditorNodeConfig {
|
|
|
457
457
|
previewUrls?: Record<string, string>;
|
|
458
458
|
previewTextValues?: Record<string, string>;
|
|
459
459
|
}
|
|
460
|
+
/**
|
|
461
|
+
* Segment position on timeline (calculated from segment timing properties)
|
|
462
|
+
*/
|
|
463
|
+
interface SegmentTimelinePosition {
|
|
464
|
+
/** Start time in milliseconds */
|
|
465
|
+
startMs: number;
|
|
466
|
+
/** Duration in milliseconds */
|
|
467
|
+
durationMs: number;
|
|
468
|
+
}
|
|
460
469
|
|
|
461
470
|
/**
|
|
462
471
|
* Segment types for the rendering system
|
|
@@ -1165,6 +1174,68 @@ declare function calculateCropBounds(elements: ImageEditorElement[], dynamicCrop
|
|
|
1165
1174
|
*/
|
|
1166
1175
|
declare function isDynamicCropEnabled(dynamicCrop: DynamicCropConfig | undefined): boolean;
|
|
1167
1176
|
|
|
1177
|
+
/**
|
|
1178
|
+
* Timeline utility functions for video editor
|
|
1179
|
+
*
|
|
1180
|
+
* These functions calculate segment positions on the timeline based on
|
|
1181
|
+
* the segment/overlay timing model. Used by both the webapp UI and
|
|
1182
|
+
* the VideoEditorComposition for rendering.
|
|
1183
|
+
*/
|
|
1184
|
+
|
|
1185
|
+
/**
|
|
1186
|
+
* Create a default TimeValue for segment offsets
|
|
1187
|
+
*/
|
|
1188
|
+
declare function defaultOffset(mode?: TimeMode): TimeValue;
|
|
1189
|
+
/**
|
|
1190
|
+
* Get base segments (no parentId) for a channel
|
|
1191
|
+
* Base segments are the primary timeline elements that overlays attach to
|
|
1192
|
+
*/
|
|
1193
|
+
declare function getBaseSegments(channel: VideoEditorChannel): VideoEditorSegment[];
|
|
1194
|
+
/**
|
|
1195
|
+
* Get overlays for a specific parent segment (or global overlays if parentId is null)
|
|
1196
|
+
*/
|
|
1197
|
+
declare function getOverlays(channel: VideoEditorChannel, parentId: string | null): VideoEditorSegment[];
|
|
1198
|
+
/**
|
|
1199
|
+
* Calculate segment position on timeline
|
|
1200
|
+
*
|
|
1201
|
+
* For base segments:
|
|
1202
|
+
* - Position is calculated from offset and previous segments
|
|
1203
|
+
* - Duration is from the duration property or default (5 seconds)
|
|
1204
|
+
*
|
|
1205
|
+
* For overlay segments:
|
|
1206
|
+
* - Position is calculated relative to parent using relativeStart/relativeEnd
|
|
1207
|
+
* - relativeStart/relativeEnd are fractions (0-1) of parent duration
|
|
1208
|
+
*/
|
|
1209
|
+
declare function getSegmentTimelinePosition(segment: VideoEditorSegment, baseSegments: VideoEditorSegment[], channel: VideoEditorChannel): SegmentTimelinePosition;
|
|
1210
|
+
/**
|
|
1211
|
+
* Check if a segment is visible at a given time
|
|
1212
|
+
*/
|
|
1213
|
+
declare function isSegmentVisibleAtTime(segment: VideoEditorSegment, time: number, channel: VideoEditorChannel): boolean;
|
|
1214
|
+
/**
|
|
1215
|
+
* Calculate estimated total duration based on segments
|
|
1216
|
+
*/
|
|
1217
|
+
declare function calculateEstimatedDuration(channels: VideoEditorChannel[]): number;
|
|
1218
|
+
/**
|
|
1219
|
+
* Calculate the timeline content end time (used for both ruler and scroll width)
|
|
1220
|
+
*/
|
|
1221
|
+
declare function calculateTimelineContentEnd(channel: VideoEditorChannel): number;
|
|
1222
|
+
/**
|
|
1223
|
+
* Format time in mm:ss.ms
|
|
1224
|
+
*/
|
|
1225
|
+
declare function formatTime(ms: number): string;
|
|
1226
|
+
/**
|
|
1227
|
+
* Parse time string to milliseconds
|
|
1228
|
+
*/
|
|
1229
|
+
declare function parseTime(timeStr: string): number;
|
|
1230
|
+
/**
|
|
1231
|
+
* Generate a unique segment ID
|
|
1232
|
+
*/
|
|
1233
|
+
declare function generateSegmentId(): string;
|
|
1234
|
+
/**
|
|
1235
|
+
* Generate a unique overlay ID
|
|
1236
|
+
*/
|
|
1237
|
+
declare function generateOverlayId(): string;
|
|
1238
|
+
|
|
1168
1239
|
/**
|
|
1169
1240
|
* Hook exports for ugcinc-render
|
|
1170
1241
|
*
|
|
@@ -1224,4 +1295,4 @@ declare function useResolvedPositions(elements: ImageEditorElement[], textValues
|
|
|
1224
1295
|
|
|
1225
1296
|
declare const RenderRoot: React.FC;
|
|
1226
1297
|
|
|
1227
|
-
export { type AudioSegment, type BaseEditorConfig, type BaseSegment, type BorderRadiusConfig, type Channel, type CropAxisConfig, type CropBoundary, type CropBounds, DIMENSION_PRESETS, type DimensionPreset, type DimensionPresetKey, type DynamicCropConfig, type EditorConfig, type EditorSegment, FONT_FAMILIES, FONT_URLS, type FitDimensions, type FitMode, type FontType, type FontWeight, type HorizontalAnchor, type HorizontalSelfAnchor, type Hyphenation, IMAGE_DEFAULTS, ImageEditorComposition, type ImageEditorCompositionProps, type ImageEditorConfig, type ImageEditorElement, type ImageEditorNodeConfig, ImageElement, type ImageElementProps, type ImageSegment, type PictureSegment, type PositionResolutionError, type PositionResolutionResult, type RelativePositionConfigX, type RelativePositionConfigY, RenderRoot, type Segment, type SegmentType, type StaticSegment, TEXT_DEFAULTS, type TextAlignment, type TextDirection, TextElement, type TextElementProps, type TextOverflow, type TextSegment, type TextWrap, type TimeMode, type TimeValue, VIDEO_DEFAULTS, VISUAL_DEFAULTS, type VerticalAlignment, type VerticalAnchor, type VerticalSelfAnchor, type VideoEditorAudioSegment, type VideoEditorBaseSegment, type VideoEditorChannel, VideoEditorComposition, type VideoEditorCompositionProps, type VideoEditorConfig, type VideoEditorImageSegment, type VideoEditorNodeConfig, type VideoEditorSegment, type VideoEditorTextSegment, type VideoEditorVideoSegment, type VideoEditorVisualSegment, VideoElement, type VideoElementProps, type VideoSegment, type VisualSegment, type VisualSegmentUnion, type WordBreak, applyImageDefaults, applyTextDefaults, applyVideoDefaults, areFontsLoaded, buildFontString, calculateAutoWidthDimensions, calculateCropBounds, calculateFitDimensions, calculateLineWidth, canSetAsReference, getBorderRadii, getDependentElements, getFontFamily, getReferenceElementX, getReferenceElementY, hexToRgba, isDynamicCropEnabled, parseHexColor, preloadFonts, resolveElementPositions, useFontsLoaded, useImageLoader, useImagePreloader, useResolvedPositions, wrapText };
|
|
1298
|
+
export { type AudioSegment, type BaseEditorConfig, type BaseSegment, type BorderRadiusConfig, type Channel, type CropAxisConfig, type CropBoundary, type CropBounds, DIMENSION_PRESETS, type DimensionPreset, type DimensionPresetKey, type DynamicCropConfig, type EditorConfig, type EditorSegment, FONT_FAMILIES, FONT_URLS, type FitDimensions, type FitMode, type FontType, type FontWeight, type HorizontalAnchor, type HorizontalSelfAnchor, type Hyphenation, IMAGE_DEFAULTS, ImageEditorComposition, type ImageEditorCompositionProps, type ImageEditorConfig, type ImageEditorElement, type ImageEditorNodeConfig, ImageElement, type ImageElementProps, type ImageSegment, type PictureSegment, type PositionResolutionError, type PositionResolutionResult, type RelativePositionConfigX, type RelativePositionConfigY, RenderRoot, type Segment, type SegmentTimelinePosition, type SegmentType, type StaticSegment, TEXT_DEFAULTS, type TextAlignment, type TextDirection, TextElement, type TextElementProps, type TextOverflow, type TextSegment, type TextWrap, type TimeMode, type TimeValue, VIDEO_DEFAULTS, VISUAL_DEFAULTS, type VerticalAlignment, type VerticalAnchor, type VerticalSelfAnchor, type VideoEditorAudioSegment, type VideoEditorBaseSegment, type VideoEditorChannel, VideoEditorComposition, type VideoEditorCompositionProps, type VideoEditorConfig, type VideoEditorImageSegment, type VideoEditorNodeConfig, type VideoEditorSegment, type VideoEditorTextSegment, type VideoEditorVideoSegment, type VideoEditorVisualSegment, VideoElement, type VideoElementProps, type VideoSegment, type VisualSegment, type VisualSegmentUnion, type WordBreak, applyImageDefaults, applyTextDefaults, applyVideoDefaults, areFontsLoaded, buildFontString, calculateAutoWidthDimensions, calculateCropBounds, calculateEstimatedDuration, calculateFitDimensions, calculateLineWidth, calculateTimelineContentEnd, canSetAsReference, defaultOffset, formatTime, generateOverlayId, generateSegmentId, getBaseSegments, getBorderRadii, getDependentElements, getFontFamily, getOverlays, getReferenceElementX, getReferenceElementY, getSegmentTimelinePosition, hexToRgba, isDynamicCropEnabled, isSegmentVisibleAtTime, parseHexColor, parseTime, preloadFonts, resolveElementPositions, useFontsLoaded, useImageLoader, useImagePreloader, useResolvedPositions, wrapText };
|
package/dist/index.d.ts
CHANGED
|
@@ -457,6 +457,15 @@ interface VideoEditorNodeConfig {
|
|
|
457
457
|
previewUrls?: Record<string, string>;
|
|
458
458
|
previewTextValues?: Record<string, string>;
|
|
459
459
|
}
|
|
460
|
+
/**
|
|
461
|
+
* Segment position on timeline (calculated from segment timing properties)
|
|
462
|
+
*/
|
|
463
|
+
interface SegmentTimelinePosition {
|
|
464
|
+
/** Start time in milliseconds */
|
|
465
|
+
startMs: number;
|
|
466
|
+
/** Duration in milliseconds */
|
|
467
|
+
durationMs: number;
|
|
468
|
+
}
|
|
460
469
|
|
|
461
470
|
/**
|
|
462
471
|
* Segment types for the rendering system
|
|
@@ -1165,6 +1174,68 @@ declare function calculateCropBounds(elements: ImageEditorElement[], dynamicCrop
|
|
|
1165
1174
|
*/
|
|
1166
1175
|
declare function isDynamicCropEnabled(dynamicCrop: DynamicCropConfig | undefined): boolean;
|
|
1167
1176
|
|
|
1177
|
+
/**
|
|
1178
|
+
* Timeline utility functions for video editor
|
|
1179
|
+
*
|
|
1180
|
+
* These functions calculate segment positions on the timeline based on
|
|
1181
|
+
* the segment/overlay timing model. Used by both the webapp UI and
|
|
1182
|
+
* the VideoEditorComposition for rendering.
|
|
1183
|
+
*/
|
|
1184
|
+
|
|
1185
|
+
/**
|
|
1186
|
+
* Create a default TimeValue for segment offsets
|
|
1187
|
+
*/
|
|
1188
|
+
declare function defaultOffset(mode?: TimeMode): TimeValue;
|
|
1189
|
+
/**
|
|
1190
|
+
* Get base segments (no parentId) for a channel
|
|
1191
|
+
* Base segments are the primary timeline elements that overlays attach to
|
|
1192
|
+
*/
|
|
1193
|
+
declare function getBaseSegments(channel: VideoEditorChannel): VideoEditorSegment[];
|
|
1194
|
+
/**
|
|
1195
|
+
* Get overlays for a specific parent segment (or global overlays if parentId is null)
|
|
1196
|
+
*/
|
|
1197
|
+
declare function getOverlays(channel: VideoEditorChannel, parentId: string | null): VideoEditorSegment[];
|
|
1198
|
+
/**
|
|
1199
|
+
* Calculate segment position on timeline
|
|
1200
|
+
*
|
|
1201
|
+
* For base segments:
|
|
1202
|
+
* - Position is calculated from offset and previous segments
|
|
1203
|
+
* - Duration is from the duration property or default (5 seconds)
|
|
1204
|
+
*
|
|
1205
|
+
* For overlay segments:
|
|
1206
|
+
* - Position is calculated relative to parent using relativeStart/relativeEnd
|
|
1207
|
+
* - relativeStart/relativeEnd are fractions (0-1) of parent duration
|
|
1208
|
+
*/
|
|
1209
|
+
declare function getSegmentTimelinePosition(segment: VideoEditorSegment, baseSegments: VideoEditorSegment[], channel: VideoEditorChannel): SegmentTimelinePosition;
|
|
1210
|
+
/**
|
|
1211
|
+
* Check if a segment is visible at a given time
|
|
1212
|
+
*/
|
|
1213
|
+
declare function isSegmentVisibleAtTime(segment: VideoEditorSegment, time: number, channel: VideoEditorChannel): boolean;
|
|
1214
|
+
/**
|
|
1215
|
+
* Calculate estimated total duration based on segments
|
|
1216
|
+
*/
|
|
1217
|
+
declare function calculateEstimatedDuration(channels: VideoEditorChannel[]): number;
|
|
1218
|
+
/**
|
|
1219
|
+
* Calculate the timeline content end time (used for both ruler and scroll width)
|
|
1220
|
+
*/
|
|
1221
|
+
declare function calculateTimelineContentEnd(channel: VideoEditorChannel): number;
|
|
1222
|
+
/**
|
|
1223
|
+
* Format time in mm:ss.ms
|
|
1224
|
+
*/
|
|
1225
|
+
declare function formatTime(ms: number): string;
|
|
1226
|
+
/**
|
|
1227
|
+
* Parse time string to milliseconds
|
|
1228
|
+
*/
|
|
1229
|
+
declare function parseTime(timeStr: string): number;
|
|
1230
|
+
/**
|
|
1231
|
+
* Generate a unique segment ID
|
|
1232
|
+
*/
|
|
1233
|
+
declare function generateSegmentId(): string;
|
|
1234
|
+
/**
|
|
1235
|
+
* Generate a unique overlay ID
|
|
1236
|
+
*/
|
|
1237
|
+
declare function generateOverlayId(): string;
|
|
1238
|
+
|
|
1168
1239
|
/**
|
|
1169
1240
|
* Hook exports for ugcinc-render
|
|
1170
1241
|
*
|
|
@@ -1224,4 +1295,4 @@ declare function useResolvedPositions(elements: ImageEditorElement[], textValues
|
|
|
1224
1295
|
|
|
1225
1296
|
declare const RenderRoot: React.FC;
|
|
1226
1297
|
|
|
1227
|
-
export { type AudioSegment, type BaseEditorConfig, type BaseSegment, type BorderRadiusConfig, type Channel, type CropAxisConfig, type CropBoundary, type CropBounds, DIMENSION_PRESETS, type DimensionPreset, type DimensionPresetKey, type DynamicCropConfig, type EditorConfig, type EditorSegment, FONT_FAMILIES, FONT_URLS, type FitDimensions, type FitMode, type FontType, type FontWeight, type HorizontalAnchor, type HorizontalSelfAnchor, type Hyphenation, IMAGE_DEFAULTS, ImageEditorComposition, type ImageEditorCompositionProps, type ImageEditorConfig, type ImageEditorElement, type ImageEditorNodeConfig, ImageElement, type ImageElementProps, type ImageSegment, type PictureSegment, type PositionResolutionError, type PositionResolutionResult, type RelativePositionConfigX, type RelativePositionConfigY, RenderRoot, type Segment, type SegmentType, type StaticSegment, TEXT_DEFAULTS, type TextAlignment, type TextDirection, TextElement, type TextElementProps, type TextOverflow, type TextSegment, type TextWrap, type TimeMode, type TimeValue, VIDEO_DEFAULTS, VISUAL_DEFAULTS, type VerticalAlignment, type VerticalAnchor, type VerticalSelfAnchor, type VideoEditorAudioSegment, type VideoEditorBaseSegment, type VideoEditorChannel, VideoEditorComposition, type VideoEditorCompositionProps, type VideoEditorConfig, type VideoEditorImageSegment, type VideoEditorNodeConfig, type VideoEditorSegment, type VideoEditorTextSegment, type VideoEditorVideoSegment, type VideoEditorVisualSegment, VideoElement, type VideoElementProps, type VideoSegment, type VisualSegment, type VisualSegmentUnion, type WordBreak, applyImageDefaults, applyTextDefaults, applyVideoDefaults, areFontsLoaded, buildFontString, calculateAutoWidthDimensions, calculateCropBounds, calculateFitDimensions, calculateLineWidth, canSetAsReference, getBorderRadii, getDependentElements, getFontFamily, getReferenceElementX, getReferenceElementY, hexToRgba, isDynamicCropEnabled, parseHexColor, preloadFonts, resolveElementPositions, useFontsLoaded, useImageLoader, useImagePreloader, useResolvedPositions, wrapText };
|
|
1298
|
+
export { type AudioSegment, type BaseEditorConfig, type BaseSegment, type BorderRadiusConfig, type Channel, type CropAxisConfig, type CropBoundary, type CropBounds, DIMENSION_PRESETS, type DimensionPreset, type DimensionPresetKey, type DynamicCropConfig, type EditorConfig, type EditorSegment, FONT_FAMILIES, FONT_URLS, type FitDimensions, type FitMode, type FontType, type FontWeight, type HorizontalAnchor, type HorizontalSelfAnchor, type Hyphenation, IMAGE_DEFAULTS, ImageEditorComposition, type ImageEditorCompositionProps, type ImageEditorConfig, type ImageEditorElement, type ImageEditorNodeConfig, ImageElement, type ImageElementProps, type ImageSegment, type PictureSegment, type PositionResolutionError, type PositionResolutionResult, type RelativePositionConfigX, type RelativePositionConfigY, RenderRoot, type Segment, type SegmentTimelinePosition, type SegmentType, type StaticSegment, TEXT_DEFAULTS, type TextAlignment, type TextDirection, TextElement, type TextElementProps, type TextOverflow, type TextSegment, type TextWrap, type TimeMode, type TimeValue, VIDEO_DEFAULTS, VISUAL_DEFAULTS, type VerticalAlignment, type VerticalAnchor, type VerticalSelfAnchor, type VideoEditorAudioSegment, type VideoEditorBaseSegment, type VideoEditorChannel, VideoEditorComposition, type VideoEditorCompositionProps, type VideoEditorConfig, type VideoEditorImageSegment, type VideoEditorNodeConfig, type VideoEditorSegment, type VideoEditorTextSegment, type VideoEditorVideoSegment, type VideoEditorVisualSegment, VideoElement, type VideoElementProps, type VideoSegment, type VisualSegment, type VisualSegmentUnion, type WordBreak, applyImageDefaults, applyTextDefaults, applyVideoDefaults, areFontsLoaded, buildFontString, calculateAutoWidthDimensions, calculateCropBounds, calculateEstimatedDuration, calculateFitDimensions, calculateLineWidth, calculateTimelineContentEnd, canSetAsReference, defaultOffset, formatTime, generateOverlayId, generateSegmentId, getBaseSegments, getBorderRadii, getDependentElements, getFontFamily, getOverlays, getReferenceElementX, getReferenceElementY, getSegmentTimelinePosition, hexToRgba, isDynamicCropEnabled, isSegmentVisibleAtTime, parseHexColor, parseTime, preloadFonts, resolveElementPositions, useFontsLoaded, useImageLoader, useImagePreloader, useResolvedPositions, wrapText };
|
package/dist/index.js
CHANGED
|
@@ -40,17 +40,28 @@ __export(index_exports, {
|
|
|
40
40
|
buildFontString: () => buildFontString,
|
|
41
41
|
calculateAutoWidthDimensions: () => calculateAutoWidthDimensions,
|
|
42
42
|
calculateCropBounds: () => calculateCropBounds,
|
|
43
|
+
calculateEstimatedDuration: () => calculateEstimatedDuration,
|
|
43
44
|
calculateFitDimensions: () => calculateFitDimensions,
|
|
44
45
|
calculateLineWidth: () => calculateLineWidth,
|
|
46
|
+
calculateTimelineContentEnd: () => calculateTimelineContentEnd,
|
|
45
47
|
canSetAsReference: () => canSetAsReference,
|
|
48
|
+
defaultOffset: () => defaultOffset,
|
|
49
|
+
formatTime: () => formatTime,
|
|
50
|
+
generateOverlayId: () => generateOverlayId,
|
|
51
|
+
generateSegmentId: () => generateSegmentId,
|
|
52
|
+
getBaseSegments: () => getBaseSegments,
|
|
46
53
|
getBorderRadii: () => getBorderRadii,
|
|
47
54
|
getDependentElements: () => getDependentElements,
|
|
48
55
|
getFontFamily: () => getFontFamily,
|
|
56
|
+
getOverlays: () => getOverlays,
|
|
49
57
|
getReferenceElementX: () => getReferenceElementX,
|
|
50
58
|
getReferenceElementY: () => getReferenceElementY,
|
|
59
|
+
getSegmentTimelinePosition: () => getSegmentTimelinePosition,
|
|
51
60
|
hexToRgba: () => hexToRgba,
|
|
52
61
|
isDynamicCropEnabled: () => isDynamicCropEnabled,
|
|
62
|
+
isSegmentVisibleAtTime: () => isSegmentVisibleAtTime,
|
|
53
63
|
parseHexColor: () => parseHexColor,
|
|
64
|
+
parseTime: () => parseTime,
|
|
54
65
|
preloadFonts: () => preloadFonts,
|
|
55
66
|
resolveElementPositions: () => resolveElementPositions,
|
|
56
67
|
useFontsLoaded: () => useFontsLoaded,
|
|
@@ -1461,6 +1472,101 @@ function calculateFitDimensions({
|
|
|
1461
1472
|
};
|
|
1462
1473
|
}
|
|
1463
1474
|
|
|
1475
|
+
// src/utils/timeline.ts
|
|
1476
|
+
function defaultOffset(mode = "flexible") {
|
|
1477
|
+
return mode === "flexible" ? { type: "relative", value: 0 } : { type: "absolute", value: 0 };
|
|
1478
|
+
}
|
|
1479
|
+
function getBaseSegments(channel) {
|
|
1480
|
+
return channel.segments.filter((s) => s.parentId === void 0);
|
|
1481
|
+
}
|
|
1482
|
+
function getOverlays(channel, parentId) {
|
|
1483
|
+
return channel.segments.filter((s) => s.parentId === (parentId ?? void 0));
|
|
1484
|
+
}
|
|
1485
|
+
function getSegmentTimelinePosition(segment, baseSegments, channel) {
|
|
1486
|
+
if (segment.parentId) {
|
|
1487
|
+
const parent = channel.segments.find((s) => s.id === segment.parentId);
|
|
1488
|
+
if (parent) {
|
|
1489
|
+
const parentPos = getSegmentTimelinePosition(parent, baseSegments, channel);
|
|
1490
|
+
const relStart = segment.relativeStart ?? 0;
|
|
1491
|
+
const relEnd = segment.relativeEnd ?? 1;
|
|
1492
|
+
return {
|
|
1493
|
+
startMs: parentPos.startMs + parentPos.durationMs * relStart,
|
|
1494
|
+
durationMs: parentPos.durationMs * (relEnd - relStart)
|
|
1495
|
+
};
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
const baseIndex = baseSegments.findIndex((s) => s.id === segment.id);
|
|
1499
|
+
let accumulatedTime = 0;
|
|
1500
|
+
for (let i = 0; i < baseIndex; i++) {
|
|
1501
|
+
const prev = baseSegments[i];
|
|
1502
|
+
if (prev) {
|
|
1503
|
+
accumulatedTime += prev.duration?.type === "absolute" ? prev.duration.value : 5e3;
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
const startMs = segment.offset.type === "absolute" ? segment.offset.value : accumulatedTime;
|
|
1507
|
+
const durationMs = segment.duration?.type === "absolute" ? segment.duration.value : 5e3;
|
|
1508
|
+
return { startMs, durationMs };
|
|
1509
|
+
}
|
|
1510
|
+
function isSegmentVisibleAtTime(segment, time, channel) {
|
|
1511
|
+
const baseSegments = getBaseSegments(channel);
|
|
1512
|
+
const { startMs, durationMs } = getSegmentTimelinePosition(segment, baseSegments, channel);
|
|
1513
|
+
const endMs = startMs + durationMs;
|
|
1514
|
+
return time >= startMs && time < endMs;
|
|
1515
|
+
}
|
|
1516
|
+
function calculateEstimatedDuration(channels) {
|
|
1517
|
+
let maxDuration = 5e3;
|
|
1518
|
+
for (const channel of channels) {
|
|
1519
|
+
let channelTime = 0;
|
|
1520
|
+
for (const segment of channel.segments) {
|
|
1521
|
+
if (segment.parentId) continue;
|
|
1522
|
+
if (segment.offset.type === "absolute") {
|
|
1523
|
+
channelTime = segment.offset.value;
|
|
1524
|
+
} else {
|
|
1525
|
+
channelTime += 5e3;
|
|
1526
|
+
}
|
|
1527
|
+
if (segment.duration?.type === "absolute") {
|
|
1528
|
+
channelTime += segment.duration.value;
|
|
1529
|
+
} else {
|
|
1530
|
+
channelTime += 5e3;
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
maxDuration = Math.max(maxDuration, channelTime);
|
|
1534
|
+
}
|
|
1535
|
+
return maxDuration;
|
|
1536
|
+
}
|
|
1537
|
+
function calculateTimelineContentEnd(channel) {
|
|
1538
|
+
const baseSegments = getBaseSegments(channel);
|
|
1539
|
+
let lastEnd = 0;
|
|
1540
|
+
for (const segment of baseSegments) {
|
|
1541
|
+
const { startMs, durationMs } = getSegmentTimelinePosition(segment, baseSegments, channel);
|
|
1542
|
+
lastEnd = Math.max(lastEnd, startMs + durationMs);
|
|
1543
|
+
}
|
|
1544
|
+
return Math.ceil((lastEnd + 2e3) / 1e3) * 1e3;
|
|
1545
|
+
}
|
|
1546
|
+
function formatTime(ms) {
|
|
1547
|
+
const totalSeconds = Math.floor(ms / 1e3);
|
|
1548
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
1549
|
+
const seconds = totalSeconds % 60;
|
|
1550
|
+
const milliseconds = Math.floor(ms % 1e3 / 10);
|
|
1551
|
+
return `${minutes.toString().padStart(2, "0")}:${seconds.toString().padStart(2, "0")}.${milliseconds.toString().padStart(2, "0")}`;
|
|
1552
|
+
}
|
|
1553
|
+
function parseTime(timeStr) {
|
|
1554
|
+
const parts = timeStr.split(":");
|
|
1555
|
+
if (parts.length !== 2) return 0;
|
|
1556
|
+
const [minStr, secPart] = parts;
|
|
1557
|
+
const minutes = parseInt(minStr ?? "0", 10) || 0;
|
|
1558
|
+
const secParts = (secPart ?? "0").split(".");
|
|
1559
|
+
const seconds = parseInt(secParts[0] ?? "0", 10) || 0;
|
|
1560
|
+
const ms = parseInt((secParts[1] ?? "0").padEnd(2, "0").slice(0, 2), 10) * 10 || 0;
|
|
1561
|
+
return (minutes * 60 + seconds) * 1e3 + ms;
|
|
1562
|
+
}
|
|
1563
|
+
function generateSegmentId() {
|
|
1564
|
+
return `segment-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
1565
|
+
}
|
|
1566
|
+
function generateOverlayId() {
|
|
1567
|
+
return `overlay-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1464
1570
|
// src/hooks/index.ts
|
|
1465
1571
|
var import_react6 = require("react");
|
|
1466
1572
|
function useFontsLoaded() {
|
|
@@ -1618,17 +1724,28 @@ var RenderRoot = () => {
|
|
|
1618
1724
|
buildFontString,
|
|
1619
1725
|
calculateAutoWidthDimensions,
|
|
1620
1726
|
calculateCropBounds,
|
|
1727
|
+
calculateEstimatedDuration,
|
|
1621
1728
|
calculateFitDimensions,
|
|
1622
1729
|
calculateLineWidth,
|
|
1730
|
+
calculateTimelineContentEnd,
|
|
1623
1731
|
canSetAsReference,
|
|
1732
|
+
defaultOffset,
|
|
1733
|
+
formatTime,
|
|
1734
|
+
generateOverlayId,
|
|
1735
|
+
generateSegmentId,
|
|
1736
|
+
getBaseSegments,
|
|
1624
1737
|
getBorderRadii,
|
|
1625
1738
|
getDependentElements,
|
|
1626
1739
|
getFontFamily,
|
|
1740
|
+
getOverlays,
|
|
1627
1741
|
getReferenceElementX,
|
|
1628
1742
|
getReferenceElementY,
|
|
1743
|
+
getSegmentTimelinePosition,
|
|
1629
1744
|
hexToRgba,
|
|
1630
1745
|
isDynamicCropEnabled,
|
|
1746
|
+
isSegmentVisibleAtTime,
|
|
1631
1747
|
parseHexColor,
|
|
1748
|
+
parseTime,
|
|
1632
1749
|
preloadFonts,
|
|
1633
1750
|
resolveElementPositions,
|
|
1634
1751
|
useFontsLoaded,
|
package/dist/index.mjs
CHANGED
|
@@ -1398,6 +1398,101 @@ function calculateFitDimensions({
|
|
|
1398
1398
|
};
|
|
1399
1399
|
}
|
|
1400
1400
|
|
|
1401
|
+
// src/utils/timeline.ts
|
|
1402
|
+
function defaultOffset(mode = "flexible") {
|
|
1403
|
+
return mode === "flexible" ? { type: "relative", value: 0 } : { type: "absolute", value: 0 };
|
|
1404
|
+
}
|
|
1405
|
+
function getBaseSegments(channel) {
|
|
1406
|
+
return channel.segments.filter((s) => s.parentId === void 0);
|
|
1407
|
+
}
|
|
1408
|
+
function getOverlays(channel, parentId) {
|
|
1409
|
+
return channel.segments.filter((s) => s.parentId === (parentId ?? void 0));
|
|
1410
|
+
}
|
|
1411
|
+
function getSegmentTimelinePosition(segment, baseSegments, channel) {
|
|
1412
|
+
if (segment.parentId) {
|
|
1413
|
+
const parent = channel.segments.find((s) => s.id === segment.parentId);
|
|
1414
|
+
if (parent) {
|
|
1415
|
+
const parentPos = getSegmentTimelinePosition(parent, baseSegments, channel);
|
|
1416
|
+
const relStart = segment.relativeStart ?? 0;
|
|
1417
|
+
const relEnd = segment.relativeEnd ?? 1;
|
|
1418
|
+
return {
|
|
1419
|
+
startMs: parentPos.startMs + parentPos.durationMs * relStart,
|
|
1420
|
+
durationMs: parentPos.durationMs * (relEnd - relStart)
|
|
1421
|
+
};
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
const baseIndex = baseSegments.findIndex((s) => s.id === segment.id);
|
|
1425
|
+
let accumulatedTime = 0;
|
|
1426
|
+
for (let i = 0; i < baseIndex; i++) {
|
|
1427
|
+
const prev = baseSegments[i];
|
|
1428
|
+
if (prev) {
|
|
1429
|
+
accumulatedTime += prev.duration?.type === "absolute" ? prev.duration.value : 5e3;
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
const startMs = segment.offset.type === "absolute" ? segment.offset.value : accumulatedTime;
|
|
1433
|
+
const durationMs = segment.duration?.type === "absolute" ? segment.duration.value : 5e3;
|
|
1434
|
+
return { startMs, durationMs };
|
|
1435
|
+
}
|
|
1436
|
+
function isSegmentVisibleAtTime(segment, time, channel) {
|
|
1437
|
+
const baseSegments = getBaseSegments(channel);
|
|
1438
|
+
const { startMs, durationMs } = getSegmentTimelinePosition(segment, baseSegments, channel);
|
|
1439
|
+
const endMs = startMs + durationMs;
|
|
1440
|
+
return time >= startMs && time < endMs;
|
|
1441
|
+
}
|
|
1442
|
+
function calculateEstimatedDuration(channels) {
|
|
1443
|
+
let maxDuration = 5e3;
|
|
1444
|
+
for (const channel of channels) {
|
|
1445
|
+
let channelTime = 0;
|
|
1446
|
+
for (const segment of channel.segments) {
|
|
1447
|
+
if (segment.parentId) continue;
|
|
1448
|
+
if (segment.offset.type === "absolute") {
|
|
1449
|
+
channelTime = segment.offset.value;
|
|
1450
|
+
} else {
|
|
1451
|
+
channelTime += 5e3;
|
|
1452
|
+
}
|
|
1453
|
+
if (segment.duration?.type === "absolute") {
|
|
1454
|
+
channelTime += segment.duration.value;
|
|
1455
|
+
} else {
|
|
1456
|
+
channelTime += 5e3;
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
maxDuration = Math.max(maxDuration, channelTime);
|
|
1460
|
+
}
|
|
1461
|
+
return maxDuration;
|
|
1462
|
+
}
|
|
1463
|
+
function calculateTimelineContentEnd(channel) {
|
|
1464
|
+
const baseSegments = getBaseSegments(channel);
|
|
1465
|
+
let lastEnd = 0;
|
|
1466
|
+
for (const segment of baseSegments) {
|
|
1467
|
+
const { startMs, durationMs } = getSegmentTimelinePosition(segment, baseSegments, channel);
|
|
1468
|
+
lastEnd = Math.max(lastEnd, startMs + durationMs);
|
|
1469
|
+
}
|
|
1470
|
+
return Math.ceil((lastEnd + 2e3) / 1e3) * 1e3;
|
|
1471
|
+
}
|
|
1472
|
+
function formatTime(ms) {
|
|
1473
|
+
const totalSeconds = Math.floor(ms / 1e3);
|
|
1474
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
1475
|
+
const seconds = totalSeconds % 60;
|
|
1476
|
+
const milliseconds = Math.floor(ms % 1e3 / 10);
|
|
1477
|
+
return `${minutes.toString().padStart(2, "0")}:${seconds.toString().padStart(2, "0")}.${milliseconds.toString().padStart(2, "0")}`;
|
|
1478
|
+
}
|
|
1479
|
+
function parseTime(timeStr) {
|
|
1480
|
+
const parts = timeStr.split(":");
|
|
1481
|
+
if (parts.length !== 2) return 0;
|
|
1482
|
+
const [minStr, secPart] = parts;
|
|
1483
|
+
const minutes = parseInt(minStr ?? "0", 10) || 0;
|
|
1484
|
+
const secParts = (secPart ?? "0").split(".");
|
|
1485
|
+
const seconds = parseInt(secParts[0] ?? "0", 10) || 0;
|
|
1486
|
+
const ms = parseInt((secParts[1] ?? "0").padEnd(2, "0").slice(0, 2), 10) * 10 || 0;
|
|
1487
|
+
return (minutes * 60 + seconds) * 1e3 + ms;
|
|
1488
|
+
}
|
|
1489
|
+
function generateSegmentId() {
|
|
1490
|
+
return `segment-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
1491
|
+
}
|
|
1492
|
+
function generateOverlayId() {
|
|
1493
|
+
return `overlay-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1401
1496
|
// src/hooks/index.ts
|
|
1402
1497
|
import { useEffect, useState, useMemo as useMemo6 } from "react";
|
|
1403
1498
|
function useFontsLoaded() {
|
|
@@ -1554,17 +1649,28 @@ export {
|
|
|
1554
1649
|
buildFontString,
|
|
1555
1650
|
calculateAutoWidthDimensions,
|
|
1556
1651
|
calculateCropBounds,
|
|
1652
|
+
calculateEstimatedDuration,
|
|
1557
1653
|
calculateFitDimensions,
|
|
1558
1654
|
calculateLineWidth,
|
|
1655
|
+
calculateTimelineContentEnd,
|
|
1559
1656
|
canSetAsReference,
|
|
1657
|
+
defaultOffset,
|
|
1658
|
+
formatTime,
|
|
1659
|
+
generateOverlayId,
|
|
1660
|
+
generateSegmentId,
|
|
1661
|
+
getBaseSegments,
|
|
1560
1662
|
getBorderRadii,
|
|
1561
1663
|
getDependentElements,
|
|
1562
1664
|
getFontFamily,
|
|
1665
|
+
getOverlays,
|
|
1563
1666
|
getReferenceElementX,
|
|
1564
1667
|
getReferenceElementY,
|
|
1668
|
+
getSegmentTimelinePosition,
|
|
1565
1669
|
hexToRgba,
|
|
1566
1670
|
isDynamicCropEnabled,
|
|
1671
|
+
isSegmentVisibleAtTime,
|
|
1567
1672
|
parseHexColor,
|
|
1673
|
+
parseTime,
|
|
1568
1674
|
preloadFonts,
|
|
1569
1675
|
resolveElementPositions,
|
|
1570
1676
|
useFontsLoaded,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ugcinc-render",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Unified rendering package for UGC Inc - shared types, components, and compositions for pixel-perfect client/server rendering",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|