@shotstack/shotstack-canvas 1.1.7 → 1.1.9
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/entry.node.cjs +92 -37
- package/dist/entry.node.cjs.map +1 -1
- package/dist/entry.node.d.cts +1 -0
- package/dist/entry.node.d.ts +1 -0
- package/dist/entry.node.js +92 -37
- package/dist/entry.node.js.map +1 -1
- package/dist/entry.web.js +14 -1
- package/dist/entry.web.js.map +1 -1
- package/package.json +1 -1
package/dist/entry.node.d.cts
CHANGED
|
@@ -204,6 +204,7 @@ interface VideoGenerationOptions {
|
|
|
204
204
|
duration: number;
|
|
205
205
|
outputPath: string;
|
|
206
206
|
pixelRatio?: number;
|
|
207
|
+
hasAlpha?: boolean;
|
|
207
208
|
crf?: number;
|
|
208
209
|
preset?: "ultrafast" | "superfast" | "veryfast" | "faster" | "fast" | "medium" | "slow" | "slower" | "veryslow";
|
|
209
210
|
tune?: "film" | "animation" | "grain" | "stillimage" | "psnr" | "ssim" | "fastdecode" | "zerolatency";
|
package/dist/entry.node.d.ts
CHANGED
|
@@ -204,6 +204,7 @@ interface VideoGenerationOptions {
|
|
|
204
204
|
duration: number;
|
|
205
205
|
outputPath: string;
|
|
206
206
|
pixelRatio?: number;
|
|
207
|
+
hasAlpha?: boolean;
|
|
207
208
|
crf?: number;
|
|
208
209
|
preset?: "ultrafast" | "superfast" | "veryfast" | "faster" | "fast" | "medium" | "slow" | "slower" | "veryslow";
|
|
209
210
|
tune?: "film" | "animation" | "grain" | "stillimage" | "psnr" | "ssim" | "fastdecode" | "zerolatency";
|
package/dist/entry.node.js
CHANGED
|
@@ -1210,6 +1210,7 @@ async function createNodePainter(opts) {
|
|
|
1210
1210
|
if (!ctx) throw new Error("2D context unavailable in Node (canvas).");
|
|
1211
1211
|
const offscreenCanvas = createCanvas(canvas.width, canvas.height);
|
|
1212
1212
|
const offscreenCtx = offscreenCanvas.getContext("2d");
|
|
1213
|
+
const gradientCache = /* @__PURE__ */ new Map();
|
|
1213
1214
|
const api = {
|
|
1214
1215
|
async render(ops) {
|
|
1215
1216
|
const globalBox = computeGlobalTextBounds(ops);
|
|
@@ -1230,6 +1231,7 @@ async function createNodePainter(opts) {
|
|
|
1230
1231
|
const hasBackground = !!(op.bg && op.bg.color);
|
|
1231
1232
|
needsAlphaExtraction = !hasBackground;
|
|
1232
1233
|
if (op.bg && op.bg.color) {
|
|
1234
|
+
ctx.clearRect(0, 0, op.width, op.height);
|
|
1233
1235
|
const { color, opacity, radius } = op.bg;
|
|
1234
1236
|
const c = parseHex6(color, opacity);
|
|
1235
1237
|
ctx.fillStyle = `rgba(${c.r},${c.g},${c.b},${c.a})`;
|
|
@@ -1243,10 +1245,15 @@ async function createNodePainter(opts) {
|
|
|
1243
1245
|
ctx.fillRect(0, 0, op.width, op.height);
|
|
1244
1246
|
}
|
|
1245
1247
|
} else {
|
|
1248
|
+
ctx.clearRect(0, 0, op.width, op.height);
|
|
1249
|
+
ctx.save();
|
|
1246
1250
|
ctx.fillStyle = "rgb(255, 255, 255)";
|
|
1247
1251
|
ctx.fillRect(0, 0, op.width, op.height);
|
|
1252
|
+
ctx.restore();
|
|
1253
|
+
offscreenCtx.save();
|
|
1248
1254
|
offscreenCtx.fillStyle = "rgb(0, 0, 0)";
|
|
1249
1255
|
offscreenCtx.fillRect(0, 0, op.width, op.height);
|
|
1256
|
+
offscreenCtx.restore();
|
|
1250
1257
|
}
|
|
1251
1258
|
continue;
|
|
1252
1259
|
}
|
|
@@ -1265,11 +1272,23 @@ async function createNodePainter(opts) {
|
|
|
1265
1272
|
context.translate(fillOp.x, fillOp.y);
|
|
1266
1273
|
const s = fillOp.scale ?? 1;
|
|
1267
1274
|
context.scale(s, -s);
|
|
1268
|
-
context.beginPath();
|
|
1269
|
-
drawSvgPathOnCtx(context, fillOp.path);
|
|
1270
1275
|
const bbox = fillOp.gradientBBox ?? globalBox;
|
|
1271
|
-
const
|
|
1276
|
+
const localBBox = {
|
|
1277
|
+
x: (bbox.x - fillOp.x) / s,
|
|
1278
|
+
y: -(bbox.y - fillOp.y) / s,
|
|
1279
|
+
w: bbox.w / s,
|
|
1280
|
+
h: bbox.h / s
|
|
1281
|
+
};
|
|
1282
|
+
const cacheKey = JSON.stringify({ fill: fillOp.fill, bbox: localBBox });
|
|
1283
|
+
let fill = gradientCache.get(cacheKey);
|
|
1284
|
+
if (!fill) {
|
|
1285
|
+
fill = makeGradientFromBBox(context, fillOp.fill, localBBox);
|
|
1286
|
+
gradientCache.set(cacheKey, fill);
|
|
1287
|
+
console.log("[Node Painter] Created NEW gradient for local bbox:", localBBox);
|
|
1288
|
+
}
|
|
1272
1289
|
context.fillStyle = fill;
|
|
1290
|
+
context.beginPath();
|
|
1291
|
+
drawSvgPathOnCtx(context, fillOp.path);
|
|
1273
1292
|
context.fill();
|
|
1274
1293
|
context.restore();
|
|
1275
1294
|
});
|
|
@@ -1632,44 +1651,72 @@ var VideoGenerator = class {
|
|
|
1632
1651
|
tune = "animation",
|
|
1633
1652
|
profile = "high",
|
|
1634
1653
|
level = "4.2",
|
|
1635
|
-
pixFmt = "yuv420p"
|
|
1654
|
+
pixFmt = "yuv420p",
|
|
1655
|
+
hasAlpha = false
|
|
1636
1656
|
} = options;
|
|
1637
1657
|
const totalFrames = Math.max(2, Math.round(duration * fps) + 1);
|
|
1658
|
+
const finalOutputPath = hasAlpha ? outputPath.replace(/\.mp4$/, ".mov") : outputPath;
|
|
1638
1659
|
console.log(
|
|
1639
|
-
`\u{1F3AC} Generating video: ${width}x${height} @ ${fps}fps, ${duration}s (${totalFrames} frames)
|
|
1640
|
-
CRF=${crf}, preset=${preset}, tune=${tune}, profile=${profile}, level=${level}, pix_fmt=${pixFmt}`
|
|
1660
|
+
`\u{1F3AC} Generating video: ${width}x${height} @ ${fps}fps, ${duration}s (${totalFrames} frames)` + (hasAlpha ? " (MOV with alpha)" : "")
|
|
1641
1661
|
);
|
|
1642
1662
|
return new Promise(async (resolve, reject) => {
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1663
|
+
let args;
|
|
1664
|
+
if (hasAlpha) {
|
|
1665
|
+
args = [
|
|
1666
|
+
"-y",
|
|
1667
|
+
"-f",
|
|
1668
|
+
"image2pipe",
|
|
1669
|
+
"-vcodec",
|
|
1670
|
+
"png",
|
|
1671
|
+
"-framerate",
|
|
1672
|
+
String(fps),
|
|
1673
|
+
"-i",
|
|
1674
|
+
"-",
|
|
1675
|
+
"-c:v",
|
|
1676
|
+
"prores_ks",
|
|
1677
|
+
"-profile:v",
|
|
1678
|
+
"4444",
|
|
1679
|
+
"-pix_fmt",
|
|
1680
|
+
"yuva444p10le",
|
|
1681
|
+
"-vendor",
|
|
1682
|
+
"apl0",
|
|
1683
|
+
"-r",
|
|
1684
|
+
String(fps),
|
|
1685
|
+
finalOutputPath
|
|
1686
|
+
];
|
|
1687
|
+
console.log(` Output: ${finalOutputPath} (ProRes 4444 with alpha)`);
|
|
1688
|
+
} else {
|
|
1689
|
+
args = [
|
|
1690
|
+
"-y",
|
|
1691
|
+
"-f",
|
|
1692
|
+
"image2pipe",
|
|
1693
|
+
"-vcodec",
|
|
1694
|
+
"png",
|
|
1695
|
+
"-framerate",
|
|
1696
|
+
String(fps),
|
|
1697
|
+
"-i",
|
|
1698
|
+
"-",
|
|
1699
|
+
"-c:v",
|
|
1700
|
+
"libx264",
|
|
1701
|
+
"-preset",
|
|
1702
|
+
preset,
|
|
1703
|
+
"-crf",
|
|
1704
|
+
String(crf),
|
|
1705
|
+
"-tune",
|
|
1706
|
+
tune,
|
|
1707
|
+
"-profile:v",
|
|
1708
|
+
profile,
|
|
1709
|
+
"-level",
|
|
1710
|
+
level,
|
|
1711
|
+
"-pix_fmt",
|
|
1712
|
+
pixFmt,
|
|
1713
|
+
"-r",
|
|
1714
|
+
String(fps),
|
|
1715
|
+
"-movflags",
|
|
1716
|
+
"+faststart",
|
|
1717
|
+
outputPath
|
|
1718
|
+
];
|
|
1719
|
+
}
|
|
1673
1720
|
const ffmpeg = spawn(this.ffmpegPath, args, { stdio: ["pipe", "inherit", "pipe"] });
|
|
1674
1721
|
let ffmpegError = "";
|
|
1675
1722
|
ffmpeg.stderr.on("data", (data) => {
|
|
@@ -1918,13 +1965,21 @@ async function createTextEngine(opts = {}) {
|
|
|
1918
1965
|
},
|
|
1919
1966
|
async generateVideo(asset, options) {
|
|
1920
1967
|
try {
|
|
1968
|
+
const hasBackground = !!asset.background?.color;
|
|
1969
|
+
const hasAnimation = !!asset.animation?.preset;
|
|
1970
|
+
const needsAlpha = !hasBackground && hasAnimation;
|
|
1971
|
+
console.log(
|
|
1972
|
+
`\u{1F3A8} Video settings: Animation=${hasAnimation}, Background=${hasBackground}, Alpha=${needsAlpha}`
|
|
1973
|
+
);
|
|
1921
1974
|
const finalOptions = {
|
|
1922
1975
|
width: asset.width ?? width,
|
|
1923
1976
|
height: asset.height ?? height,
|
|
1924
1977
|
fps,
|
|
1925
1978
|
duration: asset.animation?.duration ?? 3,
|
|
1926
1979
|
outputPath: options.outputPath ?? "output.mp4",
|
|
1927
|
-
pixelRatio: asset.pixelRatio ?? pixelRatio
|
|
1980
|
+
pixelRatio: asset.pixelRatio ?? pixelRatio,
|
|
1981
|
+
hasAlpha: needsAlpha,
|
|
1982
|
+
...options
|
|
1928
1983
|
};
|
|
1929
1984
|
const frameGenerator = async (time) => {
|
|
1930
1985
|
return this.renderFrame(asset, time);
|