Fix player cached images and blank time dates
This commit is contained in:
@@ -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 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 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
|
## 2.13.2 - 2026-09-11
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
app.get('/local-control', function (req, res) {
|
||||||
res.set('Cache-Control', 'no-store');
|
res.set('Cache-Control', 'no-store');
|
||||||
res.type('html').send(renderPageV2());
|
res.type('html').send(renderPageV2());
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
var registry = window.pulsePlayerRegionTypes;
|
var registry = window.pulsePlayerRegionTypes;
|
||||||
var placeholderUtils = window.placeholderUtils || {};
|
var placeholderUtils = window.placeholderUtils || {};
|
||||||
var DEFAULT_FORMAT = '{{hh}}:{{mm}}';
|
|
||||||
var DEFAULT_STYLE = {
|
var DEFAULT_STYLE = {
|
||||||
font_family: 'Arial',
|
font_family: 'Arial',
|
||||||
font_size: 32,
|
font_size: 32,
|
||||||
@@ -223,7 +222,7 @@ function resolveTimeDateTemplatePlaceholder(values, expression) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderTimeDateTemplate(format, timeZone, date) {
|
function renderTimeDateTemplate(format, timeZone, date) {
|
||||||
var template = String(format || '').trim() || DEFAULT_FORMAT;
|
var template = String(format || '').trim();
|
||||||
var values = getTimeDateFormattedParts(timeZone, date);
|
var values = getTimeDateFormattedParts(timeZone, date);
|
||||||
return template.replace(/\{\{\s*([a-zA-Z0-9_.()\-]+)\s*\}\}/g, function (_match, key) {
|
return template.replace(/\{\{\s*([a-zA-Z0-9_.()\-]+)\s*\}\}/g, function (_match, key) {
|
||||||
return String(resolveTimeDateTemplatePlaceholder(values, key, { timeZone: timeZone }) || '');
|
return String(resolveTimeDateTemplatePlaceholder(values, key, { timeZone: timeZone }) || '');
|
||||||
@@ -243,7 +242,7 @@ function getTextStyle(regionContent, region) {
|
|||||||
|
|
||||||
function renderTimeDateRegion(region, regionContent) {
|
function renderTimeDateRegion(region, regionContent) {
|
||||||
var content = regionContent && typeof regionContent === 'object' ? 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 timeZone = resolveTimeZone(content.timezone || content.time_zone || '');
|
||||||
var style = getTextStyle(content, region);
|
var style = getTextStyle(content, region);
|
||||||
var fontFamily = style.font_family ? 'font-family:' + escapeHtml(style.font_family) + ';' : '';
|
var fontFamily = style.font_family ? 'font-family:' + escapeHtml(style.font_family) + ';' : '';
|
||||||
@@ -259,7 +258,7 @@ function updateTimeDateRegion(element) {
|
|||||||
return;
|
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 timeZone = String(element.dataset.timeDateTimezone || '').trim();
|
||||||
var scaleWrapper = element.querySelector('.template-region-text-scale');
|
var scaleWrapper = element.querySelector('.template-region-text-scale');
|
||||||
if (!scaleWrapper) {
|
if (!scaleWrapper) {
|
||||||
|
|||||||
@@ -31,6 +31,25 @@ async function createTestService(options) {
|
|||||||
return { app, server, service, temporaryDirectory };
|
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 () => {
|
test('local control authenticates only cached eligible users and scopes commands to local runtime', async () => {
|
||||||
const { app, service, temporaryDirectory } = await createTestService();
|
const { app, service, temporaryDirectory } = await createTestService();
|
||||||
const password = hashPassword('CorrectHorseBatteryStaple!');
|
const password = hashPassword('CorrectHorseBatteryStaple!');
|
||||||
|
|||||||
@@ -63,3 +63,22 @@ test('time/date region renders placeholder tokens on the player side', () => {
|
|||||||
|
|
||||||
assert.match(markup, /<p>\d{2}:\d{2}<\/p>/);
|
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>/);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user