// Screen slug helpers and screen lookup utilities. function slugify(value) { return String(value || '') .toLowerCase() .trim() .replace(/[^a-z0-9]+/g, '-') .replace(/^-+|-+$/g, '') .replace(/-{2,}/g, '-'); } function normalizePlayerBaseUrl(value) { return String(value || '').trim().replace(/\/$/, ''); } function getConfiguredPlayerIdentifier() { return String(process.env.PLAYER_IDENTIFIER || process.env.PLAYER_DEVICE_ID || '').trim() || null; } async function fetchPlayerRecordByIdentifier(pool, identifier) { const normalizedIdentifier = String(identifier || '').trim(); if (!normalizedIdentifier) { return null; } const [rows] = await pool.query( `SELECT id, identifier, public_base_url, internal_base_url, last_seen_at FROM d_players WHERE identifier = ? LIMIT 1`, [normalizedIdentifier] ); return rows[0] || null; } function buildScreenPlayerUrl(screen, fallbackBaseUrl) { const slug = String(screen && screen.slug || '').trim(); if (!slug) { return null; } const baseUrl = normalizePlayerBaseUrl(fallbackBaseUrl); if (!baseUrl) { return null; } return `${baseUrl}/screen/${encodeURIComponent(slug)}`; } async function fetchScreenPlayerUrls(pool) { const playerBaseUrl = await fetchPlayerPublicBaseUrl(pool); if (!playerBaseUrl) { return {}; } const [rows] = await pool.query(` SELECT slug FROM d_screens ORDER BY slug ASC `); const playerUrls = {}; rows.forEach(function (row) { const slug = String(row && row.slug || '').trim(); const playerUrl = buildScreenPlayerUrl({ slug: slug }, playerBaseUrl); if (slug && playerUrl && !playerUrls[slug]) { playerUrls[slug] = playerUrl; } }); return playerUrls; } async function fetchPlayerPublicBaseUrl(pool) { const configuredPlayerIdentifier = getConfiguredPlayerIdentifier(); const [rows] = await pool.query(` SELECT public_base_url FROM d_players WHERE identifier = ? LIMIT 1 `, [configuredPlayerIdentifier]); return normalizePlayerBaseUrl(rows[0] && rows[0].public_base_url) || null; } async function fetchScreenPlayerRecord(pool, slug) { if (slug) { const [rows] = await pool.query( `SELECT slug FROM d_screens WHERE slug = ? LIMIT 1`, [slug] ); if (!rows.length) { return null; } } return fetchPlayerRecordByIdentifier(pool, getConfiguredPlayerIdentifier()); } async function uniqueScreenSlug(pool, baseSlug, excludeId) { const start = baseSlug || `screen-${Date.now()}`; let candidate = start; let counter = 2; while (true) { const params = [candidate]; let sql = 'SELECT id FROM d_screens WHERE slug = ?'; if (excludeId !== undefined && excludeId !== null) { sql += ' AND id <> ?'; params.push(excludeId); } const [rows] = await pool.query(sql, params); if (!rows.length) { return candidate; } candidate = `${start}-${counter}`; counter += 1; } } async function fetchScreenById(pool, id) { const [rows] = await pool.query(` SELECT s.id, s.name, s.slug, s.playlist_id, s.created_at, s.modified_at, s.created_by, s.modified_by, p.name AS playlist_name FROM d_screens s LEFT JOIN c_playlists p ON p.id = s.playlist_id WHERE s.id = ? `, [id]); return rows[0] || null; } async function fetchScreenEditData(pool) { const [playlists] = await pool.query('SELECT id, name, fade_between_slides, skip_unavailable_rtmp, created_at, modified_at, created_by, modified_by FROM c_playlists ORDER BY id DESC'); return { playlists }; } module.exports = { slugify, normalizePlayerBaseUrl, getConfiguredPlayerIdentifier, fetchPlayerRecordByIdentifier, buildScreenPlayerUrl, fetchScreenPlayerUrls, fetchPlayerPublicBaseUrl, fetchScreenPlayerRecord, uniqueScreenSlug, fetchScreenById, fetchScreenEditData };