const crypto = require('crypto'); function createPlayerPlaylistService(options) { const pool = options && options.pool ? options.pool : null; const common = options && options.common ? options.common : null; if (!pool) { throw new Error('pool is required'); } if (!common) { throw new Error('common is required'); } async function buildScreenPlaylist(slug) { const [screenRows] = await pool.query('SELECT id, name, slug, playlist_id, created_at, modified_at, created_by, modified_by FROM screens WHERE slug = ?', [slug]); if (!screenRows.length) { return { screen: null, playlist: null, slides: [] }; } const screen = screenRows[0]; if (!screen.playlist_id) { return { screen: screen, playlist: null, slides: [], revision: getPlaylistRevision(screen, null, [], [], []) }; } const [playlistRows] = await pool.query('SELECT id, name, fade_between_slides, created_at, modified_at, created_by, modified_by FROM 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, ps.position, ps.duration_seconds AS duration_seconds, 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 WHERE ps.playlist_id = ? ORDER BY ps.position ASC, ps.id ASC `, [screen.playlist_id]); const templateIds = slideRows .filter(function (slide) { return slide.template_id; }) .map(function (slide) { return slide.template_id; }); const templatesById = {}; let templateRows = []; let regionRows = []; if (templateIds.length) { [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 WHERE st.id IN (?) `, [templateIds]); [regionRows] = await pool.query('SELECT id, template_id, region_key, region_type, label, font_family, 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]); templateRows.forEach(function (template) { template.regions = regionRows.filter(function (region) { return region.template_id === template.id; }); templatesById[template.id] = template; }); } const slides = slideRows.map(function (slide) { return { id: slide.id, title: slide.title, body: slide.body, duration_seconds: slide.duration_seconds, schedule_mode: slide.schedule_mode, schedule_start_datetime: slide.schedule_start_datetime, schedule_end_datetime: slide.schedule_end_datetime, 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: common.parseJsonSafe(slide.content_json) || {} }; }); const revision = getPlaylistRevision(screen, playlist, slideRows, templateRows, regionRows); return { screen: screen, playlist: playlist, slides: slides, revision: revision }; } function updatePlaylistRevisionHash(hash, value) { hash.update(String(value === null || value === undefined ? '' : value)); hash.update('\0'); } function getPlaylistRevision(screen, playlist, slideRows, templateRows, regionRows) { const hash = crypto.createHash('sha1'); updatePlaylistRevisionHash(hash, screen && screen.id); updatePlaylistRevisionHash(hash, screen && screen.playlist_id); updatePlaylistRevisionHash(hash, screen && screen.modified_at); updatePlaylistRevisionHash(hash, playlist && playlist.id); updatePlaylistRevisionHash(hash, playlist && playlist.modified_at); updatePlaylistRevisionHash(hash, playlist && playlist.fade_between_slides); (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); updatePlaylistRevisionHash(hash, slide.schedule_mode); updatePlaylistRevisionHash(hash, slide.schedule_start_datetime); updatePlaylistRevisionHash(hash, slide.schedule_end_datetime); updatePlaylistRevisionHash(hash, slide.schedule_start_time); updatePlaylistRevisionHash(hash, slide.schedule_end_time); updatePlaylistRevisionHash(hash, slide.schedule_days_json); }); (Array.isArray(templateRows) ? templateRows : []).forEach(function (template) { updatePlaylistRevisionHash(hash, template.id); updatePlaylistRevisionHash(hash, template.name); updatePlaylistRevisionHash(hash, template.canvas_size_id); updatePlaylistRevisionHash(hash, template.canvas_size_width); updatePlaylistRevisionHash(hash, template.canvas_size_height); updatePlaylistRevisionHash(hash, template.background_image_path); updatePlaylistRevisionHash(hash, template.background_color); updatePlaylistRevisionHash(hash, template.modified_at); }); (Array.isArray(regionRows) ? regionRows : []).forEach(function (region) { updatePlaylistRevisionHash(hash, region.id); updatePlaylistRevisionHash(hash, region.template_id); updatePlaylistRevisionHash(hash, region.region_key); updatePlaylistRevisionHash(hash, region.region_type); updatePlaylistRevisionHash(hash, region.label); updatePlaylistRevisionHash(hash, region.font_family); updatePlaylistRevisionHash(hash, region.x); updatePlaylistRevisionHash(hash, region.y); updatePlaylistRevisionHash(hash, region.width); updatePlaylistRevisionHash(hash, region.height); updatePlaylistRevisionHash(hash, region.z_index); updatePlaylistRevisionHash(hash, region.modified_at); }); return hash.digest('hex'); } return { buildScreenPlaylist: buildScreenPlaylist }; } module.exports = { createPlayerPlaylistService: createPlayerPlaylistService };