diff --git a/CHANGELOG.md b/CHANGELOG.md index 613bcd0..38c2ef3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to this project will be documented in this file. +## 2.6.25 - 2026-08-10 + +### Fixed + +- Screen group edit now keeps the slug locked after creation, so existing screen group URLs remain stable. + ## 2.6.24 - 2026-08-10 ### Fixed diff --git a/build/package.player.json b/build/package.player.json index abd59ca..bf2612f 100644 --- a/build/package.player.json +++ b/build/package.player.json @@ -1,6 +1,6 @@ { "name": "pulse-signage-player", - "version": "2.6.24", + "version": "2.6.25", "private": false, "description": "Pulse Signage player application bundle", "main": "src/common.js", diff --git a/build/package.web.json b/build/package.web.json index f6ab8b9..0c7f88c 100644 --- a/build/package.web.json +++ b/build/package.web.json @@ -1,6 +1,6 @@ { "name": "pulse-signage-web", - "version": "2.6.24", + "version": "2.6.25", "private": false, "description": "Pulse Signage web and bridge application bundle", "main": "src/common.js", diff --git a/package.json b/package.json index 63de599..33a6e24 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pulse-signage", - "version": "2.6.24", + "version": "2.6.25", "private": false, "description": "Pulse Signage application with MySQL and media storage", "repository": { diff --git a/src/player/public/js/player-page-playback.js b/src/player/public/js/player-page-playback.js index 129f769..0e21088 100644 --- a/src/player/public/js/player-page-playback.js +++ b/src/player/public/js/player-page-playback.js @@ -189,6 +189,15 @@ function refresh() { const nextActiveSlides = getActiveSlidesFrom(nextSlides); const nextFadeBetweenSlides = Boolean(data && data.playlist && data.playlist.fade_between_slides); const nextSkipUnavailableRtmp = Boolean(data && data.playlist && data.playlist.skip_unavailable_rtmp); + if (window.initialData && typeof window.initialData === 'object') { + window.initialData.screen = data.screen || window.initialData.screen || null; + window.initialData.playlist = data.playlist || null; + window.initialData.slides = nextSlides; + window.initialData.rssFeeds = Array.isArray(data.rssFeeds) ? data.rssFeeds : []; + window.initialData.apiSources = Array.isArray(data.apiSources) ? data.apiSources : []; + window.initialData.timetableGroups = Array.isArray(data.timetableGroups) ? data.timetableGroups : []; + window.initialData.revision = nextSignature; + } savePlaylistSnapshot({ slides: nextSlides, signature: nextSignature, diff --git a/src/web/routes/admin/manage.js b/src/web/routes/admin/manage.js index b11eb53..77992b3 100644 --- a/src/web/routes/admin/manage.js +++ b/src/web/routes/admin/manage.js @@ -26,10 +26,6 @@ module.exports = function registerManageRoutes(app, deps) { return text.slice(0, limit); } - function normalizeBaseUrl(value) { - return String(value || '').trim().replace(/\/$/, ''); - } - function normalizeExplicitPlayerBaseUrl(value) { return String(value || '').trim().replace(/\/$/, ''); } @@ -246,39 +242,24 @@ module.exports = function registerManageRoutes(app, deps) { if (await common.fetchDuplicateName(pool, 'd_screens', name, screen.id)) { return res.status(400).send('A screen with that name already exists.'); } - const slugInput = typeof common.validateMaxLength === 'function' - ? common.validateMaxLength(req.body.slug || '', SCREEN_SLUG_MAX_LENGTH, 'Screen URL') - : readLimitedText(req.body.slug || '', 255); const playlistId = req.body.playlist_id ? Number(req.body.playlist_id) : null; const previousPlaylistId = screen.playlist_id; - const slug = await common.uniqueScreenSlug(pool, common.slugify(slugInput || name), screen.id); + const slug = String(screen.slug || '').trim(); const previousSlug = String(screen.slug || '').trim(); await pool.query('UPDATE d_screens SET name = ?, slug = ?, playlist_id = ?, modified_by = ? WHERE id = ?', [name, slug, playlistId, getAuditUserId(req), screen.id]); if (previousPlaylistId !== playlistId && previousSlug) { await notifyPlayerScreens([previousSlug], 'refresh'); } if (previousSlug && previousSlug !== slug) { - const previousScreenTargets = await pool.query( - `SELECT s.slug, s.player_id, p.public_base_url, p.internal_base_url - FROM d_screens s - LEFT JOIN d_players p ON p.device_id = s.player_id - WHERE s.slug = ? - LIMIT 1`, - [previousSlug] - ); - const previousTargetRow = previousScreenTargets[0] && previousScreenTargets[0][0] || null; - const previousInternalBaseUrl = normalizeBaseUrl(previousTargetRow && previousTargetRow.internal_base_url) || ''; - const previousPublicBaseUrl = normalizeBaseUrl(previousTargetRow && previousTargetRow.public_base_url) || ''; - if (previousInternalBaseUrl && typeof forwardPlayerCommandToBaseUrl === 'function') { - await forwardPlayerCommandToBaseUrl(previousInternalBaseUrl, previousSlug, { - command: 'redirect', - url: previousPublicBaseUrl ? `${previousPublicBaseUrl}/screen/${encodeURIComponent(slug)}` : `/screen/${encodeURIComponent(slug)}` - }); + const previousConnections = await fetchLiveConnectionsForScreen(previousSlug); + const redirectPayload = { + command: 'redirect', + url: `/screen/${encodeURIComponent(slug)}` + }; + if (previousConnections.length) { + await forwardPlayerCommandForConnections(previousSlug, previousConnections, redirectPayload); } else { - await forwardPlayerCommand(previousSlug, { - command: 'redirect', - url: previousPublicBaseUrl ? `${previousPublicBaseUrl}/screen/${encodeURIComponent(slug)}` : `/screen/${encodeURIComponent(slug)}` - }); + await forwardPlayerCommand(previousSlug, redirectPayload); } } redirectAfterSave(req, res, '/screens?edit=' + screen.id, { diff --git a/src/web/views/signage/screens/form.hbs b/src/web/views/signage/screens/form.hbs index 93a3368..e9a9002 100644 --- a/src/web/views/signage/screens/form.hbs +++ b/src/web/views/signage/screens/form.hbs @@ -20,7 +20,7 @@