Fix player cached images and blank time dates
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile, web, pulse-signage-web) (push) Successful in 1m50s
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile.player, player, pulse-signage-player) (push) Successful in 33s

This commit is contained in:
2026-09-13 23:56:08 +01:00
parent ac18644fe8
commit 222162df57
5 changed files with 43 additions and 8 deletions
+2
View File
@@ -8,6 +8,8 @@ All notable changes to this project will be documented in this file.
- Fixed the template background image media picker Upload media button so it opens the file selector.
- Fixed the template designer empty background message moving when the background image is removed.
- Fixed cached API and RSS images not being served by the player after being downloaded.
- Fixed blank time/date regions displaying the default clock format on the player.
## 2.13.2 - 2026-09-11
-4
View File
@@ -489,10 +489,6 @@ function createLocalControlService(options) {
}
}
app.use('/media/player-cache', function (_req, res) {
return res.sendStatus(404);
});
app.get('/local-control', function (req, res) {
res.set('Cache-Control', 'no-store');
res.type('html').send(renderPageV2());
+3 -4
View File
@@ -2,7 +2,6 @@
var registry = window.pulsePlayerRegionTypes;
var placeholderUtils = window.placeholderUtils || {};
var DEFAULT_FORMAT = '{{hh}}:{{mm}}';
var DEFAULT_STYLE = {
font_family: 'Arial',
font_size: 32,
@@ -223,7 +222,7 @@ function resolveTimeDateTemplatePlaceholder(values, expression) {
}
function renderTimeDateTemplate(format, timeZone, date) {
var template = String(format || '').trim() || DEFAULT_FORMAT;
var template = String(format || '').trim();
var values = getTimeDateFormattedParts(timeZone, date);
return template.replace(/\{\{\s*([a-zA-Z0-9_.()\-]+)\s*\}\}/g, function (_match, key) {
return String(resolveTimeDateTemplatePlaceholder(values, key, { timeZone: timeZone }) || '');
@@ -243,7 +242,7 @@ function getTextStyle(regionContent, region) {
function renderTimeDateRegion(region, regionContent) {
var content = regionContent && typeof regionContent === 'object' ? regionContent : {};
var format = String(content.value !== undefined ? content.value : content.text || '').trim() || DEFAULT_FORMAT;
var format = String(content.value !== undefined ? content.value : content.text || '').trim();
var timeZone = resolveTimeZone(content.timezone || content.time_zone || '');
var style = getTextStyle(content, region);
var fontFamily = style.font_family ? 'font-family:' + escapeHtml(style.font_family) + ';' : '';
@@ -259,7 +258,7 @@ function updateTimeDateRegion(element) {
return;
}
var format = String(element.dataset.timeDateFormat || '').trim() || DEFAULT_FORMAT;
var format = String(element.dataset.timeDateFormat || '').trim();
var timeZone = String(element.dataset.timeDateTimezone || '').trim();
var scaleWrapper = element.querySelector('.template-region-text-scale');
if (!scaleWrapper) {
+19
View File
@@ -31,6 +31,25 @@ async function createTestService(options) {
return { app, server, service, temporaryDirectory };
}
test('local control leaves cached player media available to the static media route', async () => {
const { app, server, temporaryDirectory } = await createTestService();
const cachedImagePath = path.join(temporaryDirectory, 'player-cache', 'remote-images', 'cached-image.png');
await fs.promises.mkdir(path.dirname(cachedImagePath), { recursive: true });
await fs.promises.writeFile(cachedImagePath, 'cached-image');
app.use('/media', express.static(temporaryDirectory));
const listener = app.listen(0);
try {
const address = listener.address();
const response = await fetch(`http://127.0.0.1:${address.port}/media/player-cache/remote-images/cached-image.png`);
assert.equal(response.status, 200);
assert.equal(await response.text(), 'cached-image');
} finally {
await new Promise((resolve) => listener.close(resolve));
server.close();
await fs.promises.rm(temporaryDirectory, { recursive: true, force: true });
}
});
test('local control authenticates only cached eligible users and scopes commands to local runtime', async () => {
const { app, service, temporaryDirectory } = await createTestService();
const password = hashPassword('CorrectHorseBatteryStaple!');
+19
View File
@@ -62,4 +62,23 @@ test('time/date region renders placeholder tokens on the player side', () => {
);
assert.match(markup, /<p>\d{2}:\d{2}<\/p>/);
});
test('time/date region stays blank when its format is blank', () => {
const module = loadTimeDateModule();
const markup = module.renderRegion(
{
pixelWidth: 320,
pixelHeight: 180,
canvasScale: 1,
baseStyle: 'position:absolute;'
},
{
value: '',
timezone: 'UTC'
}
);
assert.match(markup, /data-time-date-format=""/);
assert.doesNotMatch(markup, /<p>\d{2}:\d{2}<\/p>/);
});