ugcinc-render 1.5.20 → 1.5.22
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 +4 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +53 -16
- package/dist/index.mjs +60 -23
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -916,6 +916,8 @@ interface ImageElementProps {
|
|
|
916
916
|
segment: ImageSegment;
|
|
917
917
|
/** The source URL of the image */
|
|
918
918
|
src: string;
|
|
919
|
+
/** Start frame of this segment in the composition (needed for fade-in) */
|
|
920
|
+
startFrame?: number;
|
|
919
921
|
/** Optional scale for high-DPI rendering */
|
|
920
922
|
scale?: number;
|
|
921
923
|
}
|
|
@@ -925,8 +927,9 @@ interface ImageElementProps {
|
|
|
925
927
|
* - Opacity
|
|
926
928
|
* - Rotation
|
|
927
929
|
* - Border radius (uniform or per-corner)
|
|
930
|
+
* - Fade-in effect
|
|
928
931
|
*/
|
|
929
|
-
declare function ImageElement({ segment, src, scale }: ImageElementProps): react_jsx_runtime.JSX.Element;
|
|
932
|
+
declare function ImageElement({ segment, src, startFrame, scale }: ImageElementProps): react_jsx_runtime.JSX.Element;
|
|
930
933
|
|
|
931
934
|
interface VideoElementProps {
|
|
932
935
|
segment: VideoSegment;
|
package/dist/index.d.ts
CHANGED
|
@@ -916,6 +916,8 @@ interface ImageElementProps {
|
|
|
916
916
|
segment: ImageSegment;
|
|
917
917
|
/** The source URL of the image */
|
|
918
918
|
src: string;
|
|
919
|
+
/** Start frame of this segment in the composition (needed for fade-in) */
|
|
920
|
+
startFrame?: number;
|
|
919
921
|
/** Optional scale for high-DPI rendering */
|
|
920
922
|
scale?: number;
|
|
921
923
|
}
|
|
@@ -925,8 +927,9 @@ interface ImageElementProps {
|
|
|
925
927
|
* - Opacity
|
|
926
928
|
* - Rotation
|
|
927
929
|
* - Border radius (uniform or per-corner)
|
|
930
|
+
* - Fade-in effect
|
|
928
931
|
*/
|
|
929
|
-
declare function ImageElement({ segment, src, scale }: ImageElementProps): react_jsx_runtime.JSX.Element;
|
|
932
|
+
declare function ImageElement({ segment, src, startFrame, scale }: ImageElementProps): react_jsx_runtime.JSX.Element;
|
|
930
933
|
|
|
931
934
|
interface VideoElementProps {
|
|
932
935
|
segment: VideoSegment;
|
package/dist/index.js
CHANGED
|
@@ -717,15 +717,26 @@ var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
|
717
717
|
function fitModeToCss(fit) {
|
|
718
718
|
return fit;
|
|
719
719
|
}
|
|
720
|
-
function ImageElement({ segment, src, scale = 1 }) {
|
|
720
|
+
function ImageElement({ segment, src, startFrame = 0, scale = 1 }) {
|
|
721
|
+
const frame = (0, import_remotion.useCurrentFrame)();
|
|
722
|
+
const { fps } = (0, import_remotion.useVideoConfig)();
|
|
721
723
|
const fit = segment.fit ?? IMAGE_DEFAULTS.fit;
|
|
722
724
|
const opacity = (segment.opacity ?? VISUAL_DEFAULTS.opacity) / 100;
|
|
723
725
|
const rotation = segment.rotation ?? 0;
|
|
724
726
|
const borderRadius = segment.borderRadius;
|
|
727
|
+
const fadeIn = segment.fadeIn ?? 0;
|
|
725
728
|
const x = segment.xOffset * scale;
|
|
726
729
|
const y = segment.yOffset * scale;
|
|
727
730
|
const width = segment.width * scale;
|
|
728
731
|
const height = segment.height * scale;
|
|
732
|
+
const fadeOpacity = (0, import_react2.useMemo)(() => {
|
|
733
|
+
if (fadeIn <= 0) return 1;
|
|
734
|
+
const framesFromStart = frame - startFrame;
|
|
735
|
+
const fadeInFrames = fadeIn / 1e3 * fps;
|
|
736
|
+
if (framesFromStart >= fadeInFrames) return 1;
|
|
737
|
+
if (framesFromStart <= 0) return 0;
|
|
738
|
+
return framesFromStart / fadeInFrames;
|
|
739
|
+
}, [frame, startFrame, fadeIn, fps]);
|
|
729
740
|
const borderRadiusStyle = (0, import_react2.useMemo)(() => {
|
|
730
741
|
if (!borderRadius) return void 0;
|
|
731
742
|
if (typeof borderRadius === "number") {
|
|
@@ -744,14 +755,14 @@ function ImageElement({ segment, src, scale = 1 }) {
|
|
|
744
755
|
transform: rotation !== 0 ? `rotate(${rotation}deg)` : void 0,
|
|
745
756
|
transformOrigin: "center center",
|
|
746
757
|
overflow: "hidden",
|
|
747
|
-
borderRadius: borderRadiusStyle
|
|
748
|
-
|
|
758
|
+
borderRadius: borderRadiusStyle,
|
|
759
|
+
opacity: opacity * fadeOpacity
|
|
760
|
+
}), [x, y, width, height, rotation, borderRadiusStyle, opacity, fadeOpacity]);
|
|
749
761
|
const imageStyle = (0, import_react2.useMemo)(() => ({
|
|
750
762
|
width: "100%",
|
|
751
763
|
height: "100%",
|
|
752
|
-
objectFit: fitModeToCss(fit)
|
|
753
|
-
|
|
754
|
-
}), [fit, opacity]);
|
|
764
|
+
objectFit: fitModeToCss(fit)
|
|
765
|
+
}), [fit]);
|
|
755
766
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: containerStyle, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
756
767
|
import_remotion.Img,
|
|
757
768
|
{
|
|
@@ -1320,17 +1331,42 @@ function ImageEditorComposition({
|
|
|
1320
1331
|
if (typeof document !== "undefined" && document.fonts) {
|
|
1321
1332
|
console.log("[Composition] Waiting for fonts to load...");
|
|
1322
1333
|
await document.fonts.ready;
|
|
1323
|
-
const
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1334
|
+
const fontsToLoad = [
|
|
1335
|
+
// SF Pro
|
|
1336
|
+
'normal 48px "SF Pro"',
|
|
1337
|
+
'bold 48px "SF Pro"',
|
|
1338
|
+
'100 48px "SF Pro"',
|
|
1339
|
+
'200 48px "SF Pro"',
|
|
1340
|
+
'300 48px "SF Pro"',
|
|
1341
|
+
'400 48px "SF Pro"',
|
|
1342
|
+
'500 48px "SF Pro"',
|
|
1343
|
+
'600 48px "SF Pro"',
|
|
1344
|
+
'700 48px "SF Pro"',
|
|
1345
|
+
'800 48px "SF Pro"',
|
|
1346
|
+
'900 48px "SF Pro"',
|
|
1347
|
+
// TikTok Sans
|
|
1348
|
+
'normal 48px "TikTok Sans"',
|
|
1349
|
+
'bold 48px "TikTok Sans"',
|
|
1350
|
+
'400 48px "TikTok Sans"',
|
|
1351
|
+
'500 48px "TikTok Sans"',
|
|
1352
|
+
'600 48px "TikTok Sans"',
|
|
1353
|
+
'700 48px "TikTok Sans"',
|
|
1354
|
+
'800 48px "TikTok Sans"',
|
|
1355
|
+
'900 48px "TikTok Sans"',
|
|
1356
|
+
// Apple Color Emoji
|
|
1357
|
+
'normal 48px "Apple Color Emoji"'
|
|
1358
|
+
];
|
|
1359
|
+
await Promise.all(
|
|
1360
|
+
fontsToLoad.map(
|
|
1361
|
+
(font) => document.fonts.load(font).catch(() => [])
|
|
1362
|
+
)
|
|
1363
|
+
);
|
|
1364
|
+
await document.fonts.ready;
|
|
1329
1365
|
console.log("[Composition] Fonts loaded:", {
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1366
|
+
sfPro: document.fonts.check('normal 48px "SF Pro"'),
|
|
1367
|
+
tiktok: document.fonts.check('normal 48px "TikTok Sans"'),
|
|
1368
|
+
emoji: document.fonts.check('normal 48px "Apple Color Emoji"'),
|
|
1369
|
+
totalFonts: document.fonts.size
|
|
1334
1370
|
});
|
|
1335
1371
|
}
|
|
1336
1372
|
setFontsLoaded(true);
|
|
@@ -1709,6 +1745,7 @@ function VideoEditorComposition({
|
|
|
1709
1745
|
{
|
|
1710
1746
|
segment,
|
|
1711
1747
|
src,
|
|
1748
|
+
startFrame,
|
|
1712
1749
|
scale: 1
|
|
1713
1750
|
},
|
|
1714
1751
|
segment.id
|
package/dist/index.mjs
CHANGED
|
@@ -626,20 +626,31 @@ function TextElement({ segment, scale = 1 }) {
|
|
|
626
626
|
|
|
627
627
|
// src/components/ImageElement.tsx
|
|
628
628
|
import { useMemo as useMemo2 } from "react";
|
|
629
|
-
import { Img } from "remotion";
|
|
629
|
+
import { Img, useCurrentFrame, useVideoConfig } from "remotion";
|
|
630
630
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
631
631
|
function fitModeToCss(fit) {
|
|
632
632
|
return fit;
|
|
633
633
|
}
|
|
634
|
-
function ImageElement({ segment, src, scale = 1 }) {
|
|
634
|
+
function ImageElement({ segment, src, startFrame = 0, scale = 1 }) {
|
|
635
|
+
const frame = useCurrentFrame();
|
|
636
|
+
const { fps } = useVideoConfig();
|
|
635
637
|
const fit = segment.fit ?? IMAGE_DEFAULTS.fit;
|
|
636
638
|
const opacity = (segment.opacity ?? VISUAL_DEFAULTS.opacity) / 100;
|
|
637
639
|
const rotation = segment.rotation ?? 0;
|
|
638
640
|
const borderRadius = segment.borderRadius;
|
|
641
|
+
const fadeIn = segment.fadeIn ?? 0;
|
|
639
642
|
const x = segment.xOffset * scale;
|
|
640
643
|
const y = segment.yOffset * scale;
|
|
641
644
|
const width = segment.width * scale;
|
|
642
645
|
const height = segment.height * scale;
|
|
646
|
+
const fadeOpacity = useMemo2(() => {
|
|
647
|
+
if (fadeIn <= 0) return 1;
|
|
648
|
+
const framesFromStart = frame - startFrame;
|
|
649
|
+
const fadeInFrames = fadeIn / 1e3 * fps;
|
|
650
|
+
if (framesFromStart >= fadeInFrames) return 1;
|
|
651
|
+
if (framesFromStart <= 0) return 0;
|
|
652
|
+
return framesFromStart / fadeInFrames;
|
|
653
|
+
}, [frame, startFrame, fadeIn, fps]);
|
|
643
654
|
const borderRadiusStyle = useMemo2(() => {
|
|
644
655
|
if (!borderRadius) return void 0;
|
|
645
656
|
if (typeof borderRadius === "number") {
|
|
@@ -658,14 +669,14 @@ function ImageElement({ segment, src, scale = 1 }) {
|
|
|
658
669
|
transform: rotation !== 0 ? `rotate(${rotation}deg)` : void 0,
|
|
659
670
|
transformOrigin: "center center",
|
|
660
671
|
overflow: "hidden",
|
|
661
|
-
borderRadius: borderRadiusStyle
|
|
662
|
-
|
|
672
|
+
borderRadius: borderRadiusStyle,
|
|
673
|
+
opacity: opacity * fadeOpacity
|
|
674
|
+
}), [x, y, width, height, rotation, borderRadiusStyle, opacity, fadeOpacity]);
|
|
663
675
|
const imageStyle = useMemo2(() => ({
|
|
664
676
|
width: "100%",
|
|
665
677
|
height: "100%",
|
|
666
|
-
objectFit: fitModeToCss(fit)
|
|
667
|
-
|
|
668
|
-
}), [fit, opacity]);
|
|
678
|
+
objectFit: fitModeToCss(fit)
|
|
679
|
+
}), [fit]);
|
|
669
680
|
return /* @__PURE__ */ jsx2("div", { style: containerStyle, children: /* @__PURE__ */ jsx2(
|
|
670
681
|
Img,
|
|
671
682
|
{
|
|
@@ -1234,17 +1245,42 @@ function ImageEditorComposition({
|
|
|
1234
1245
|
if (typeof document !== "undefined" && document.fonts) {
|
|
1235
1246
|
console.log("[Composition] Waiting for fonts to load...");
|
|
1236
1247
|
await document.fonts.ready;
|
|
1237
|
-
const
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1248
|
+
const fontsToLoad = [
|
|
1249
|
+
// SF Pro
|
|
1250
|
+
'normal 48px "SF Pro"',
|
|
1251
|
+
'bold 48px "SF Pro"',
|
|
1252
|
+
'100 48px "SF Pro"',
|
|
1253
|
+
'200 48px "SF Pro"',
|
|
1254
|
+
'300 48px "SF Pro"',
|
|
1255
|
+
'400 48px "SF Pro"',
|
|
1256
|
+
'500 48px "SF Pro"',
|
|
1257
|
+
'600 48px "SF Pro"',
|
|
1258
|
+
'700 48px "SF Pro"',
|
|
1259
|
+
'800 48px "SF Pro"',
|
|
1260
|
+
'900 48px "SF Pro"',
|
|
1261
|
+
// TikTok Sans
|
|
1262
|
+
'normal 48px "TikTok Sans"',
|
|
1263
|
+
'bold 48px "TikTok Sans"',
|
|
1264
|
+
'400 48px "TikTok Sans"',
|
|
1265
|
+
'500 48px "TikTok Sans"',
|
|
1266
|
+
'600 48px "TikTok Sans"',
|
|
1267
|
+
'700 48px "TikTok Sans"',
|
|
1268
|
+
'800 48px "TikTok Sans"',
|
|
1269
|
+
'900 48px "TikTok Sans"',
|
|
1270
|
+
// Apple Color Emoji
|
|
1271
|
+
'normal 48px "Apple Color Emoji"'
|
|
1272
|
+
];
|
|
1273
|
+
await Promise.all(
|
|
1274
|
+
fontsToLoad.map(
|
|
1275
|
+
(font) => document.fonts.load(font).catch(() => [])
|
|
1276
|
+
)
|
|
1277
|
+
);
|
|
1278
|
+
await document.fonts.ready;
|
|
1243
1279
|
console.log("[Composition] Fonts loaded:", {
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1280
|
+
sfPro: document.fonts.check('normal 48px "SF Pro"'),
|
|
1281
|
+
tiktok: document.fonts.check('normal 48px "TikTok Sans"'),
|
|
1282
|
+
emoji: document.fonts.check('normal 48px "Apple Color Emoji"'),
|
|
1283
|
+
totalFonts: document.fonts.size
|
|
1248
1284
|
});
|
|
1249
1285
|
}
|
|
1250
1286
|
setFontsLoaded(true);
|
|
@@ -1417,11 +1453,11 @@ function BackgroundImage({
|
|
|
1417
1453
|
|
|
1418
1454
|
// src/compositions/VideoEditorComposition.tsx
|
|
1419
1455
|
import { useMemo as useMemo5 } from "react";
|
|
1420
|
-
import { AbsoluteFill as AbsoluteFill2, useCurrentFrame as
|
|
1456
|
+
import { AbsoluteFill as AbsoluteFill2, useCurrentFrame as useCurrentFrame3, useVideoConfig as useVideoConfig3, Sequence, Audio } from "remotion";
|
|
1421
1457
|
|
|
1422
1458
|
// src/components/VideoElement.tsx
|
|
1423
1459
|
import { useMemo as useMemo4 } from "react";
|
|
1424
|
-
import { Video, useCurrentFrame, useVideoConfig } from "remotion";
|
|
1460
|
+
import { Video, useCurrentFrame as useCurrentFrame2, useVideoConfig as useVideoConfig2 } from "remotion";
|
|
1425
1461
|
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
1426
1462
|
function fitModeToCss2(fit) {
|
|
1427
1463
|
return fit;
|
|
@@ -1433,8 +1469,8 @@ function VideoElement({
|
|
|
1433
1469
|
durationInFrames,
|
|
1434
1470
|
scale = 1
|
|
1435
1471
|
}) {
|
|
1436
|
-
const frame =
|
|
1437
|
-
const { fps } =
|
|
1472
|
+
const frame = useCurrentFrame2();
|
|
1473
|
+
const { fps } = useVideoConfig2();
|
|
1438
1474
|
const fit = segment.fit ?? VIDEO_DEFAULTS.fit;
|
|
1439
1475
|
const speed = segment.speed ?? VIDEO_DEFAULTS.speed;
|
|
1440
1476
|
const volume = (segment.volume ?? VIDEO_DEFAULTS.volume) / 100;
|
|
@@ -1554,8 +1590,8 @@ function VideoEditorComposition({
|
|
|
1554
1590
|
sources = {},
|
|
1555
1591
|
textContent = {}
|
|
1556
1592
|
}) {
|
|
1557
|
-
const frame =
|
|
1558
|
-
const { fps, durationInFrames } =
|
|
1593
|
+
const frame = useCurrentFrame3();
|
|
1594
|
+
const { fps, durationInFrames } = useVideoConfig3();
|
|
1559
1595
|
const segmentTimings = useMemo5(
|
|
1560
1596
|
() => calculateSegmentTimings(config, fps),
|
|
1561
1597
|
[config, fps]
|
|
@@ -1623,6 +1659,7 @@ function VideoEditorComposition({
|
|
|
1623
1659
|
{
|
|
1624
1660
|
segment,
|
|
1625
1661
|
src,
|
|
1662
|
+
startFrame,
|
|
1626
1663
|
scale: 1
|
|
1627
1664
|
},
|
|
1628
1665
|
segment.id
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ugcinc-render",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.22",
|
|
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",
|