collage fixes
Some checks failed
Build & Push Docker (latest) / verify (push) Successful in 9m27s
Build & Push Docker (latest) / build (push) Has been cancelled

This commit is contained in:
Aiden Wilson
2026-05-29 23:45:52 +10:00
parent 4b488913e4
commit 30cd5c7b13
7 changed files with 372 additions and 55 deletions

View File

@@ -84,6 +84,7 @@ async function serveStatic(requestUrl, response) {
const staticPaths = new Map([
["/styles.css", "styles.css"],
["/caspar.js", "caspar.js"],
["/collage.js", "collage.js"],
]);
const path = staticPaths.get(requestUrl.pathname) || "";
@@ -153,8 +154,10 @@ async function handleCollage(requestUrl, response) {
const spacing = getNumber(requestUrl.searchParams, "spacing", Number(legacySpacing || 48), 0, 400);
const fade = getNumber(requestUrl.searchParams, "fade", 0, 0, 600);
const columns = getNumber(requestUrl.searchParams, "columns", 3, 1, 8);
const repeatDistance = getNumber(requestUrl.searchParams, "repeatDistance", 900, 0, 4000);
const hydrateDelay = getNumber(requestUrl.searchParams, "hydrateDelay", 180, 0, 2000);
const duration = getNumber(requestUrl.searchParams, "duration", 360, 10, 1200);
const repeat = getNumber(requestUrl.searchParams, "repeat", 4, 2, 12);
const repeat = getNumber(requestUrl.searchParams, "repeat", 2, 1, 8);
const shuffle = getBoolean(requestUrl.searchParams, "shuffle", true);
const transparent = getBoolean(requestUrl.searchParams, "transparent", true);
const autoplay = getBoolean(requestUrl.searchParams, "autoplay", true);
@@ -189,6 +192,8 @@ async function handleCollage(requestUrl, response) {
spacing,
fade,
columns,
repeatDistance,
hydrateDelay,
duration,
repeat,
shuffle,

View File

@@ -45,7 +45,7 @@ function collageCardWidth({ width, spacing, columns }) {
const availableWidth = width - (spacing * 2) - (spacing * Math.max(columns - 1, 0));
const columnWidth = Math.floor(availableWidth / columns);
return Math.max(Math.min(columnWidth, 500), 120);
return Math.max(columnWidth, 120);
}
function shuffleItems(items) {
@@ -59,6 +59,10 @@ function shuffleItems(items) {
return shuffled;
}
function safeJson(value) {
return JSON.stringify(value).replaceAll("</", "<\\/");
}
function addIframePermissions(tag) {
const autoplayPermission = "autoplay";
@@ -350,8 +354,10 @@ export function homePage({ providersCount = 0 } = {}) {
<label>Spacing <input name="spacing" type="number" value="48" min="0" max="400"></label>
<label>Fade <input name="fade" type="number" value="0" min="0" max="600"></label>
<label>Columns <input name="columns" type="number" value="3" min="1" max="8"></label>
<label>Separation <input name="repeatDistance" type="number" value="900" min="0" max="4000"></label>
<label>Hydrate <input name="hydrateDelay" type="number" value="180" min="0" max="2000"></label>
<label>Duration <input name="duration" type="number" value="360" min="10" max="1200"></label>
<label>Repeat <input name="repeat" type="number" value="4" min="2" max="12"></label>
<label>Repeat <input name="repeat" type="number" value="2" min="1" max="8"></label>
<label class="checkbox-label"><input name="shuffle" value="1" type="checkbox" checked> shuffle</label>
<label class="checkbox-label"><input name="transparent" value="1" type="checkbox" checked> transparent</label>
</div>
@@ -435,35 +441,43 @@ export function collagePage({
spacing = 48,
fade = 0,
columns = 3,
repeatDistance = 900,
hydrateDelay = 180,
duration = 360,
repeat = 4,
repeat = 2,
shuffle = true,
}) {
const cardWidth = collageCardWidth({ width, spacing, columns });
const orderedItems = shuffle ? shuffleItems(items) : items;
const groupItems = Array.from({ length: repeat }, () => orderedItems).flat();
const groupCards = groupItems
.map((item) => embedCardHtml({
targetUrl: item.targetUrl,
embed: item.embed,
autoplay,
muted,
className: "embed collage-card",
widthCap: cardWidth,
includeHeight: false,
}))
.join("\n");
const groups = [groupCards, groupCards]
.map((cards) => `<div class="collage-group">${cards}</div>`)
.join("\n");
const orderedItems = shuffle ? shuffleItems(items) : [...items];
const collageItems = orderedItems.map((item) => ({
targetUrl: item.targetUrl,
providerName: item.embed.provider_name || "",
type: item.embed.type || "rich",
title: item.embed.title || item.embed.provider_name || "",
url: item.embed.url || "",
html: prepareEmbedHtml(item.embed.html || "", { autoplay, muted }),
}));
const collageData = {
items: collageItems,
cardWidth,
columns,
repeatDistance,
hydrateDelay,
spacing,
duration,
repeat,
shuffle,
autoplay,
muted,
};
return `${commonHead({ title: "oEmbed Collage", htmlClass: transparent ? "graphic-document transparent" : "graphic-document" })}
<body class="graphic collage-page ${transparent ? "transparent" : ""} is-ready" style="--stage-width:${width}px; --stage-height:${height}px; --chroma:${escapeHtml(chroma)}; --collage-spacing:${spacing}px; --collage-fade:${fade}px; --collage-columns:${columns}; --collage-card-width:${cardWidth}px; --collage-duration:${duration}s;">
<main class="collage-stage">
<div class="collage-track">
${groups}
</div>
<div class="collage-track"></div>
</main>
<script id="collage-data" type="application/json">${safeJson(collageData)}</script>
<script src="/collage.js"></script>
</body>
</html>`;
}