Separate YouTube video titles from on-video song/artist fields with Pro gating, serve curated fonts and watermark assets for 1:1 preview parity, and include billing/API/docs/deploy stack for self-hosted release. Co-authored-by: Cursor <cursoragent@cursor.com>
90 lines
2.6 KiB
TypeScript
90 lines
2.6 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { sanitizeFontfileForFilter } from "../lib/fonts";
|
|
import {
|
|
buildDrawtextFilter,
|
|
clampOffset,
|
|
drawtextXy,
|
|
overlayXy,
|
|
requiresCustomWatermarkEntitlement,
|
|
sanitizeDrawtext,
|
|
normalizeWatermarkSettings,
|
|
} from "../lib/watermark";
|
|
|
|
function testSanitize() {
|
|
const s = sanitizeDrawtext("Hi:there'100%[x]");
|
|
assert.ok(s.includes("\\:"), "colon escaped as \\\\:");
|
|
assert.ok(s.includes("\\'"), "quote escaped");
|
|
assert.equal(sanitizeDrawtext("a".repeat(200)).length, 80);
|
|
assert.equal(sanitizeDrawtext("ok\nline").includes("\n"), false);
|
|
}
|
|
|
|
function testPositions() {
|
|
assert.deepEqual(overlayXy("bottom-right", 20, 20), { x: "W-w-20", y: "H-h-20" });
|
|
assert.deepEqual(overlayXy("top-left", 10, 5), { x: "10", y: "5" });
|
|
assert.deepEqual(drawtextXy("center", 0, 0), {
|
|
x: "(w-text_w)/2+0",
|
|
y: "(h-th)/2+0",
|
|
});
|
|
}
|
|
|
|
function testEntitlements() {
|
|
assert.equal(
|
|
requiresCustomWatermarkEntitlement(
|
|
normalizeWatermarkSettings({ mode: "default" }, true),
|
|
),
|
|
false,
|
|
);
|
|
assert.equal(
|
|
requiresCustomWatermarkEntitlement(
|
|
normalizeWatermarkSettings({ mode: "text", text: "Brand" }, true),
|
|
),
|
|
true,
|
|
);
|
|
assert.equal(
|
|
requiresCustomWatermarkEntitlement(
|
|
normalizeWatermarkSettings({ mode: "default", position: "top-left" }, true),
|
|
),
|
|
true,
|
|
);
|
|
assert.equal(
|
|
requiresCustomWatermarkEntitlement(
|
|
normalizeWatermarkSettings({ mode: "default", fontKey: "inter" }, true),
|
|
),
|
|
true,
|
|
);
|
|
assert.equal(clampOffset(999), 200);
|
|
assert.equal(clampOffset(-5), 0);
|
|
}
|
|
|
|
function testDrawtextFontfile() {
|
|
const withFont = buildDrawtextFilter({
|
|
text: "Brand",
|
|
fontSize: 24,
|
|
position: "bottom-right",
|
|
offsetX: 20,
|
|
offsetY: 20,
|
|
fontfileEscaped: sanitizeFontfileForFilter("C:\\fonts\\My Font.ttf"),
|
|
});
|
|
assert.ok(withFont.includes("fontfile="), "fontfile present");
|
|
assert.ok(withFont.includes("C\\:/fonts/My Font.ttf") || withFont.includes("C\\:/fonts/My\\ Font.ttf") || withFont.includes("fontfile='"), "escaped path");
|
|
assert.ok(!withFont.includes("C:\\fonts"), "backslashes normalized");
|
|
|
|
const injection = sanitizeFontfileForFilter("/tmp/evil':drawtext=text='x");
|
|
assert.ok(injection.includes("\\'"), "quote escaped in font path");
|
|
|
|
const noFont = buildDrawtextFilter({
|
|
text: "Hi",
|
|
fontSize: 18,
|
|
position: "top-left",
|
|
offsetX: 0,
|
|
offsetY: 0,
|
|
});
|
|
assert.equal(noFont.includes("fontfile"), false);
|
|
}
|
|
|
|
testSanitize();
|
|
testPositions();
|
|
testEntitlements();
|
|
testDrawtextFontfile();
|
|
console.log("watermark.test.ts: ok");
|