From a8e4274c224b6fdd59de7cdf55983902c6987071 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraj=20Nyi=CC=81ri?= Date: Mon, 4 Oct 2021 01:19:29 +0200 Subject: [PATCH] Add: View for songs WIP --- dist/plex-meets-homeassistant.js | 127 ++++++++++++++++++++++++++++++- src/modules/style.ts | 25 ++++++ src/modules/utils.ts | 109 +++++++++++++++++++++++++- src/plex-meets-homeassistant.ts | 44 ++++++++--- 4 files changed, 293 insertions(+), 12 deletions(-) diff --git a/dist/plex-meets-homeassistant.js b/dist/plex-meets-homeassistant.js index 5edb8d4..1996e76 100644 --- a/dist/plex-meets-homeassistant.js +++ b/dist/plex-meets-homeassistant.js @@ -19240,6 +19240,13 @@ const sleep = async (ms) => { const getState = async (hass, entityID) => { return hass.callApi('GET', `states/${entityID}`); }; +const padWithZeroes = (number, length) => { + let myString = `${number}`; + while (myString.length < length) { + myString = `0${myString}`; + } + return myString; +}; const waitUntilState = async (hass, entityID, state) => { let entityState = await getState(hass, entityID); while (entityState.state !== state) { @@ -19249,6 +19256,84 @@ const waitUntilState = async (hass, entityID, state) => { await sleep(1000); } }; +const createTrackView = (playController, plex, data, fontSize1, fontSize2, isEven) => { + const margin1 = fontSize1 / 4; + const trackContainer = document.createElement('tr'); + trackContainer.classList.add('trackContainer'); + if (isEven) { + trackContainer.classList.add('even'); + } + else { + trackContainer.classList.add('odd'); + } + const trackIndexElem = document.createElement('td'); + trackIndexElem.className = 'trackIndexElem'; + trackIndexElem.innerHTML = escapeHtml(data.index); + trackIndexElem.style.fontSize = `${fontSize1}px`; + trackIndexElem.style.lineHeight = `${fontSize1}px`; + trackIndexElem.style.marginBottom = `${margin1}px`; + trackContainer.append(trackIndexElem); + const trackTitleElem = document.createElement('td'); + trackTitleElem.className = 'trackTitleElem'; + trackTitleElem.innerHTML = escapeHtml(data.title); + trackTitleElem.style.fontSize = `${fontSize1}px`; + trackTitleElem.style.lineHeight = `${fontSize1}px`; + trackTitleElem.style.marginBottom = `${margin1}px`; + trackContainer.append(trackTitleElem); + const duration = lodash.get(data, 'Media[0].duration'); + const trackLengthElem = document.createElement('td'); + trackLengthElem.className = 'trackLengthElem'; + trackLengthElem.style.fontSize = `${fontSize1}px`; + trackLengthElem.style.lineHeight = `${fontSize1}px`; + trackLengthElem.style.marginBottom = `${margin1}px`; + if (duration) { + const minutes = Math.floor(duration / 60 / 1000); + const seconds = Math.round((duration - minutes * 1000) / 6000); + trackLengthElem.innerHTML = escapeHtml(`${padWithZeroes(minutes, 2)}:${padWithZeroes(seconds, 2)}`); + } + trackContainer.append(trackLengthElem); + /* + const episodeInteractiveArea = document.createElement('div'); + episodeInteractiveArea.className = 'interactiveArea'; + if (playController) { + const episodePlayButton = playController.getPlayButton(data.type); + episodePlayButton.addEventListener('click', (episodeEvent: MouseEvent) => { + episodeEvent.stopPropagation(); + playController.play(data, true); + }); + if (playController.isPlaySupported(data)) { + episodePlayButton.classList.remove('disabled'); + } + episodeInteractiveArea.append(episodePlayButton); + } + */ + /* + + const trackIndexElem = document.createElement('div'); + trackIndexElem.className = 'trackIndexElem'; + trackIndexElem.innerHTML = escapeHtml(data.index); + + trackIndexElem.style.fontSize = `${fontSize1}px`; + trackIndexElem.style.lineHeight = `${fontSize1}px`; + trackIndexElem.style.marginBottom = `${margin1}px`; + + trackContainer.append(trackIndexElem); + + const trackTitleElem = document.createElement('div'); + trackTitleElem.className = 'trackTitleElem'; + trackTitleElem.innerHTML = escapeHtml(data.title); + + trackTitleElem.style.fontSize = `${fontSize1}px`; + trackTitleElem.style.lineHeight = `${fontSize1}px`; + trackTitleElem.style.marginBottom = `${margin1}px`; + + trackContainer.append(trackTitleElem); + */ + trackContainer.addEventListener('click', episodeEvent => { + episodeEvent.stopPropagation(); + }); + return trackContainer; +}; const createEpisodesView = (playController, plex, data, fontSize1, fontSize2) => { const episodeContainer = document.createElement('div'); episodeContainer.className = 'episodeContainer'; @@ -21259,6 +21344,31 @@ style.textContent = css ` transform: rotate(360deg); } } + .trackContainer { + width: 100%; + } + .trackContainer.odd:hover, + .trackContainer.even:hover { + background-color: rgba(255, 255, 255, 0.2); + } + .trackContainer.odd { + background-color: rgba(255, 255, 255, 0.08); + } + .trackContainer.even { + background-color: rgba(255, 255, 255, 0.04); + } + .trackLengthElem { + position: relative; + padding: 10px 10px 10px 20px; + } + .trackIndexElem { + position: relative; + padding: 10px 20px 10px 10px; + } + + .trackTitleElem { + width: 100%; + } .detail { position: absolute; left: 247px; @@ -22945,9 +23055,24 @@ class PlexMeetsHomeAssistant extends HTMLElement { this.episodesElem.innerHTML = ''; this.episodesElem.style.transition = `0s`; this.episodesElem.style.top = `${top + 2000}px`; + const tableView = document.createElement('table'); + tableView.style.width = 'calc(100% - 10px)'; + tableView.style.border = 'none'; + tableView.cellSpacing = '0'; + tableView.cellPadding = '0'; + if (lodash.isEqual(childData.type, 'album')) { + this.episodesElem.append(tableView); + } + let isEven = false; lodash.forEach(episodesData, episodeData => { if (this.episodesElem && this.playController && this.plex) { - this.episodesElem.append(createEpisodesView(this.playController, this.plex, episodeData, this.fontSize1, this.fontSize2)); + if (lodash.isEqual(episodeData.type, 'track')) { + tableView.append(createTrackView(this.playController, this.plex, episodeData, this.fontSize1, this.fontSize2, isEven)); + isEven = !isEven; + } + else { + this.episodesElem.append(createEpisodesView(this.playController, this.plex, episodeData, this.fontSize1, this.fontSize2)); + } } }); clearInterval(this.episodesLoadTimeout); diff --git a/src/modules/style.ts b/src/modules/style.ts index 3f0b390..5bce93a 100644 --- a/src/modules/style.ts +++ b/src/modules/style.ts @@ -181,6 +181,31 @@ style.textContent = css` transform: rotate(360deg); } } + .trackContainer { + width: 100%; + } + .trackContainer.odd:hover, + .trackContainer.even:hover { + background-color: rgba(255, 255, 255, 0.2); + } + .trackContainer.odd { + background-color: rgba(255, 255, 255, 0.08); + } + .trackContainer.even { + background-color: rgba(255, 255, 255, 0.04); + } + .trackLengthElem { + position: relative; + padding: 10px 10px 10px 20px; + } + .trackIndexElem { + position: relative; + padding: 10px 20px 10px 10px; + } + + .trackTitleElem { + width: 100%; + } .detail { position: absolute; left: 247px; diff --git a/src/modules/utils.ts b/src/modules/utils.ts index fe3582a..904012d 100644 --- a/src/modules/utils.ts +++ b/src/modules/utils.ts @@ -167,6 +167,15 @@ const getState = async (hass: HomeAssistant, entityID: string): Promise { + let myString = `${number}`; + while (myString.length < length) { + myString = `0${myString}`; + } + + return myString; +}; + const waitUntilState = async (hass: HomeAssistant, entityID: string, state: string): Promise => { let entityState = await getState(hass, entityID); @@ -178,6 +187,103 @@ const waitUntilState = async (hass: HomeAssistant, entityID: string, state: stri } }; +const createTrackView = ( + playController: any, + plex: Plex, + data: Record, + fontSize1: number, + fontSize2: number, + isEven: boolean +): HTMLElement => { + const margin1 = fontSize1 / 4; + const margin2 = fontSize2 / 4; + const trackContainer = document.createElement('tr'); + trackContainer.classList.add('trackContainer'); + if (isEven) { + trackContainer.classList.add('even'); + } else { + trackContainer.classList.add('odd'); + } + + const trackIndexElem = document.createElement('td'); + trackIndexElem.className = 'trackIndexElem'; + trackIndexElem.innerHTML = escapeHtml(data.index); + + trackIndexElem.style.fontSize = `${fontSize1}px`; + trackIndexElem.style.lineHeight = `${fontSize1}px`; + trackIndexElem.style.marginBottom = `${margin1}px`; + + trackContainer.append(trackIndexElem); + + const trackTitleElem = document.createElement('td'); + trackTitleElem.className = 'trackTitleElem'; + trackTitleElem.innerHTML = escapeHtml(data.title); + + trackTitleElem.style.fontSize = `${fontSize1}px`; + trackTitleElem.style.lineHeight = `${fontSize1}px`; + trackTitleElem.style.marginBottom = `${margin1}px`; + + trackContainer.append(trackTitleElem); + + const duration = _.get(data, 'Media[0].duration'); + const trackLengthElem = document.createElement('td'); + trackLengthElem.className = 'trackLengthElem'; + + trackLengthElem.style.fontSize = `${fontSize1}px`; + trackLengthElem.style.lineHeight = `${fontSize1}px`; + trackLengthElem.style.marginBottom = `${margin1}px`; + + if (duration) { + const minutes = Math.floor(duration / 60 / 1000); + const seconds = Math.round((duration - minutes * 1000) / 6000); + trackLengthElem.innerHTML = escapeHtml(`${padWithZeroes(minutes, 2)}:${padWithZeroes(seconds, 2)}`); + } + trackContainer.append(trackLengthElem); + + /* + const episodeInteractiveArea = document.createElement('div'); + episodeInteractiveArea.className = 'interactiveArea'; + if (playController) { + const episodePlayButton = playController.getPlayButton(data.type); + episodePlayButton.addEventListener('click', (episodeEvent: MouseEvent) => { + episodeEvent.stopPropagation(); + playController.play(data, true); + }); + if (playController.isPlaySupported(data)) { + episodePlayButton.classList.remove('disabled'); + } + episodeInteractiveArea.append(episodePlayButton); + } + */ + + /* + + const trackIndexElem = document.createElement('div'); + trackIndexElem.className = 'trackIndexElem'; + trackIndexElem.innerHTML = escapeHtml(data.index); + + trackIndexElem.style.fontSize = `${fontSize1}px`; + trackIndexElem.style.lineHeight = `${fontSize1}px`; + trackIndexElem.style.marginBottom = `${margin1}px`; + + trackContainer.append(trackIndexElem); + + const trackTitleElem = document.createElement('div'); + trackTitleElem.className = 'trackTitleElem'; + trackTitleElem.innerHTML = escapeHtml(data.title); + + trackTitleElem.style.fontSize = `${fontSize1}px`; + trackTitleElem.style.lineHeight = `${fontSize1}px`; + trackTitleElem.style.marginBottom = `${margin1}px`; + + trackContainer.append(trackTitleElem); + */ + + trackContainer.addEventListener('click', episodeEvent => { + episodeEvent.stopPropagation(); + }); + return trackContainer; +}; const createEpisodesView = ( playController: any, plex: Plex, @@ -301,5 +407,6 @@ export { clickHandler, fetchEntityRegistry, waitUntilState, - getState + getState, + createTrackView }; diff --git a/src/plex-meets-homeassistant.ts b/src/plex-meets-homeassistant.ts index 6a3b368..2c3fd20 100644 --- a/src/plex-meets-homeassistant.ts +++ b/src/plex-meets-homeassistant.ts @@ -19,7 +19,8 @@ import { getDetailsBottom, clickHandler, fetchEntityRegistry, - getWidth + getWidth, + createTrackView } from './modules/utils'; import style from './modules/style'; @@ -1588,17 +1589,40 @@ class PlexMeetsHomeAssistant extends HTMLElement { this.episodesElem.innerHTML = ''; this.episodesElem.style.transition = `0s`; this.episodesElem.style.top = `${top + 2000}px`; + const tableView = document.createElement('table'); + tableView.style.width = 'calc(100% - 10px)'; + tableView.style.border = 'none'; + tableView.cellSpacing = '0'; + tableView.cellPadding = '0'; + if (_.isEqual(childData.type, 'album')) { + this.episodesElem.append(tableView); + } + let isEven = false; _.forEach(episodesData, episodeData => { if (this.episodesElem && this.playController && this.plex) { - this.episodesElem.append( - createEpisodesView( - this.playController, - this.plex, - episodeData, - this.fontSize1, - this.fontSize2 - ) - ); + if (_.isEqual(episodeData.type, 'track')) { + tableView.append( + createTrackView( + this.playController, + this.plex, + episodeData, + this.fontSize1, + this.fontSize2, + isEven + ) + ); + isEven = !isEven; + } else { + this.episodesElem.append( + createEpisodesView( + this.playController, + this.plex, + episodeData, + this.fontSize1, + this.fontSize2 + ) + ); + } } }); clearInterval(this.episodesLoadTimeout);