Add: Seasons display

pull/16/head
Juraj Nyíri 4 years ago
parent 16def24e64
commit ff61397421

@ -18701,7 +18701,7 @@ const escapeHtml = (unsafe) => {
const CSS_STYLE = { const CSS_STYLE = {
width: 138, width: 138,
height: 206, height: 203,
expandedWidth: 220, expandedWidth: 220,
expandedHeight: 324 expandedHeight: 324
}; };
@ -18954,6 +18954,14 @@ style.textContent = css `
position: relative; position: relative;
background: orange; background: orange;
} }
.seasons {
z-index: 4;
position: absolute;
top: ${CSS_STYLE.expandedHeight + 16}px;
width: 100%;
left: 0;
padding: 16px;
}
.ratingDetail { .ratingDetail {
background: #ffffff24; background: #ffffff24;
padding: 5px 10px; padding: 5px 10px;
@ -19069,12 +19077,35 @@ style.textContent = css `
color: hsla(0, 0%, 100%, 0.45); color: hsla(0, 0%, 100%, 0.45);
position: relative; position: relative;
} }
.seasonTitleElem {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
position: relative;
font-weight: bold;
margin-top: 5px;
}
.seasonEpisodesCount {
}
.titleElem { .titleElem {
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
position: relative; position: relative;
} }
.seasonContainer {
position: relative;
float: left;
margin-right: 16px;
margin-bottom: 15px;
transition: 5s;
}
.seasonElem {
background-repeat: no-repeat;
background-size: contain;
border-radius: 5px;
transition: 0.5s;
}
.movieElem { .movieElem {
margin-bottom: 5px; margin-bottom: 5px;
background-repeat: no-repeat; background-repeat: no-repeat;
@ -19168,7 +19199,9 @@ class PlexMeetsHomeAssistant extends HTMLElement {
this.plexProtocol = 'http'; this.plexProtocol = 'http';
this.plex = undefined; this.plex = undefined;
this.movieElems = []; this.movieElems = [];
this.seasonElemFreshlyLoaded = false;
this.detailElem = undefined; this.detailElem = undefined;
this.seasonsElem = undefined;
this.data = {}; this.data = {};
this.config = {}; this.config = {};
this.requestTimeout = 3000; this.requestTimeout = 3000;
@ -19177,9 +19210,9 @@ class PlexMeetsHomeAssistant extends HTMLElement {
this.playSupported = false; this.playSupported = false;
this.error = ''; this.error = '';
this.previousPositions = []; this.previousPositions = [];
this.loadInitialData = async (hass) => { this.loadInitialData = async () => {
this.loading = true; this.loading = true;
this.renderPage(hass); this.renderPage();
try { try {
if (this.plex) { if (this.plex) {
const [plexInfo, plexSections] = await Promise.all([this.plex.getServerInfo(), this.plex.getSectionsData()]); const [plexInfo, plexSections] = await Promise.all([this.plex.getServerInfo(), this.plex.getSectionsData()]);
@ -19192,7 +19225,7 @@ class PlexMeetsHomeAssistant extends HTMLElement {
this.error = `Library name ${this.config.libraryName} does not exist.`; this.error = `Library name ${this.config.libraryName} does not exist.`;
} }
this.loading = false; this.loading = false;
this.render(hass); this.render();
} }
else { else {
throw Error('Plex not initialized.'); throw Error('Plex not initialized.');
@ -19201,10 +19234,10 @@ class PlexMeetsHomeAssistant extends HTMLElement {
catch (err) { catch (err) {
// todo: proper timeout here // todo: proper timeout here
this.error = `Plex server did not respond.<br/>Details of the error: ${escapeHtml(err.message)}`; this.error = `Plex server did not respond.<br/>Details of the error: ${escapeHtml(err.message)}`;
this.renderPage(hass); this.renderPage();
} }
}; };
this.render = (hass) => { this.render = () => {
this.previousPositions = []; this.previousPositions = [];
// todo: find a better way to detect resize... // todo: find a better way to detect resize...
setInterval(() => { setInterval(() => {
@ -19227,13 +19260,13 @@ class PlexMeetsHomeAssistant extends HTMLElement {
} }
} }
if (renderNeeded) { if (renderNeeded) {
this.renderPage(hass); this.renderPage();
} }
} }
}, 100); }, 100);
this.renderPage(hass); this.renderPage();
}; };
this.renderPage = (hass) => { this.renderPage = () => {
if (this) if (this)
this.innerHTML = ''; this.innerHTML = '';
const card = document.createElement('ha-card'); const card = document.createElement('ha-card');
@ -19264,6 +19297,13 @@ class PlexMeetsHomeAssistant extends HTMLElement {
"<h1></h1><h2></h2><span class='metaInfo'></span><span class='detailDesc'></span><div class='clear'></div>"; "<h1></h1><h2></h2><span class='metaInfo'></span><span class='detailDesc'></span><div class='clear'></div>";
if (this.playSupported) ; if (this.playSupported) ;
this.content.appendChild(this.detailElem); this.content.appendChild(this.detailElem);
this.seasonsElem = document.createElement('div');
this.seasonsElem.className = 'seasons';
this.seasonsElem.addEventListener('click', () => {
this.hideBackground();
this.minimizeAll();
});
this.content.appendChild(this.seasonsElem);
// todo: figure out why timeout is needed here and do it properly // todo: figure out why timeout is needed here and do it properly
setTimeout(() => { setTimeout(() => {
contentbg.addEventListener('click', () => { contentbg.addEventListener('click', () => {
@ -19276,7 +19316,7 @@ class PlexMeetsHomeAssistant extends HTMLElement {
lodash.forEach(this.data[this.config.libraryName], (movieData) => { lodash.forEach(this.data[this.config.libraryName], (movieData) => {
if (!this.maxCount || count < this.maxCount) { if (!this.maxCount || count < this.maxCount) {
count += 1; count += 1;
this.content.appendChild(this.getMovieElement(movieData, hass, this.data.server_id)); this.content.appendChild(this.getMovieElement(movieData, this.data.server_id));
} }
else { else {
return true; return true;
@ -19321,6 +19361,20 @@ class PlexMeetsHomeAssistant extends HTMLElement {
}, 500); }, 500);
} }
} }
if (this.seasonsElem) {
const doc = document.documentElement;
const top = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0);
this.seasonsElem.style.top = `${top + 2000}px`;
setTimeout(() => {
if (this.seasonsElem && !this.seasonElemFreshlyLoaded) {
this.seasonsElem.innerHTML = '';
this.seasonsElem.style.display = 'none';
// fix for a specific case when user scrolls outside of normal home assistant area for seasons look
const contentbg = this.getElementsByClassName('contentbg');
contentbg[0].style.height = '100%';
}
}, 700);
}
this.hideDetails(); this.hideDetails();
}; };
this.hideDetails = () => { this.hideDetails = () => {
@ -19370,7 +19424,60 @@ class PlexMeetsHomeAssistant extends HTMLElement {
}, 200); }, 200);
} }
if (this.plex) { if (this.plex) {
this.seasonElemFreshlyLoaded = true;
const seasonsData = await this.plex.getLibraryData(data.key.split('/')[3]); const seasonsData = await this.plex.getLibraryData(data.key.split('/')[3]);
if (this.seasonsElem) {
this.seasonsElem.style.display = 'block';
this.seasonsElem.innerHTML = '';
this.seasonsElem.style.transition = `0s`;
this.seasonsElem.style.top = `${top + 2000}px`;
}
lodash.forEach(seasonsData, seasonData => {
if (this.seasonsElem) {
const seasonContainer = document.createElement('div');
seasonContainer.className = 'seasonContainer';
seasonContainer.style.width = `${CSS_STYLE.width}px`;
const thumbURL = `${this.plexProtocol}://${this.config.ip}:${this.config.port}/photo/:/transcode?width=${CSS_STYLE.expandedWidth}&height=${CSS_STYLE.expandedHeight}&minSize=1&upscale=1&url=${seasonData.thumb}&X-Plex-Token=${this.config.token}`;
const seasonElem = document.createElement('div');
seasonElem.className = 'seasonElem';
seasonElem.style.width = `${CSS_STYLE.width}px`;
seasonElem.style.height = `${CSS_STYLE.height}px`;
seasonElem.style.backgroundImage = `url('${thumbURL}')`;
seasonContainer.append(seasonElem);
const seasonTitleElem = document.createElement('div');
seasonTitleElem.className = 'seasonTitleElem';
seasonTitleElem.innerHTML = escapeHtml(seasonData.title);
seasonContainer.append(seasonTitleElem);
const seasonEpisodesCount = document.createElement('div');
seasonEpisodesCount.className = 'seasonEpisodesCount';
seasonEpisodesCount.innerHTML = `${escapeHtml(seasonData.leafCount)} episodes`;
seasonContainer.append(seasonEpisodesCount);
seasonContainer.addEventListener('click', event => {
event.stopPropagation();
(async () => {
if (this.plex) {
console.log(seasonData);
console.log(await this.plex.getLibraryData(seasonData.key.split('/')[3]));
}
})();
});
this.seasonsElem.append(seasonContainer);
setTimeout(() => {
this.seasonElemFreshlyLoaded = false;
}, 700);
}
});
setTimeout(() => {
if (this.seasonsElem) {
this.seasonsElem.style.transition = `0.7s`;
this.seasonsElem.style.top = `${top + CSS_STYLE.expandedHeight + 16}px`;
const requiredBodyHeight = parseInt(this.seasonsElem.style.top.replace('px', ''), 10) + this.seasonsElem.scrollHeight;
const contentbg = this.getElementsByClassName('contentbg');
if (requiredBodyHeight > contentbg[0].scrollHeight) {
contentbg[0].style.height = `${requiredBodyHeight}px`;
}
}
}, 200);
console.log(seasonsData); console.log(seasonsData);
} }
}; };
@ -19384,7 +19491,7 @@ class PlexMeetsHomeAssistant extends HTMLElement {
contentbg[0].style.zIndex = '1'; contentbg[0].style.zIndex = '1';
contentbg[0].style.backgroundColor = 'rgba(0,0,0,0)'; contentbg[0].style.backgroundColor = 'rgba(0,0,0,0)';
}; };
this.getMovieElement = (data, hass, serverID) => { this.getMovieElement = (data, serverID) => {
const thumbURL = `${this.plexProtocol}://${this.config.ip}:${this.config.port}/photo/:/transcode?width=${CSS_STYLE.expandedWidth}&height=${CSS_STYLE.expandedHeight}&minSize=1&upscale=1&url=${data.thumb}&X-Plex-Token=${this.config.token}`; const thumbURL = `${this.plexProtocol}://${this.config.ip}:${this.config.port}/photo/:/transcode?width=${CSS_STYLE.expandedWidth}&height=${CSS_STYLE.expandedHeight}&minSize=1&upscale=1&url=${data.thumb}&X-Plex-Token=${this.config.token}`;
const container = document.createElement('div'); const container = document.createElement('div');
container.className = 'container'; container.className = 'container';
@ -19441,7 +19548,7 @@ class PlexMeetsHomeAssistant extends HTMLElement {
console.log(command); console.log(command);
// eslint-disable-next-line @typescript-eslint/camelcase // eslint-disable-next-line @typescript-eslint/camelcase
const { entity_id } = this.config; const { entity_id } = this.config;
hass.callService('androidtv', 'adb_command', { this.hass.callService('androidtv', 'adb_command', {
// eslint-disable-next-line @typescript-eslint/camelcase // eslint-disable-next-line @typescript-eslint/camelcase
entity_id, entity_id,
command command
@ -19505,7 +19612,7 @@ class PlexMeetsHomeAssistant extends HTMLElement {
hass.states[this.config.entity_id].attributes.adb_response !== undefined; hass.states[this.config.entity_id].attributes.adb_response !== undefined;
this.error = ''; this.error = '';
if (!this.loading) { if (!this.loading) {
this.loadInitialData(hass); this.loadInitialData();
} }
} }
} }

@ -1,6 +1,6 @@
const CSS_STYLE = { const CSS_STYLE = {
width: 138, width: 138,
height: 206, height: 203,
expandedWidth: 220, expandedWidth: 220,
expandedHeight: 324 expandedHeight: 324
}; };

@ -14,6 +14,14 @@ style.textContent = css`
position: relative; position: relative;
background: orange; background: orange;
} }
.seasons {
z-index: 4;
position: absolute;
top: ${CSS_STYLE.expandedHeight + 16}px;
width: 100%;
left: 0;
padding: 16px;
}
.ratingDetail { .ratingDetail {
background: #ffffff24; background: #ffffff24;
padding: 5px 10px; padding: 5px 10px;
@ -129,12 +137,35 @@ style.textContent = css`
color: hsla(0, 0%, 100%, 0.45); color: hsla(0, 0%, 100%, 0.45);
position: relative; position: relative;
} }
.seasonTitleElem {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
position: relative;
font-weight: bold;
margin-top: 5px;
}
.seasonEpisodesCount {
}
.titleElem { .titleElem {
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
position: relative; position: relative;
} }
.seasonContainer {
position: relative;
float: left;
margin-right: 16px;
margin-bottom: 15px;
transition: 5s;
}
.seasonElem {
background-repeat: no-repeat;
background-size: contain;
border-radius: 5px;
transition: 0.5s;
}
.movieElem { .movieElem {
margin-bottom: 5px; margin-bottom: 5px;
background-repeat: no-repeat; background-repeat: no-repeat;

@ -14,8 +14,12 @@ class PlexMeetsHomeAssistant extends HTMLElement {
movieElems: any = []; movieElems: any = [];
seasonElemFreshlyLoaded = false;
detailElem: HTMLElement | undefined = undefined; detailElem: HTMLElement | undefined = undefined;
seasonsElem: HTMLElement | undefined = undefined;
data: Record<string, any> = {}; data: Record<string, any> = {};
config: Record<string, any> = {}; config: Record<string, any> = {};
@ -43,14 +47,14 @@ class PlexMeetsHomeAssistant extends HTMLElement {
this.error = ''; this.error = '';
if (!this.loading) { if (!this.loading) {
this.loadInitialData(hass); this.loadInitialData();
} }
} }
} }
loadInitialData = async (hass: HomeAssistant): Promise<void> => { loadInitialData = async (): Promise<void> => {
this.loading = true; this.loading = true;
this.renderPage(hass); this.renderPage();
try { try {
if (this.plex) { if (this.plex) {
const [plexInfo, plexSections] = await Promise.all([this.plex.getServerInfo(), this.plex.getSectionsData()]); const [plexInfo, plexSections] = await Promise.all([this.plex.getServerInfo(), this.plex.getSectionsData()]);
@ -65,18 +69,18 @@ class PlexMeetsHomeAssistant extends HTMLElement {
} }
this.loading = false; this.loading = false;
this.render(hass); this.render();
} else { } else {
throw Error('Plex not initialized.'); throw Error('Plex not initialized.');
} }
} catch (err) { } catch (err) {
// todo: proper timeout here // todo: proper timeout here
this.error = `Plex server did not respond.<br/>Details of the error: ${escapeHtml(err.message)}`; this.error = `Plex server did not respond.<br/>Details of the error: ${escapeHtml(err.message)}`;
this.renderPage(hass); this.renderPage();
} }
}; };
render = (hass: HomeAssistant): void => { render = (): void => {
this.previousPositions = []; this.previousPositions = [];
// todo: find a better way to detect resize... // todo: find a better way to detect resize...
@ -102,15 +106,15 @@ class PlexMeetsHomeAssistant extends HTMLElement {
} }
} }
if (renderNeeded) { if (renderNeeded) {
this.renderPage(hass); this.renderPage();
} }
} }
}, 100); }, 100);
this.renderPage(hass); this.renderPage();
}; };
renderPage = (hass: HomeAssistant): void => { renderPage = (): void => {
if (this) this.innerHTML = ''; if (this) this.innerHTML = '';
const card = document.createElement('ha-card'); const card = document.createElement('ha-card');
// card.header = this.config.libraryName; // card.header = this.config.libraryName;
@ -149,6 +153,14 @@ class PlexMeetsHomeAssistant extends HTMLElement {
this.content.appendChild(this.detailElem); this.content.appendChild(this.detailElem);
this.seasonsElem = document.createElement('div');
this.seasonsElem.className = 'seasons';
this.seasonsElem.addEventListener('click', () => {
this.hideBackground();
this.minimizeAll();
});
this.content.appendChild(this.seasonsElem);
// todo: figure out why timeout is needed here and do it properly // todo: figure out why timeout is needed here and do it properly
setTimeout(() => { setTimeout(() => {
contentbg.addEventListener('click', () => { contentbg.addEventListener('click', () => {
@ -161,7 +173,7 @@ class PlexMeetsHomeAssistant extends HTMLElement {
_.forEach(this.data[this.config.libraryName], (movieData: Record<string, any>) => { _.forEach(this.data[this.config.libraryName], (movieData: Record<string, any>) => {
if (!this.maxCount || count < this.maxCount) { if (!this.maxCount || count < this.maxCount) {
count += 1; count += 1;
this.content.appendChild(this.getMovieElement(movieData, hass, this.data.server_id)); this.content.appendChild(this.getMovieElement(movieData, this.data.server_id));
} else { } else {
return true; return true;
} }
@ -207,6 +219,21 @@ class PlexMeetsHomeAssistant extends HTMLElement {
}, 500); }, 500);
} }
} }
if (this.seasonsElem) {
const doc = document.documentElement;
const top = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0);
this.seasonsElem.style.top = `${top + 2000}px`;
setTimeout(() => {
if (this.seasonsElem && !this.seasonElemFreshlyLoaded) {
this.seasonsElem.innerHTML = '';
this.seasonsElem.style.display = 'none';
// fix for a specific case when user scrolls outside of normal home assistant area for seasons look
const contentbg = this.getElementsByClassName('contentbg');
(contentbg[0] as HTMLElement).style.height = '100%';
}
}, 700);
}
this.hideDetails(); this.hideDetails();
}; };
@ -265,7 +292,72 @@ class PlexMeetsHomeAssistant extends HTMLElement {
}, 200); }, 200);
} }
if (this.plex) { if (this.plex) {
this.seasonElemFreshlyLoaded = true;
const seasonsData = await this.plex.getLibraryData(data.key.split('/')[3]); const seasonsData = await this.plex.getLibraryData(data.key.split('/')[3]);
if (this.seasonsElem) {
this.seasonsElem.style.display = 'block';
this.seasonsElem.innerHTML = '';
this.seasonsElem.style.transition = `0s`;
this.seasonsElem.style.top = `${top + 2000}px`;
}
_.forEach(seasonsData, seasonData => {
if (this.seasonsElem) {
const seasonContainer = document.createElement('div');
seasonContainer.className = 'seasonContainer';
seasonContainer.style.width = `${CSS_STYLE.width}px`;
const thumbURL = `${this.plexProtocol}://${this.config.ip}:${this.config.port}/photo/:/transcode?width=${CSS_STYLE.expandedWidth}&height=${CSS_STYLE.expandedHeight}&minSize=1&upscale=1&url=${seasonData.thumb}&X-Plex-Token=${this.config.token}`;
const seasonElem = document.createElement('div');
seasonElem.className = 'seasonElem';
seasonElem.style.width = `${CSS_STYLE.width}px`;
seasonElem.style.height = `${CSS_STYLE.height}px`;
seasonElem.style.backgroundImage = `url('${thumbURL}')`;
seasonContainer.append(seasonElem);
const seasonTitleElem = document.createElement('div');
seasonTitleElem.className = 'seasonTitleElem';
seasonTitleElem.innerHTML = escapeHtml(seasonData.title);
seasonContainer.append(seasonTitleElem);
const seasonEpisodesCount = document.createElement('div');
seasonEpisodesCount.className = 'seasonEpisodesCount';
seasonEpisodesCount.innerHTML = `${escapeHtml(seasonData.leafCount)} episodes`;
seasonContainer.append(seasonEpisodesCount);
seasonContainer.addEventListener('click', event => {
event.stopPropagation();
(async (): Promise<void> => {
if (this.plex) {
console.log(seasonData);
console.log(await this.plex.getLibraryData(seasonData.key.split('/')[3]));
}
})();
});
this.seasonsElem.append(seasonContainer);
setTimeout(() => {
this.seasonElemFreshlyLoaded = false;
}, 700);
}
});
setTimeout(() => {
if (this.seasonsElem) {
this.seasonsElem.style.transition = `0.7s`;
this.seasonsElem.style.top = `${top + CSS_STYLE.expandedHeight + 16}px`;
const requiredBodyHeight =
parseInt(this.seasonsElem.style.top.replace('px', ''), 10) + this.seasonsElem.scrollHeight;
const contentbg = this.getElementsByClassName('contentbg');
if (requiredBodyHeight > (contentbg[0] as HTMLElement).scrollHeight) {
(contentbg[0] as HTMLElement).style.height = `${requiredBodyHeight}px`;
}
}
}, 200);
console.log(seasonsData); console.log(seasonsData);
} }
}; };
@ -282,7 +374,7 @@ class PlexMeetsHomeAssistant extends HTMLElement {
(contentbg[0] as HTMLElement).style.backgroundColor = 'rgba(0,0,0,0)'; (contentbg[0] as HTMLElement).style.backgroundColor = 'rgba(0,0,0,0)';
}; };
getMovieElement = (data: any, hass: HomeAssistant, serverID: string): HTMLDivElement => { getMovieElement = (data: any, serverID: string): HTMLDivElement => {
const thumbURL = `${this.plexProtocol}://${this.config.ip}:${this.config.port}/photo/:/transcode?width=${CSS_STYLE.expandedWidth}&height=${CSS_STYLE.expandedHeight}&minSize=1&upscale=1&url=${data.thumb}&X-Plex-Token=${this.config.token}`; const thumbURL = `${this.plexProtocol}://${this.config.ip}:${this.config.port}/photo/:/transcode?width=${CSS_STYLE.expandedWidth}&height=${CSS_STYLE.expandedHeight}&minSize=1&upscale=1&url=${data.thumb}&X-Plex-Token=${this.config.token}`;
const container = document.createElement('div'); const container = document.createElement('div');
@ -348,7 +440,7 @@ class PlexMeetsHomeAssistant extends HTMLElement {
console.log(command); console.log(command);
// eslint-disable-next-line @typescript-eslint/camelcase // eslint-disable-next-line @typescript-eslint/camelcase
const { entity_id } = this.config; const { entity_id } = this.config;
hass.callService('androidtv', 'adb_command', { this.hass.callService('androidtv', 'adb_command', {
// eslint-disable-next-line @typescript-eslint/camelcase // eslint-disable-next-line @typescript-eslint/camelcase
entity_id, entity_id,
command command

Loading…
Cancel
Save