Prepare v2.0.0 release
This commit is contained in:
@@ -74,8 +74,8 @@ async function getOnboardingStatus(pool, deviceId) {
|
||||
|
||||
const [rows] = await pool.query(
|
||||
`SELECT d.device_id, d.client_name, d.screen_id, s.name AS screen_name, s.slug AS screen_slug, s.playlist_id
|
||||
FROM player_onboarding_devices d
|
||||
LEFT JOIN screens s ON s.id = d.screen_id
|
||||
FROM d_onboarding_devices d
|
||||
LEFT JOIN d_screens s ON s.id = d.screen_id
|
||||
WHERE d.device_id = ?`,
|
||||
[normalizedDeviceId]
|
||||
);
|
||||
@@ -99,7 +99,7 @@ async function commitDeviceBinding(pool, deviceId, clientName, screenSlug, isNam
|
||||
}
|
||||
|
||||
return withClientNameReservation(pool, normalizedClientName, async function () {
|
||||
const [screenRows] = await pool.query('SELECT id, name, slug FROM screens WHERE slug = ?', [normalizedScreenSlug]);
|
||||
const [screenRows] = await pool.query('SELECT id, name, slug FROM d_screens WHERE slug = ?', [normalizedScreenSlug]);
|
||||
if (!screenRows.length) {
|
||||
throw new Error('Screen not found.');
|
||||
}
|
||||
@@ -113,7 +113,7 @@ async function commitDeviceBinding(pool, deviceId, clientName, screenSlug, isNam
|
||||
}
|
||||
|
||||
await pool.query(
|
||||
'INSERT INTO player_onboarding_devices (device_id, client_name, screen_id) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE client_name = VALUES(client_name), screen_id = VALUES(screen_id), modified_at = CURRENT_TIMESTAMP',
|
||||
'INSERT INTO d_onboarding_devices (device_id, client_name, screen_id) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE client_name = VALUES(client_name), screen_id = VALUES(screen_id), modified_at = CURRENT_TIMESTAMP',
|
||||
[normalizedDeviceId, normalizedClientName, screen.id]
|
||||
);
|
||||
|
||||
@@ -218,7 +218,7 @@ function registerPlayerOnboardingRoutes(app, options) {
|
||||
|
||||
app.get('/api/onboarding/screens', requireOnboardingPageAuth, async function (_req, res, next) {
|
||||
try {
|
||||
const [rows] = await pool.query('SELECT id, name, slug FROM screens ORDER BY name ASC, id ASC');
|
||||
const [rows] = await pool.query('SELECT id, name, slug FROM d_screens ORDER BY name ASC, id ASC');
|
||||
res.json({ screens: rows });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
// ignore storage errors
|
||||
}
|
||||
if (socket && socket.readyState === WebSocket.OPEN) {
|
||||
sendCommandHello(socket);
|
||||
sendCommandState(socket);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -72,13 +72,13 @@
|
||||
}
|
||||
|
||||
// Announce the player to the command websocket.
|
||||
function sendCommandHello(socket) {
|
||||
function sendPlayerBootstrapState(socket) {
|
||||
if (!socket || socket.readyState !== WebSocket.OPEN) {
|
||||
return;
|
||||
}
|
||||
var clientName = getOnboardingClientName();
|
||||
socket.send(JSON.stringify({
|
||||
type: 'hello',
|
||||
type: 'state',
|
||||
clientId: getCommandClientId(),
|
||||
clientName: clientName || null,
|
||||
deviceId: getOnboardingDeviceId() || null,
|
||||
|
||||
+10
-17
@@ -56,7 +56,7 @@ function createPlayerPlaylistService(options) {
|
||||
|
||||
async function buildScreenPlaylist(slug) {
|
||||
try {
|
||||
const [screenRows] = await pool.query('SELECT id, name, slug, playlist_id, created_at, modified_at, created_by, modified_by FROM screens WHERE slug = ?', [slug]);
|
||||
const [screenRows] = await pool.query('SELECT id, name, slug, playlist_id, created_at, modified_at, created_by, modified_by FROM d_screens WHERE slug = ?', [slug]);
|
||||
if (!screenRows.length) {
|
||||
return { screen: null, playlist: null, slides: [], rssFeeds: [], apiSources: [] };
|
||||
}
|
||||
@@ -73,16 +73,16 @@ function createPlayerPlaylistService(options) {
|
||||
return payloadWithoutPlaylist;
|
||||
}
|
||||
|
||||
const [playlistRows] = await pool.query('SELECT id, name, fade_between_slides, skip_unavailable_rtmp, created_at, modified_at, created_by, modified_by FROM playlists WHERE id = ?', [screen.playlist_id]);
|
||||
const [playlistRows] = await pool.query('SELECT id, name, fade_between_slides, skip_unavailable_rtmp, created_at, modified_at, created_by, modified_by FROM c_playlists WHERE id = ?', [screen.playlist_id]);
|
||||
const playlist = playlistRows[0] || null;
|
||||
const [slideRows] = await pool.query(`
|
||||
SELECT sl.id, sl.title, sl.body, sl.template_id, sl.content_json, sl.media_path, sl.media_type, sl.created_at, sl.modified_at,
|
||||
SELECT sl.id, sl.title, sl.template_id, sl.content_json, sl.created_at, sl.modified_at,
|
||||
ps.position, ps.duration_seconds AS duration_seconds, ps.use_video_duration, ps.schedule_mode, ps.schedule_start_datetime, ps.schedule_end_datetime, ps.schedule_start_time, ps.schedule_end_time, ps.schedule_days_json,
|
||||
st.name AS template_name, cs.name AS canvas_size_name, cs.width AS canvas_size_width, cs.height AS canvas_size_height, cs.width AS canvas_width, cs.height AS canvas_height
|
||||
FROM playlist_slides ps
|
||||
JOIN slides sl ON sl.id = ps.slide_id
|
||||
LEFT JOIN slide_templates st ON st.id = sl.template_id
|
||||
LEFT JOIN canvas_sizes cs ON cs.id = st.canvas_size_id
|
||||
FROM c_playlist_slides ps
|
||||
JOIN c_slides sl ON sl.id = ps.slide_id
|
||||
LEFT JOIN c_templates st ON st.id = sl.template_id
|
||||
LEFT JOIN c_canvas_sizes cs ON cs.id = st.canvas_size_id
|
||||
WHERE ps.playlist_id = ?
|
||||
ORDER BY ps.position ASC, ps.id ASC
|
||||
`, [screen.playlist_id]);
|
||||
@@ -97,11 +97,11 @@ function createPlayerPlaylistService(options) {
|
||||
[templateRows] = await pool.query(`
|
||||
SELECT st.id, st.name, st.canvas_size_id, st.background_image_path, st.background_color, st.created_at, st.modified_at,
|
||||
cs.name AS canvas_size_name, cs.width AS canvas_size_width, cs.height AS canvas_size_height, cs.width AS canvas_width, cs.height AS canvas_height
|
||||
FROM slide_templates st
|
||||
LEFT JOIN canvas_sizes cs ON cs.id = st.canvas_size_id
|
||||
FROM c_templates st
|
||||
LEFT JOIN c_canvas_sizes cs ON cs.id = st.canvas_size_id
|
||||
WHERE st.id IN (?)
|
||||
`, [templateIds]);
|
||||
[regionRows] = await pool.query('SELECT id, template_id, region_key, region_type, label, font_family, lock_ratio, x, y, width, height, z_index, created_at, modified_at, created_by, modified_by FROM slide_template_regions WHERE template_id IN (?) ORDER BY template_id ASC, z_index ASC, id ASC', [templateIds]);
|
||||
[regionRows] = await pool.query('SELECT id, template_id, region_key, region_type, label, font_family, lock_ratio, x, y, width, height, z_index, created_at, modified_at, created_by, modified_by FROM c_template_regions WHERE template_id IN (?) ORDER BY template_id ASC, z_index ASC, id ASC', [templateIds]);
|
||||
templateRows.forEach(function (template) {
|
||||
template.regions = regionRows.filter(function (region) { return region.template_id === template.id; });
|
||||
templatesById[template.id] = template;
|
||||
@@ -144,7 +144,6 @@ function createPlayerPlaylistService(options) {
|
||||
return {
|
||||
id: slide.id,
|
||||
title: slide.title,
|
||||
body: slide.body,
|
||||
modified_at: slide.modified_at,
|
||||
duration_seconds: videoDuration || storedDuration,
|
||||
use_video_duration: Boolean(slide.use_video_duration),
|
||||
@@ -154,9 +153,6 @@ function createPlayerPlaylistService(options) {
|
||||
schedule_start_time: slide.schedule_start_time,
|
||||
schedule_end_time: slide.schedule_end_time,
|
||||
schedule_days_json: slide.schedule_days_json,
|
||||
media_url: slide.media_path,
|
||||
media_type: slide.media_type,
|
||||
kind: common.mediaKind(slide.media_path),
|
||||
template_id: slide.template_id,
|
||||
template: slide.template_id ? templatesById[slide.template_id] || null : null,
|
||||
content: content
|
||||
@@ -219,11 +215,8 @@ function createPlayerPlaylistService(options) {
|
||||
(Array.isArray(slideRows) ? slideRows : []).forEach(function (slide) {
|
||||
updatePlaylistRevisionHash(hash, slide.id);
|
||||
updatePlaylistRevisionHash(hash, slide.title);
|
||||
updatePlaylistRevisionHash(hash, slide.body);
|
||||
updatePlaylistRevisionHash(hash, slide.template_id);
|
||||
updatePlaylistRevisionHash(hash, slide.content_json);
|
||||
updatePlaylistRevisionHash(hash, slide.media_path);
|
||||
updatePlaylistRevisionHash(hash, slide.media_type);
|
||||
updatePlaylistRevisionHash(hash, slide.modified_at);
|
||||
updatePlaylistRevisionHash(hash, slide.position);
|
||||
updatePlaylistRevisionHash(hash, slide.duration_seconds);
|
||||
|
||||
@@ -381,11 +381,9 @@ function handleCommandMessage(rawMessage) {
|
||||
}
|
||||
return;
|
||||
case 'previous':
|
||||
case 'left':
|
||||
navigateSlides(-1);
|
||||
return;
|
||||
case 'next':
|
||||
case 'right':
|
||||
navigateSlides(1);
|
||||
return;
|
||||
case 'reload':
|
||||
@@ -424,11 +422,11 @@ function connectCommandSocket() {
|
||||
socket.onopen = function () {
|
||||
if (typeof syncOnboardingClientNameFromServer === 'function') {
|
||||
syncOnboardingClientNameFromServer(socket).then(function () {
|
||||
sendCommandHello(socket);
|
||||
sendCommandState(socket);
|
||||
});
|
||||
return;
|
||||
}
|
||||
sendCommandHello(socket);
|
||||
sendCommandState(socket);
|
||||
};
|
||||
|
||||
socket.onmessage = function (event) {
|
||||
|
||||
@@ -61,7 +61,7 @@ function substituteApiVariables(html, item) {
|
||||
|
||||
function getApiPreviewFallback(item) {
|
||||
if (!item || typeof item !== 'object') {
|
||||
return '<div class="template-region-placeholder">API item</div>';
|
||||
return '';
|
||||
}
|
||||
|
||||
var title = String(item.title || item.name || '').trim();
|
||||
@@ -74,7 +74,7 @@ function getApiPreviewFallback(item) {
|
||||
summary.push('<div>' + sanitizeRichText(description) + '</div>');
|
||||
}
|
||||
if (!summary.length) {
|
||||
return '<div class="template-region-placeholder">API item</div>';
|
||||
return '';
|
||||
}
|
||||
return summary.join('');
|
||||
}
|
||||
@@ -92,6 +92,9 @@ function renderApiRegion(region, regionContent) {
|
||||
body = getApiPreviewFallback(item);
|
||||
}
|
||||
var contentStyle = 'width:' + region.pixelWidth + 'px;height:' + region.pixelHeight + 'px;transform:scale(' + region.canvasScale + ');transform-origin:top left;' + (fontFamily ? 'font-family:' + escapeHtml(fontFamily) + ';' : '') + 'font-size:' + fontSize + 'px;color:' + escapeHtml(fontColor) + ';';
|
||||
var renderedBody = body ? renderEditorJsContent(body) : '<div class="template-region-placeholder">API item</div>';
|
||||
return '<div class="template-region api" style="' + region.baseStyle + '"><div class="template-region-text-scale" style="' + contentStyle + '">' + renderedBody + '</div></div>';
|
||||
if (!body) {
|
||||
return '';
|
||||
}
|
||||
var renderedBody = renderEditorJsContent(body);
|
||||
return renderedBody ? '<div class="template-region api" style="' + region.baseStyle + '"><div class="template-region-text-scale" style="' + contentStyle + '">' + renderedBody + '</div></div>' : '';
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
function renderHtmlRegionContent(value) {
|
||||
var html = String(value || '').trim();
|
||||
if (!html) {
|
||||
return '<div class="template-region-placeholder">HTML</div>';
|
||||
return '';
|
||||
}
|
||||
return '<iframe class="template-region-html-frame" sandbox="" scrolling="no" srcdoc="<!doctype html><html><head><style>html,body{margin:0;width:100%;height:100%;overflow:hidden;background:transparent;}</style></head><body>' + escapeHtml(html) + '</body></html>" title="HTML region" loading="eager"></iframe>';
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
function renderImageRegion(region, regionContent) {
|
||||
var src = regionContent.value || '';
|
||||
if (!String(src || '').trim()) {
|
||||
return '';
|
||||
}
|
||||
return '<div class="template-region image" style="' + region.baseStyle + '"><img src="' + escapeHtml(src) + '" alt="' + escapeHtml(region.label) + '" /></div>';
|
||||
}
|
||||
@@ -62,6 +62,9 @@ function renderRssRegion(region, regionContent) {
|
||||
body = summaryParts.join('');
|
||||
}
|
||||
var contentStyle = 'width:' + region.pixelWidth + 'px;height:' + region.pixelHeight + 'px;transform:scale(' + region.canvasScale + ');transform-origin:top left;' + (fontFamily ? 'font-family:' + escapeHtml(fontFamily) + ';' : '') + 'font-size:' + fontSize + 'px;color:' + escapeHtml(fontColor) + ';';
|
||||
var renderedBody = body ? renderEditorJsContent(body) : '<div class="template-region-placeholder">RSS item</div>';
|
||||
return '<div class="template-region rss" style="' + region.baseStyle + '"><div class="template-region-text-scale" style="' + contentStyle + '">' + renderedBody + '</div></div>';
|
||||
if (!body) {
|
||||
return '';
|
||||
}
|
||||
var renderedBody = renderEditorJsContent(body);
|
||||
return renderedBody ? '<div class="template-region rss" style="' + region.baseStyle + '"><div class="template-region-text-scale" style="' + contentStyle + '">' + renderedBody + '</div></div>' : '';
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ function renderRtmpRegion(region, regionContent) {
|
||||
var disableAudio = regionContent.disable_audio === undefined ? true : Boolean(regionContent.disable_audio);
|
||||
var skipUnavailable = Boolean(currentPlaylistSkipUnavailableRtmp);
|
||||
if (!url) {
|
||||
return '<div class="template-region rtmp" style="' + region.baseStyle + '"><div class="template-region-placeholder">RTMP stream</div></div>';
|
||||
return '';
|
||||
}
|
||||
|
||||
return '<div class="template-region rtmp" style="' + region.baseStyle + '"><video class="template-region-rtmp-video" data-rtmp-source="' + escapeHtml(url) + '" data-rtmp-disable-audio="' + (disableAudio ? '1' : '0') + '" data-rtmp-skip-unavailable="' + (skipUnavailable ? '1' : '0') + '" autoplay playsinline preload="auto" tabindex="-1" disablepictureinpicture></video><div class="template-region-placeholder template-region-rtmp-placeholder">Loading RTMP stream...</div></div>';
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
function renderTextRegion(region, regionContent) {
|
||||
var rawValue = regionContent && regionContent.value !== undefined ? regionContent.value : '';
|
||||
if (!String(rawValue || '').trim()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
var fontFamily = sanitizeFontFamily(regionContent.font_family || region.fontFamily);
|
||||
var fontSize = sanitizeFontSize(regionContent.font_size || region.fontSize);
|
||||
var fontColor = sanitizeTextColor(regionContent.font_color || region.fontColor);
|
||||
var contentStyle = 'width:' + region.pixelWidth + 'px;height:' + region.pixelHeight + 'px;transform:scale(' + region.canvasScale + ');transform-origin:top left;' + (fontFamily ? 'font-family:' + escapeHtml(fontFamily) + ';' : '') + 'font-size:' + fontSize + 'px;color:' + escapeHtml(fontColor) + ';';
|
||||
return '<div class="template-region text" style="' + region.baseStyle + '"><div class="template-region-text-scale" style="' + contentStyle + '">' + renderEditorJsContent(regionContent.value || '') + '</div></div>';
|
||||
var renderedBody = renderEditorJsContent(rawValue);
|
||||
return renderedBody ? '<div class="template-region text" style="' + region.baseStyle + '"><div class="template-region-text-scale" style="' + contentStyle + '">' + renderedBody + '</div></div>' : '';
|
||||
}
|
||||
@@ -111,7 +111,7 @@ function renderVideoRegion(region, regionContent) {
|
||||
var cachedSrcVersioned = appendCacheBust(cachedSrc, regionContent && regionContent.cache_bust);
|
||||
|
||||
if (!requestedSrc) {
|
||||
return '<div class="template-region video" style="' + region.baseStyle + '"><div class="template-region-placeholder">Video</div></div>';
|
||||
return '';
|
||||
}
|
||||
|
||||
if (isDirectlyRenderableSource(requestedSrc)) {
|
||||
@@ -141,5 +141,5 @@ function renderVideoRegion(region, regionContent) {
|
||||
return '<div class="template-region video" style="' + region.baseStyle + '"><video src="' + escapeHtml(cachedSrcVersioned) + '" title="' + escapeHtml(region.label) + '" autoplay muted loop playsinline preload="auto" disablepictureinpicture oncanplay="this.play().catch(function () {})" onloadedmetadata="this.play().catch(function () {})"></video></div>';
|
||||
}
|
||||
|
||||
return '<div class="template-region video" style="' + region.baseStyle + '"><div class="template-region-placeholder">Video</div></div>';
|
||||
return '';
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
function renderWebpageRegion(region, regionContent) {
|
||||
var url = String(regionContent.value || '').trim();
|
||||
var iframe = url ? '<iframe src="' + escapeHtml(url) + '" title="' + escapeHtml(region.label) + '" loading="eager" referrerpolicy="no-referrer" scrolling="no"></iframe>' : '<div class="template-region-placeholder">Webpage</div>';
|
||||
return '<div class="template-region webpage" style="' + region.baseStyle + '">' + iframe + '</div>';
|
||||
if (!url) {
|
||||
return '';
|
||||
}
|
||||
return '<div class="template-region webpage" style="' + region.baseStyle + '"><iframe src="' + escapeHtml(url) + '" title="' + escapeHtml(region.label) + '" loading="eager" referrerpolicy="no-referrer" scrolling="no"></iframe></div>';
|
||||
}
|
||||
@@ -279,7 +279,7 @@ function registerPlayerRoutes(app, options) {
|
||||
let screen = null;
|
||||
let screenLookupFailed = false;
|
||||
try {
|
||||
const [screenRows] = await pool.query('SELECT id, name, slug FROM screens WHERE slug = ?', [req.params.slug]);
|
||||
const [screenRows] = await pool.query('SELECT id, name, slug FROM d_screens WHERE slug = ?', [req.params.slug]);
|
||||
screen = screenRows[0] || null;
|
||||
} catch (error) {
|
||||
screenLookupFailed = isTransientDbError(error);
|
||||
@@ -309,7 +309,7 @@ function registerPlayerRoutes(app, options) {
|
||||
if (!command) {
|
||||
return res.status(400).json({ error: 'Command is required' });
|
||||
}
|
||||
if (['refresh', 'reload', 'redirect', 'pause', 'blackout', 'previous', 'next', 'left', 'right', 'setclientname'].indexOf(command) === -1) {
|
||||
if (['refresh', 'reload', 'redirect', 'pause', 'blackout', 'previous', 'next', 'setclientname'].indexOf(command) === -1) {
|
||||
return res.status(400).json({ error: 'Unsupported command' });
|
||||
}
|
||||
|
||||
@@ -319,7 +319,7 @@ function registerPlayerRoutes(app, options) {
|
||||
let screenLookupFailed = false;
|
||||
if (!isRedirectCommand) {
|
||||
try {
|
||||
const [screenRows] = await pool.query('SELECT id, name, slug FROM screens WHERE slug = ?', [req.params.slug]);
|
||||
const [screenRows] = await pool.query('SELECT id, name, slug FROM d_screens WHERE slug = ?', [req.params.slug]);
|
||||
screen = screenRows[0] || null;
|
||||
} catch (error) {
|
||||
screenLookupFailed = isTransientDbError(error);
|
||||
|
||||
@@ -328,7 +328,7 @@ function createPlayerRuntime(options) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!payload || (payload.type !== 'hello' && payload.type !== 'state')) {
|
||||
if (!payload || payload.type !== 'state') {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user