Release 2.6.24
This commit is contained in:
@@ -2,6 +2,12 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
## 2.6.24 - 2026-08-10
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Deferred playlist updates now keep the current slide index when the next playlist snapshot is applied, so playback no longer jumps back to the first slide mid-cycle.
|
||||||
|
|
||||||
## 2.6.23 - 2026-08-09
|
## 2.6.23 - 2026-08-09
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "pulse-signage-player",
|
"name": "pulse-signage-player",
|
||||||
"version": "2.6.23",
|
"version": "2.6.24",
|
||||||
"private": false,
|
"private": false,
|
||||||
"description": "Pulse Signage player application bundle",
|
"description": "Pulse Signage player application bundle",
|
||||||
"main": "src/common.js",
|
"main": "src/common.js",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "pulse-signage-web",
|
"name": "pulse-signage-web",
|
||||||
"version": "2.6.23",
|
"version": "2.6.24",
|
||||||
"private": false,
|
"private": false,
|
||||||
"description": "Pulse Signage web and bridge application bundle",
|
"description": "Pulse Signage web and bridge application bundle",
|
||||||
"main": "src/common.js",
|
"main": "src/common.js",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "pulse-signage",
|
"name": "pulse-signage",
|
||||||
"version": "2.6.23",
|
"version": "2.6.24",
|
||||||
"private": false,
|
"private": false,
|
||||||
"description": "Pulse Signage application with MySQL and media storage",
|
"description": "Pulse Signage application with MySQL and media storage",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ function applyPendingPlaylistUpdate() {
|
|||||||
if (!pendingPlaylistUpdate) {
|
if (!pendingPlaylistUpdate) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
var nextIndex = Number(index || 0);
|
||||||
slides = pendingPlaylistUpdate.slides;
|
slides = pendingPlaylistUpdate.slides;
|
||||||
currentPlaylistSignature = pendingPlaylistUpdate.signature;
|
currentPlaylistSignature = pendingPlaylistUpdate.signature;
|
||||||
currentPlaylistFadeBetweenSlides = pendingPlaylistUpdate.fadeBetweenSlides;
|
currentPlaylistFadeBetweenSlides = pendingPlaylistUpdate.fadeBetweenSlides;
|
||||||
@@ -80,7 +81,11 @@ function applyPendingPlaylistUpdate() {
|
|||||||
templateLayoutCache = Object.create(null);
|
templateLayoutCache = Object.create(null);
|
||||||
templateRenderPlanCache = Object.create(null);
|
templateRenderPlanCache = Object.create(null);
|
||||||
renderCacheViewportKey = window.innerWidth + 'x' + window.innerHeight;
|
renderCacheViewportKey = window.innerWidth + 'x' + window.innerHeight;
|
||||||
index = 0;
|
const activeSlides = getCurrentActiveSlides();
|
||||||
|
if (!Number.isFinite(nextIndex) || nextIndex < 0) {
|
||||||
|
nextIndex = 0;
|
||||||
|
}
|
||||||
|
index = activeSlides.length ? Math.min(nextIndex, activeSlides.length - 1) : 0;
|
||||||
logDebug('Applied updated playlist on slide transition.');
|
logDebug('Applied updated playlist on slide transition.');
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -279,6 +279,122 @@ test('single-slide playlists re-render the active slide instead of refreshing af
|
|||||||
assert.deepEqual(sandbox.slides, [{ id: 2, duration_seconds: 12 }]);
|
assert.deepEqual(sandbox.slides, [{ id: 2, duration_seconds: 12 }]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('deferred playlist updates preserve the current slide index', async () => {
|
||||||
|
const sandbox = {
|
||||||
|
window: null,
|
||||||
|
location: { origin: 'http://localhost', href: 'http://localhost/screen/test2' },
|
||||||
|
Date,
|
||||||
|
JSON,
|
||||||
|
Math,
|
||||||
|
Number,
|
||||||
|
String,
|
||||||
|
Boolean,
|
||||||
|
Array,
|
||||||
|
Object,
|
||||||
|
Promise,
|
||||||
|
setTimeout,
|
||||||
|
clearTimeout,
|
||||||
|
console,
|
||||||
|
currentPlaylistSignature: 'old-signature',
|
||||||
|
currentPlaylistFadeBetweenSlides: false,
|
||||||
|
currentPlaylistSkipUnavailableRtmp: false,
|
||||||
|
slug: 'test2',
|
||||||
|
pendingPlaylistUpdate: null,
|
||||||
|
slides: [
|
||||||
|
{ id: 1, duration_seconds: 12 },
|
||||||
|
{ id: 2, duration_seconds: 12 },
|
||||||
|
{ id: 3, duration_seconds: 12 }
|
||||||
|
],
|
||||||
|
lastRenderedSlide: { id: 2, duration_seconds: 12 },
|
||||||
|
index: 1,
|
||||||
|
timer: null,
|
||||||
|
slideExpiresAt: null,
|
||||||
|
pausedRemainingMs: null,
|
||||||
|
app: null,
|
||||||
|
currentPlaylistEtag: '',
|
||||||
|
activeSlidesCacheKey: '',
|
||||||
|
activeSlidesCacheValue: [],
|
||||||
|
renderCacheViewportKey: '',
|
||||||
|
slideMarkupCache: Object.create(null),
|
||||||
|
templateLayoutCache: Object.create(null),
|
||||||
|
templateRenderPlanCache: Object.create(null),
|
||||||
|
getCurrentActiveSlides() {
|
||||||
|
return sandbox.slides;
|
||||||
|
},
|
||||||
|
getPlaylistRevision(data) {
|
||||||
|
return data.signature;
|
||||||
|
},
|
||||||
|
normalizeSlide(slide) {
|
||||||
|
return slide;
|
||||||
|
},
|
||||||
|
getActiveSlidesFrom(slideList) {
|
||||||
|
return slideList;
|
||||||
|
},
|
||||||
|
savePlaylistSnapshot() {},
|
||||||
|
markRefreshHealthy() {},
|
||||||
|
setOfflineBannerVisible() {},
|
||||||
|
scheduleRefreshRetry() {},
|
||||||
|
syncWebpagePreloads() {},
|
||||||
|
syncRtmpWarmups() {},
|
||||||
|
clearActiveSlidesCache() {},
|
||||||
|
showCurrent() {},
|
||||||
|
sendCommandState() {},
|
||||||
|
logDebug() {}
|
||||||
|
};
|
||||||
|
sandbox.window = sandbox;
|
||||||
|
|
||||||
|
const scriptPath = path.join(__dirname, '..', 'src', 'player', 'public', 'js', 'player-page-playback.js');
|
||||||
|
const script = fs.readFileSync(scriptPath, 'utf8');
|
||||||
|
const prelude = `
|
||||||
|
var pendingPlaylistUpdate = null;
|
||||||
|
var slides = [
|
||||||
|
{ id: 1, duration_seconds: 12 },
|
||||||
|
{ id: 2, duration_seconds: 12 },
|
||||||
|
{ id: 3, duration_seconds: 12 }
|
||||||
|
];
|
||||||
|
var currentPlaylistSignature = 'old-signature';
|
||||||
|
var currentPlaylistFadeBetweenSlides = false;
|
||||||
|
var currentPlaylistSkipUnavailableRtmp = false;
|
||||||
|
var currentPlaylistEtag = '';
|
||||||
|
var lastRenderedSlide = { id: 2, duration_seconds: 12 };
|
||||||
|
var index = 1;
|
||||||
|
var timer = null;
|
||||||
|
var slideExpiresAt = null;
|
||||||
|
var pausedRemainingMs = null;
|
||||||
|
var app = null;
|
||||||
|
var activeSlidesCacheKey = '';
|
||||||
|
var activeSlidesCacheValue = [];
|
||||||
|
var renderCacheViewportKey = '';
|
||||||
|
var slideMarkupCache = Object.create(null);
|
||||||
|
var templateLayoutCache = Object.create(null);
|
||||||
|
var templateRenderPlanCache = Object.create(null);
|
||||||
|
`;
|
||||||
|
vm.runInNewContext(prelude + '\n' + script, sandbox, { filename: scriptPath });
|
||||||
|
|
||||||
|
sandbox.pendingPlaylistUpdate = {
|
||||||
|
slides: [
|
||||||
|
{ id: 10, duration_seconds: 12 },
|
||||||
|
{ id: 11, duration_seconds: 12 },
|
||||||
|
{ id: 12, duration_seconds: 12 }
|
||||||
|
],
|
||||||
|
signature: 'next-signature',
|
||||||
|
fadeBetweenSlides: false,
|
||||||
|
skipUnavailableRtmp: false
|
||||||
|
};
|
||||||
|
|
||||||
|
const applied = sandbox.applyPendingPlaylistUpdate();
|
||||||
|
|
||||||
|
assert.equal(applied, true);
|
||||||
|
assert.equal(sandbox.currentPlaylistSignature, 'next-signature');
|
||||||
|
assert.equal(sandbox.pendingPlaylistUpdate, null);
|
||||||
|
assert.deepEqual(sandbox.slides, [
|
||||||
|
{ id: 10, duration_seconds: 12 },
|
||||||
|
{ id: 11, duration_seconds: 12 },
|
||||||
|
{ id: 12, duration_seconds: 12 }
|
||||||
|
]);
|
||||||
|
assert.equal(sandbox.index, 1);
|
||||||
|
});
|
||||||
|
|
||||||
test('removing the currently visible slide from a two-slide playlist applies the one-slide update immediately', async () => {
|
test('removing the currently visible slide from a two-slide playlist applies the one-slide update immediately', async () => {
|
||||||
const calls = {
|
const calls = {
|
||||||
showCurrent: 0,
|
showCurrent: 0,
|
||||||
|
|||||||
Reference in New Issue
Block a user