(function () { 'use strict'; var POLL_INTERVAL_MS = 1000; var OVERVIEW_MAX_LENGTH = 160; var overlayEl = null; var personCache = {}; function truncate(text, maxLength) { if (text.length <= maxLength) { return text; } return text.slice(0, maxLength).trim() + '…'; } function getPerson(jellyfinId) { if (personCache[jellyfinId]) { return personCache[jellyfinId]; } var promise = window.ApiClient.getItem(window.ApiClient.getCurrentUserId(), jellyfinId) .catch(function () { return null; }); personCache[jellyfinId] = promise; return promise; } function getNowPlaying() { if (!window.ApiClient) { return Promise.reject(new Error('no ApiClient')); } var url = window.ApiClient.getUrl('Sessions', { DeviceId: window.ApiClient.deviceId() }); return window.ApiClient.ajax({ url: url, type: 'GET', dataType: 'json' }).then(function (sessions) { var session = sessions && sessions[0]; if (!session || !session.NowPlayingItem || !session.PlayState) { return null; } return { itemId: session.NowPlayingItem.Id, positionSeconds: (session.PlayState.PositionTicks || 0) / 10000000 }; }); } function removeOverlay() { if (overlayEl && overlayEl.parentNode) { overlayEl.parentNode.removeChild(overlayEl); } overlayEl = null; } function showOverlay(video, actors) { removeOverlay(); if (!actors || actors.length === 0) { return; } var container = video.parentElement; if (!container) { return; } overlayEl = document.createElement('div'); overlayEl.className = 'jrayOverlay'; overlayEl.style.position = 'absolute'; overlayEl.style.bottom = '10%'; overlayEl.style.left = '2%'; overlayEl.style.zIndex = '9999'; overlayEl.style.display = 'flex'; overlayEl.style.flexWrap = 'wrap'; overlayEl.style.gap = '12px'; overlayEl.style.pointerEvents = 'none'; actors.forEach(function (actor) { var card = document.createElement('div'); card.className = 'jrayActorCard'; card.style.background = 'rgba(0, 0, 0, 0.75)'; card.style.color = '#fff'; card.style.padding = '8px 12px'; card.style.borderRadius = '6px'; card.style.display = 'flex'; card.style.alignItems = 'center'; card.style.gap = '10px'; card.style.maxWidth = '320px'; var textBlock = document.createElement('div'); var name = document.createElement('div'); name.style.fontWeight = 'bold'; name.style.fontSize = '14px'; name.textContent = actor.name; textBlock.appendChild(name); card.appendChild(textBlock); overlayEl.appendChild(card); if (!actor.jellyfin_id || !window.ApiClient) { return; } card.style.pointerEvents = 'auto'; card.style.cursor = 'pointer'; card.addEventListener('click', function (event) { event.stopPropagation(); window.location.hash = '#/details?id=' + actor.jellyfin_id; }); getPerson(actor.jellyfin_id).then(function (person) { if (!person || !overlayEl || !overlayEl.contains(card)) { return; } if (person.ImageTags && person.ImageTags.Primary) { var img = document.createElement('img'); img.src = window.ApiClient.getImageUrl(actor.jellyfin_id, { type: 'Primary', maxHeight: 80, tag: person.ImageTags.Primary }); img.style.height = '80px'; img.style.width = 'auto'; img.style.borderRadius = '4px'; img.style.objectFit = 'cover'; card.insertBefore(img, textBlock); } if (person.Overview) { var overview = document.createElement('div'); overview.style.fontSize = '12px'; overview.style.opacity = '0.85'; overview.style.marginTop = '4px'; overview.textContent = truncate(person.Overview, OVERVIEW_MAX_LENGTH); textBlock.appendChild(overview); } }); }); container.appendChild(overlayEl); } function fetchContext(itemId, t) { if (!window.ApiClient) { return; } var url = window.ApiClient.getUrl('Plugins/JRay/Items/' + itemId + '/jray', { t: t }); window.ApiClient.ajax({ url: url, type: 'GET', dataType: 'json' }).then(function (context) { var video = document.querySelector('video'); if (!video || !video.paused) { return; } showOverlay(video, context.actors); }, function () { // No truth data (404) or request error - fail silently, never break playback. }); } function onPause() { getNowPlaying().then(function (info) { if (info) { fetchContext(info.itemId, info.positionSeconds); } }, function () { // ApiClient unavailable or request error - fail silently, never break playback. }); } function onPlay() { removeOverlay(); } function attach(video) { if (video.dataset.jrayAttached) { return; } video.dataset.jrayAttached = 'true'; video.addEventListener('pause', onPause); video.addEventListener('play', onPlay); video.addEventListener('playing', onPlay); video.addEventListener('seeking', removeOverlay); } setInterval(function () { var video = document.querySelector('video'); if (video) { attach(video); } else { removeOverlay(); } }, POLL_INTERVAL_MS); })();