diff --git a/dist/plex-meets-homeassistant.js b/dist/plex-meets-homeassistant.js index 6c5964d..e83cc03 100644 --- a/dist/plex-meets-homeassistant.js +++ b/dist/plex-meets-homeassistant.js @@ -18674,6 +18674,9 @@ class Plex { this.clients = []; this.requestTimeout = 10000; this.sections = []; + this.providers = []; + this.livetv = {}; + this.livetvepg = {}; this.collections = false; this.playlists = []; this.init = async () => { @@ -18692,6 +18695,67 @@ class Plex { } return this.clients; }; + this.getProviders = async () => { + if (lodash.isEmpty(this.providers)) { + const url = this.authorizeURL(`${this.getBasicURL()}/media/providers`); + const providersData = await axios.get(url, { + timeout: this.requestTimeout + }); + this.providers = providersData.data.MediaContainer.MediaProvider; + } + return this.providers; + }; + this.getLiveTV = async () => { + if (lodash.isEmpty(this.livetv)) { + const returnData = {}; + const providers = await this.getProviders(); + const liveTVRequests = []; + const liveTVRequestsNames = []; + lodash.forEach(providers, provider => { + if (lodash.isEqual(provider.protocols, 'livetv')) { + const url = this.authorizeURL(`${this.getBasicURL()}/${provider.identifier}/tags?type=310`); + liveTVRequests.push(axios.get(url, { + timeout: this.requestTimeout + })); + liveTVRequestsNames.push(provider.title); + } + }); + const allResults = await Promise.all(liveTVRequests); + lodash.forEach(allResults, (result, key) => { + returnData[liveTVRequestsNames[key]] = result.data.MediaContainer.Directory; + }); + this.livetv = returnData; + } + return this.livetv; + }; + this.getEPG = async () => { + if (lodash.isEmpty(this.livetvepg)) { + const returnData = {}; + const providers = await this.getProviders(); + const liveTVRequests = []; + const liveTVRequestsNames = []; + lodash.forEach(providers, provider => { + if (lodash.isEqual(provider.protocols, 'livetv')) { + let url = this.authorizeURL(`${this.getBasicURL()}/${provider.identifier}/grid?type=1&sort=beginsAt`); + url += `&endsAt>=${Math.floor(Date.now() / 1000)}`; + url += `&beginsAt<=${Math.floor(Date.now() / 1000)}`; + liveTVRequests.push(axios.get(url, { + timeout: this.requestTimeout + })); + liveTVRequestsNames.push(provider.title); + } + }); + const allResults = await Promise.all(liveTVRequests); + lodash.forEach(allResults, (result, key) => { + returnData[liveTVRequestsNames[key]] = {}; + lodash.forEach(result.data.MediaContainer.Metadata, data => { + returnData[liveTVRequestsNames[key]][data.Media[0].channelCallSign] = data; + }); + }); + this.livetvepg = returnData; + } + return this.livetvepg; + }; this.getServerID = async () => { if (lodash.isEmpty(this.serverInfo)) { await this.getServerInfo(); @@ -19228,26 +19292,34 @@ class PlayController { } } const entity = this.getPlayService(data); + let processData = data; + let provider; + if (!lodash.isNil(data.epg)) { + processData = data.epg; + provider = ''; + } switch (entity.key) { case 'kodi': - await this.playViaKodi(entity.value, data, data.type); + await this.playViaKodi(entity.value, processData, processData.type); break; case 'androidtv': - await this.playViaAndroidTV(entity.value, data.key.split('/')[3], instantPlay); + await this.playViaAndroidTV(entity.value, processData.key, instantPlay, provider); break; case 'plexPlayer': - await this.playViaPlexPlayer(entity.value, data.key.split('/')[3]); + await this.playViaPlexPlayer(entity.value, processData.key.split('/')[3]); break; case 'cast': if (this.hass.services.plex) { - const libraryName = lodash.isNil(data.librarySectionTitle) ? this.libraryName : data.librarySectionTitle; + const libraryName = lodash.isNil(processData.librarySectionTitle) + ? this.libraryName + : processData.librarySectionTitle; try { - switch (data.type) { + switch (processData.type) { case 'movie': await this.playViaCastPlex(entity.value, 'movie', `plex://${JSON.stringify({ // eslint-disable-next-line @typescript-eslint/camelcase library_name: libraryName, - title: data.title + title: processData.title })}`); break; case 'episode': @@ -19255,28 +19327,28 @@ class PlayController { // eslint-disable-next-line @typescript-eslint/camelcase library_name: libraryName, // eslint-disable-next-line @typescript-eslint/camelcase - show_name: data.grandparentTitle, + show_name: processData.grandparentTitle, // eslint-disable-next-line @typescript-eslint/camelcase - season_number: data.parentIndex, + season_number: processData.parentIndex, // eslint-disable-next-line @typescript-eslint/camelcase - episode_number: data.index + episode_number: processData.index })}`); break; default: - this.playViaCast(entity.value, data.Media[0].Part[0].key); + this.playViaCast(entity.value, processData.Media[0].Part[0].key); } } catch (err) { console.log(err); - this.playViaCast(entity.value, data.Media[0].Part[0].key); + this.playViaCast(entity.value, processData.Media[0].Part[0].key); } } else { - this.playViaCast(entity.value, data.Media[0].Part[0].key); + this.playViaCast(entity.value, processData.Media[0].Part[0].key); } break; default: - throw Error(`No service available to play ${data.title}!`); + throw Error(`No service available to play ${processData.title}!`); } if (lodash.isArray(this.runAfter)) { await this.hass.callService(this.runAfter[0], this.runAfter[1], {}); @@ -19417,13 +19489,13 @@ class PlayController { media_content_id: mediaLink }); }; - this.playViaAndroidTV = async (entityName, mediaID, instantPlay = false) => { + this.playViaAndroidTV = async (entityName, mediaID, instantPlay = false, provider = 'com.plexapp.plugins.library') => { const serverID = await this.plex.getServerID(); let command = `am start`; if (instantPlay) { command += ' --ez "android.intent.extra.START_PLAYBACK" true'; } - command += ` -a android.intent.action.VIEW 'plex://server://${serverID}/com.plexapp.plugins.library/library/metadata/${mediaID}'`; + command += ` -a android.intent.action.VIEW 'plex://server://${serverID}/${provider}${mediaID}'`; this.hass.callService('androidtv', 'adb_command', { // eslint-disable-next-line @typescript-eslint/camelcase entity_id: entityName, @@ -19611,6 +19683,7 @@ class PlexMeetsHomeAssistantEditor extends HTMLElement { this.entitiesRegistry = false; this.plexValidSection = document.createElement('div'); this.loaded = false; + this.livetv = {}; this.fireEvent = (node, type, detail, options = {}) => { // eslint-disable-next-line no-param-reassign detail = detail === null || detail === undefined ? {} : detail; @@ -19820,10 +19893,14 @@ class PlexMeetsHomeAssistantEditor extends HTMLElement { this.libraryName.appendChild(libraryItems); this.libraryName.style.width = '100%'; this.libraryName.addEventListener('value-changed', this.valueUpdated); + const warningLibrary = document.createElement('div'); + warningLibrary.style.color = 'red'; this.content.appendChild(this.libraryName); + this.content.appendChild(warningLibrary); this.appendChild(this.content); this.plex = new Plex(this.config.ip, this.plexPort, this.config.token, this.plexProtocol, this.config.sort); this.sections = await this.plex.getSections(); + this.livetv = await this.plex.getLiveTV(); this.collections = await this.plex.getCollections(); this.playlists = await this.plex.getPlaylists(); this.clients = await this.plex.getClients(); @@ -20033,6 +20110,15 @@ class PlexMeetsHomeAssistantEditor extends HTMLElement { this.runAfter.addEventListener('value-changed', this.valueUpdated); this.runAfter.value = this.config.runAfter; this.plexValidSection.appendChild(this.runAfter); + if (!lodash.isEmpty(this.livetv)) { + libraryItems.appendChild(addDropdownItem('Live TV', true)); + lodash.forEach(lodash.keys(this.livetv), (livetv) => { + if (lodash.isEqual(this.config.libraryName, livetv)) { + warningLibrary.textContent = `Warning: ${this.config.libraryName} play action currently not supported by Plex.`; + } + libraryItems.appendChild(addDropdownItem(livetv)); + }); + } if (!lodash.isEmpty(this.sections)) { libraryItems.appendChild(addDropdownItem('Libraries', true)); lodash.forEach(this.sections, (section) => { @@ -20911,6 +20997,7 @@ class PlexMeetsHomeAssistant extends HTMLElement { this.searchInputElem = document.createElement('input'); this.plexProtocol = 'http'; this.plexPort = false; + this.epgData = {}; this.detailsShown = false; this.entityRegistry = []; this.runBefore = ''; @@ -21185,6 +21272,19 @@ class PlexMeetsHomeAssistant extends HTMLElement { } } }; + const getLiveTV = async () => { + if (this.plex) { + const liveTV = await this.plex.getLiveTV(); + lodash.forEach(liveTV, (data, key) => { + this.data[key] = data; + }); + } + }; + const getEPG = async () => { + if (this.plex) { + this.epgData = await this.plex.getEPG(); + } + }; let sectionKey = 0; lodash.forEach(plexAllSections, (section) => { if (lodash.isEqual(section.title, this.config.libraryName)) { @@ -21209,7 +21309,17 @@ class PlexMeetsHomeAssistant extends HTMLElement { else if (lodash.isEqual(this.config.libraryName, 'Recently Added')) { loadDataRequests.push(getRecentyAdded()); } + loadDataRequests.push(getLiveTV()); + loadDataRequests.push(getEPG()); const [plexSections] = await Promise.all(loadDataRequests); + lodash.forEach(this.epgData, (value, key) => { + lodash.forEach(this.data[key], (libraryData, libraryKey) => { + if (!lodash.isNil(this.epgData[key][libraryData.channelCallSign])) { + this.data[key][libraryKey].epg = this.epgData[key][libraryData.channelCallSign]; + this.data[key][libraryKey].type = 'epg'; + } + }); + }); if (plexSections && sectionKey) { lodash.forEach(plexSections, section => { this.data[section.title1] = section.Metadata; @@ -21662,8 +21772,14 @@ class PlexMeetsHomeAssistant extends HTMLElement { this.activeMovieElem = undefined; for (let i = 0; i < this.movieElems.length; i += 1) { if (parseInt(this.movieElems[i].style.width, 10) > CSS_STYLE.width) { - this.movieElems[i].style.width = `${CSS_STYLE.width}px`; - this.movieElems[i].style.height = `${CSS_STYLE.height}px`; + if (lodash.isEqual(this.movieElems[i].style.width, this.movieElems[i].style.height)) { + this.movieElems[i].style.width = `${CSS_STYLE.width}px`; + this.movieElems[i].style.height = `${CSS_STYLE.width}px`; + } + else { + this.movieElems[i].style.width = `${CSS_STYLE.width}px`; + this.movieElems[i].style.height = `${CSS_STYLE.height}px`; + } this.movieElems[i].style['z-index'] = 1; this.movieElems[i].style.position = 'absolute'; this.movieElems[i].style.left = `${this.movieElems[i].dataset.left}px`; @@ -21810,8 +21926,21 @@ class PlexMeetsHomeAssistant extends HTMLElement { genreElem.parentElement.style.display = 'none'; } } - this.getElementsByClassName('detailsTitle')[0].innerHTML = escapeHtml(mainData.title); - this.getElementsByClassName('detailsYear')[0].innerHTML = escapeHtml(mainData.year); + if (!lodash.isNil(mainData.channelCallSign)) { + this.getElementsByClassName('detailsTitle')[0].innerHTML = escapeHtml(mainData.channelCallSign); + } + else { + this.getElementsByClassName('detailsTitle')[0].innerHTML = escapeHtml(mainData.title); + } + if (!lodash.isNil(mainData.year)) { + this.getElementsByClassName('detailsYear')[0].innerHTML = escapeHtml(mainData.year); + } + else if (!lodash.isNil(mainData.epg) && !lodash.isNil(mainData.epg.title)) { + this.getElementsByClassName('detailsYear')[0].innerHTML = escapeHtml(mainData.epg.title); + } + else { + this.getElementsByClassName('detailsYear')[0].innerHTML = ''; + } this.getElementsByClassName('metaInfo')[0].innerHTML = `${(mainData.duration !== undefined ? `${Math.round(parseInt(escapeHtml(mainData.duration), 10) / 60 / 1000)} min` : '') + @@ -21821,7 +21950,15 @@ class PlexMeetsHomeAssistant extends HTMLElement { (mainData.rating !== undefined ? ` ` : '')}
`; - this.getElementsByClassName('detailDesc')[0].innerHTML = escapeHtml(mainData.summary); + if (!lodash.isNil(mainData.summary)) { + this.getElementsByClassName('detailDesc')[0].innerHTML = escapeHtml(mainData.summary); + } + else if (!lodash.isNil(mainData.epg) && !lodash.isNil(mainData.epg.summary)) { + this.getElementsByClassName('detailDesc')[0].innerHTML = escapeHtml(mainData.epg.summary); + } + else { + this.getElementsByClassName('detailDesc')[0].innerHTML = ''; + } /* todo temp disabled if (data.type === 'movie') { (this.detailElem.children[5] as HTMLElement).style.visibility = 'visible'; @@ -21843,97 +21980,100 @@ class PlexMeetsHomeAssistant extends HTMLElement { else if (data.childCount > 0) { seasonsData = await this.plex.getLibraryData(data.key.split('/')[3]); } - const dataDetails = await this.plex.getDetails(data.key.split('/')[3]); - if (this.videoElem) { - const art = this.plex.authorizeURL(this.plex.getBasicURL() + data.art); - const trailerURL = findTrailerURL(dataDetails); - if (trailerURL !== '' && !lodash.isEqual(this.playTrailer, false)) { - const videoPlayer = this.getElementsByClassName('videoPlayer')[0]; - const video = document.createElement('video'); - video.style.height = '100%'; - video.style.width = '100%'; - video.controls = false; - if (lodash.isEqual(this.playTrailer, 'muted')) { - video.muted = true; - } - const source = document.createElement('source'); - source.type = 'video/mp4'; - source.src = this.plex.authorizeURL(`${this.plex.getBasicURL()}${dataDetails.Extras.Metadata[0].Media[0].Part[0].key}`); - video.appendChild(source); - videoPlayer.appendChild(video); - video.load(); - video.play(); - let playingFired = false; - const videobgs1 = this.getElementsByClassName('videobg1'); - const videobgs2 = this.getElementsByClassName('videobg2'); - video.addEventListener('click', event => { - if (isVideoFullScreen(this)) { - event.stopPropagation(); + let dataDetails = {}; + if (!lodash.isNil(data.key)) { + dataDetails = await this.plex.getDetails(data.key.split('/')[3]); + if (this.videoElem) { + const art = this.plex.authorizeURL(this.plex.getBasicURL() + data.art); + const trailerURL = findTrailerURL(dataDetails); + if (trailerURL !== '' && !lodash.isEqual(this.playTrailer, false)) { + const videoPlayer = this.getElementsByClassName('videoPlayer')[0]; + const video = document.createElement('video'); + video.style.height = '100%'; + video.style.width = '100%'; + video.controls = false; + if (lodash.isEqual(this.playTrailer, 'muted')) { + video.muted = true; } - }); - const fullScreenChangeAction = () => { - if (this.videoElem) { + const source = document.createElement('source'); + source.type = 'video/mp4'; + source.src = this.plex.authorizeURL(`${this.plex.getBasicURL()}${dataDetails.Extras.Metadata[0].Media[0].Part[0].key}`); + video.appendChild(source); + videoPlayer.appendChild(video); + video.load(); + video.play(); + let playingFired = false; + const videobgs1 = this.getElementsByClassName('videobg1'); + const videobgs2 = this.getElementsByClassName('videobg2'); + video.addEventListener('click', event => { if (isVideoFullScreen(this)) { - // eslint-disable-next-line no-restricted-syntax - for (const videobg1 of videobgs1) { - videobg1.classList.add('transparent'); - } - // eslint-disable-next-line no-restricted-syntax - for (const videobg2 of videobgs2) { - videobg2.classList.add('transparent'); - } - this.videoElem.classList.add('maxZIndex'); - video.controls = true; - video.muted = false; + event.stopPropagation(); } - else { - // eslint-disable-next-line no-restricted-syntax - for (const videobg1 of videobgs1) { - videobg1.classList.remove('transparent'); - } - // eslint-disable-next-line no-restricted-syntax - for (const videobg2 of videobgs2) { - videobg2.classList.remove('transparent'); + }); + const fullScreenChangeAction = () => { + if (this.videoElem) { + if (isVideoFullScreen(this)) { + // eslint-disable-next-line no-restricted-syntax + for (const videobg1 of videobgs1) { + videobg1.classList.add('transparent'); + } + // eslint-disable-next-line no-restricted-syntax + for (const videobg2 of videobgs2) { + videobg2.classList.add('transparent'); + } + this.videoElem.classList.add('maxZIndex'); + video.controls = true; + video.muted = false; } - this.videoElem.classList.remove('maxZIndex'); - video.controls = false; - window.scroll({ - top: getOffset(this.activeMovieElem).top - 70, - behavior: 'smooth' - }); - if (lodash.isEqual(this.playTrailer, 'muted')) { - video.muted = true; + else { + // eslint-disable-next-line no-restricted-syntax + for (const videobg1 of videobgs1) { + videobg1.classList.remove('transparent'); + } + // eslint-disable-next-line no-restricted-syntax + for (const videobg2 of videobgs2) { + videobg2.classList.remove('transparent'); + } + this.videoElem.classList.remove('maxZIndex'); + video.controls = false; + window.scroll({ + top: getOffset(this.activeMovieElem).top - 70, + behavior: 'smooth' + }); + if (lodash.isEqual(this.playTrailer, 'muted')) { + video.muted = true; + } } } - } - }; - video.addEventListener('fullscreenchange', fullScreenChangeAction); - video.addEventListener('mozfullscreenchange', fullScreenChangeAction); - video.addEventListener('webkitfullscreenchange', fullScreenChangeAction); - video.addEventListener('msfullscreenchange', fullScreenChangeAction); - video.addEventListener('playing', () => { - if (this.videoElem && !playingFired) { - const contentbg = this.getElementsByClassName('contentbg')[0]; - const fullscreenTrailer = this.getElementsByClassName('detailPlayAction')[0]; - fullscreenTrailer.style.visibility = 'visible'; - contentbg.classList.add('no-transparency'); - playingFired = true; - this.videoElem.style.width = `${this.getElementsByClassName('searchContainer')[0].offsetWidth}px`; - this.videoElem.style.visibility = 'visible'; - this.videoElem.style.top = `${top}px`; - } - }); - } - else if (!lodash.isEmpty(art)) { - const contentArt = this.getElementsByClassName('contentArt')[0]; - const contentbg = this.getElementsByClassName('contentbg')[0]; - contentArt.style.width = `${window.innerWidth}px`; - contentArt.style.height = `${window.innerHeight}px`; - contentArt.style.backgroundImage = `url('${art}')`; - contentArt.style.top = `${top - 8}px`; - contentArt.style.transition = '0.5s'; - contentArt.style.display = 'block'; - contentbg.classList.add('no-transparency'); + }; + video.addEventListener('fullscreenchange', fullScreenChangeAction); + video.addEventListener('mozfullscreenchange', fullScreenChangeAction); + video.addEventListener('webkitfullscreenchange', fullScreenChangeAction); + video.addEventListener('msfullscreenchange', fullScreenChangeAction); + video.addEventListener('playing', () => { + if (this.videoElem && !playingFired) { + const contentbg = this.getElementsByClassName('contentbg')[0]; + const fullscreenTrailer = this.getElementsByClassName('detailPlayAction')[0]; + fullscreenTrailer.style.visibility = 'visible'; + contentbg.classList.add('no-transparency'); + playingFired = true; + this.videoElem.style.width = `${this.getElementsByClassName('searchContainer')[0].offsetWidth}px`; + this.videoElem.style.visibility = 'visible'; + this.videoElem.style.top = `${top}px`; + } + }); + } + else if (!lodash.isEmpty(art)) { + const contentArt = this.getElementsByClassName('contentArt')[0]; + const contentbg = this.getElementsByClassName('contentbg')[0]; + contentArt.style.width = `${window.innerWidth}px`; + contentArt.style.height = `${window.innerHeight}px`; + contentArt.style.backgroundImage = `url('${art}')`; + contentArt.style.top = `${top - 8}px`; + contentArt.style.transition = '0.5s'; + contentArt.style.display = 'block'; + contentbg.classList.add('no-transparency'); + } } } if (!lodash.isEmpty(seasonsData)) { @@ -22115,7 +22255,7 @@ class PlexMeetsHomeAssistant extends HTMLElement { } }); } - else if (this.showExtras) { + else if (this.showExtras && !lodash.isNil(dataDetails.Extras)) { const extras = dataDetails.Extras.Metadata; lodash.forEach(extras, extrasData => { if (this.episodesElem && this.playController && this.plex) { @@ -22175,8 +22315,14 @@ class PlexMeetsHomeAssistant extends HTMLElement { this.minimizeAll(); this.activeMovieElem = undefined; this.hideDetails(); - movieElemLocal.style.width = `${CSS_STYLE.width}px`; - movieElemLocal.style.height = `${CSS_STYLE.height}px`; + if (lodash.isEqual(movieElem.style.width, movieElem.style.height)) { + movieElemLocal.style.width = `${CSS_STYLE.width}px`; + movieElemLocal.style.height = `${CSS_STYLE.width}px`; + } + else { + movieElemLocal.style.width = `${CSS_STYLE.width}px`; + movieElemLocal.style.height = `${CSS_STYLE.height}px`; + } movieElemLocal.style.zIndex = '1'; movieElemLocal.style.top = `${movieElem.dataset.top}px`; movieElemLocal.style.left = `${movieElem.dataset.left}px`; @@ -22189,8 +22335,14 @@ class PlexMeetsHomeAssistant extends HTMLElement { const top = this.getTop(); this.showDetails(this.activeMovieElemData); this.showBackground(); - movieElemLocal.style.width = `${CSS_STYLE.expandedWidth}px`; - movieElemLocal.style.height = `${CSS_STYLE.expandedHeight}px`; + if (lodash.isEqual(movieElem.style.width, movieElem.style.height)) { + movieElemLocal.style.width = `${CSS_STYLE.expandedWidth}px`; + movieElemLocal.style.height = `${CSS_STYLE.expandedWidth}px`; + } + else { + movieElemLocal.style.width = `${CSS_STYLE.expandedWidth}px`; + movieElemLocal.style.height = `${CSS_STYLE.expandedHeight}px`; + } movieElemLocal.style.zIndex = '3'; movieElemLocal.style.left = '16px'; movieElemLocal.style.top = `${top + 16}px`; @@ -22229,10 +22381,20 @@ class PlexMeetsHomeAssistant extends HTMLElement { else { container.style.height = `${CSS_STYLE.height + 30}px`; } + if (!lodash.isNil(data.channelCallSign)) { + container.style.marginBottom = '50px'; + } const movieElem = document.createElement('div'); movieElem.className = 'movieElem'; movieElem.style.width = `${CSS_STYLE.width}px`; movieElem.style.height = `${CSS_STYLE.height}px`; + if (!lodash.isNil(data.channelCallSign)) { + movieElem.style.backgroundSize = '80%'; + movieElem.style.backgroundColor = 'rgba(0,0,0,0.2)'; + movieElem.style.backgroundPosition = 'center'; + container.style.height = container.style.width; + movieElem.style.height = `${CSS_STYLE.width}px`; + } movieElem.style.backgroundImage = `url('${thumbURL}')`; if (this.playController && !this.playController.isPlaySupported(data)) { movieElem.style.cursor = 'pointer'; @@ -22278,18 +22440,29 @@ class PlexMeetsHomeAssistant extends HTMLElement { if (lodash.isEqual(data.type, 'episode')) { titleElem.innerHTML = escapeHtml(data.grandparentTitle); } + else if (!lodash.isNil(data.channelCallSign)) { + titleElem.innerHTML = escapeHtml(data.channelCallSign); + } else { titleElem.innerHTML = escapeHtml(data.title); } titleElem.className = 'titleElem'; - titleElem.style.marginTop = `${CSS_STYLE.height}px`; + if (!lodash.isNil(data.channelCallSign)) { + titleElem.style.marginTop = `${CSS_STYLE.width}px`; + } + else { + titleElem.style.marginTop = `${CSS_STYLE.height}px`; + } const yearElem = document.createElement('div'); if (lodash.isEqual(data.type, 'episode')) { yearElem.innerHTML = escapeHtml(data.title); } - else { + else if (!lodash.isNil(data.year)) { yearElem.innerHTML = escapeHtml(data.year); } + else if (!lodash.isNil(data.epg)) { + yearElem.innerHTML = escapeHtml(data.epg.title); + } yearElem.className = 'yearElem'; const additionalElem = document.createElement('div'); if (lodash.isEqual(data.type, 'episode')) { diff --git a/src/editor.ts b/src/editor.ts index b25f844..0904995 100644 --- a/src/editor.ts +++ b/src/editor.ts @@ -72,6 +72,8 @@ class PlexMeetsHomeAssistantEditor extends HTMLElement { loaded = false; + livetv: Record