Release 2.21.0: non-destructive media and scoped player sync
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile, web, pulse-signage-web) (push) Successful in 1m36s
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile.player, player, pulse-signage-player) (push) Successful in 31s

This commit is contained in:
lzstealth committed 2026-10-26 21:46:33 +00:00
1 parent 24df245ea1
commit 7cff39dc76
35 files changed
+2009 -949

No files matched your search

+67
View File
@@ -1,3 +1,70 @@
async function waitForSlideImages(markup, renderRequestId) {
if (typeof Image !== 'function' || typeof document === 'undefined') return markup;
var template = document.createElement('template');
template.innerHTML = markup;
var sources = Array.from(new Set(Array.from(template.content.querySelectorAll('img[src]')).map(function (image) { return image.getAttribute('src'); }).filter(Boolean)));
if (!sources.length) return markup;
if (!lastRenderedViewKey && !app.querySelector('.player-media-loading')) {
var loading = document.createElement('div');
loading.className = 'empty player-media-loading';
loading.setAttribute('role', 'status');
loading.setAttribute('aria-live', 'polite');
loading.innerHTML = '<span class="player-media-loading-spinner" aria-hidden="true"></span><span>Loading media...</span>';
app.appendChild(loading);
}
async function loadSource(source) {
var attempt = 0;
var loadedUrl = source;
while (renderRequestId === slideRenderRequestId) {
var ready = await new Promise(function (resolve) {
var image = new Image();
var settled = false;
var timeout;
function finish(value) {
if (settled) return;
settled = true;
window.clearTimeout(timeout);
image.onload = null;
image.onerror = null;
resolve(value);
}
image.onload = async function () {
try {
if (typeof image.decode === 'function') await image.decode();
loadedUrl = image.src;
finish(image.naturalWidth > 0);
} catch (_error) { finish(false); }
};
image.onerror = function () { finish(false); };
timeout = window.setTimeout(function () { finish(false); }, 10000);
var url = source;
if (attempt) {
try {
var retryUrl = new URL(source, window.location.href);
if (retryUrl.origin === window.location.origin && retryUrl.pathname.startsWith('/media/')) {
retryUrl.searchParams.set('_mediaReady', String(Date.now()));
url = retryUrl.toString();
}
} catch (_error) {}
}
image.src = url;
});
if (ready) {
if (loadedUrl !== source) template.content.querySelectorAll('img[src]').forEach(function (element) {
if (element.getAttribute('src') === source) element.setAttribute('src', loadedUrl);
});
return renderRequestId === slideRenderRequestId;
}
if (renderRequestId !== slideRenderRequestId) return false;
attempt += 1;
await new Promise(function (resolve) { window.setTimeout(resolve, Math.min(1000 * attempt, 5000)); });
}
return false;
}
var results = await Promise.all(sources.map(loadSource));
return renderRequestId === slideRenderRequestId && results.every(Boolean) ? template.innerHTML : false;
}
// Slide media startup and teardown helpers.
var renderedVideoPlaybackCleanup = new WeakMap();