WIP: Extras view, has a bug!

pull/16/head
Juraj Nyíri 3 years ago
parent e44f141c64
commit a7494f1d6b

@ -17205,8 +17205,8 @@ const CSS_STYLE = {
}; };
const supported = { const supported = {
kodi: ['movie', 'episode'], kodi: ['movie', 'episode'],
androidtv: ['movie', 'show', 'season', 'episode'], androidtv: ['movie', 'show', 'season', 'episode', 'clip'],
plexPlayer: ['movie', 'show', 'season', 'episode'] plexPlayer: ['movie', 'show', 'season', 'episode', 'clip']
}; };
var bind = function bind(fn, thisArg) { var bind = function bind(fn, thisArg) {
@ -19041,6 +19041,71 @@ const getOffset = (el) => {
} }
return { top: y, left: x }; return { top: y, left: x };
}; };
const createEpisodesView = (playController, plexProtocol, ip, port, token, data) => {
const episodeContainer = document.createElement('div');
episodeContainer.className = 'episodeContainer';
episodeContainer.style.width = `${CSS_STYLE.episodeWidth}px`;
const episodeThumbURL = `${plexProtocol}://${ip}:${port}/photo/:/transcode?width=${CSS_STYLE.episodeWidth}&height=${CSS_STYLE.episodeHeight}&minSize=1&upscale=1&url=${data.thumb}&X-Plex-Token=${token}`;
const episodeElem = document.createElement('div');
episodeElem.className = 'episodeElem';
episodeElem.style.width = `${CSS_STYLE.episodeWidth}px`;
episodeElem.style.height = `${CSS_STYLE.episodeHeight}px`;
episodeElem.style.backgroundImage = `url('${episodeThumbURL}')`;
episodeElem.dataset.clicked = 'false';
if (typeof data.lastViewedAt === 'undefined') {
const toViewElem = document.createElement('div');
toViewElem.className = 'toViewEpisode';
episodeElem.appendChild(toViewElem);
}
if (playController.isPlaySupported(data)) {
const episodeInteractiveArea = document.createElement('div');
episodeInteractiveArea.className = 'interactiveArea';
const episodePlayButton = document.createElement('button');
episodePlayButton.name = 'playButton';
episodePlayButton.addEventListener('click', episodeEvent => {
episodeEvent.stopPropagation();
playController.play(data, true);
});
episodeInteractiveArea.append(episodePlayButton);
episodeElem.append(episodeInteractiveArea);
}
episodeContainer.append(episodeElem);
const episodeTitleElem = document.createElement('div');
episodeTitleElem.className = 'episodeTitleElem';
episodeTitleElem.innerHTML = escapeHtml(data.title);
episodeContainer.append(episodeTitleElem);
const episodeNumber = document.createElement('div');
episodeNumber.className = 'episodeNumber';
if (data.type === 'episode') {
episodeNumber.innerHTML = escapeHtml(`Episode ${escapeHtml(data.index)}`);
}
else if (data.type === 'clip') {
let text = '';
switch (data.subtype) {
case 'behindTheScenes':
text = 'Behind the Scenes';
break;
case 'trailer':
text = 'Trailer';
break;
case 'scene':
text = 'Scene';
break;
case 'sceneOrSample':
text = 'Scene';
break;
default:
text = data.subtype;
break;
}
episodeNumber.innerHTML = escapeHtml(text);
}
episodeContainer.append(episodeNumber);
episodeContainer.addEventListener('click', episodeEvent => {
episodeEvent.stopPropagation();
});
return episodeContainer;
};
const isScrolledIntoView = (elem) => { const isScrolledIntoView = (elem) => {
const rect = elem.getBoundingClientRect(); const rect = elem.getBoundingClientRect();
const elemTop = rect.top; const elemTop = rect.top;
@ -19558,6 +19623,14 @@ style.textContent = css `
margin-right: 10px; margin-right: 10px;
transition: 0.5s; transition: 0.5s;
} }
.movieExtras {
z-index: 4;
position: absolute;
top: 340px;
width: calc(100% - 32px);
left: 0;
padding: 16px;
}
.interactiveArea { .interactiveArea {
position: relative; position: relative;
width: 100%; width: 100%;
@ -20134,49 +20207,8 @@ class PlexMeetsHomeAssistant extends HTMLElement {
this.episodesElem.style.transition = `0s`; this.episodesElem.style.transition = `0s`;
this.episodesElem.style.top = `${top + 2000}px`; this.episodesElem.style.top = `${top + 2000}px`;
lodash.forEach(episodesData, episodeData => { lodash.forEach(episodesData, episodeData => {
if (this.episodesElem) { if (this.episodesElem && this.playController) {
const episodeContainer = document.createElement('div'); this.episodesElem.append(createEpisodesView(this.playController, this.plexProtocol, this.config.ip, this.config.port, this.config.token, episodeData));
episodeContainer.className = 'episodeContainer';
episodeContainer.style.width = `${CSS_STYLE.episodeWidth}px`;
const episodeThumbURL = `${this.plexProtocol}://${this.config.ip}:${this.config.port}/photo/:/transcode?width=${CSS_STYLE.episodeWidth}&height=${CSS_STYLE.episodeHeight}&minSize=1&upscale=1&url=${episodeData.thumb}&X-Plex-Token=${this.config.token}`;
const episodeElem = document.createElement('div');
episodeElem.className = 'episodeElem';
episodeElem.style.width = `${CSS_STYLE.episodeWidth}px`;
episodeElem.style.height = `${CSS_STYLE.episodeHeight}px`;
episodeElem.style.backgroundImage = `url('${episodeThumbURL}')`;
episodeElem.dataset.clicked = 'false';
if (typeof episodeData.lastViewedAt === 'undefined') {
const toViewElem = document.createElement('div');
toViewElem.className = 'toViewEpisode';
episodeElem.appendChild(toViewElem);
}
if (this.playController && this.playController.isPlaySupported(episodeData)) {
const episodeInteractiveArea = document.createElement('div');
episodeInteractiveArea.className = 'interactiveArea';
const episodePlayButton = document.createElement('button');
episodePlayButton.name = 'playButton';
episodePlayButton.addEventListener('click', episodeEvent => {
episodeEvent.stopPropagation();
if (this.plex && this.playController) {
this.playController.play(episodeData, true);
}
});
episodeInteractiveArea.append(episodePlayButton);
episodeElem.append(episodeInteractiveArea);
}
episodeContainer.append(episodeElem);
const episodeTitleElem = document.createElement('div');
episodeTitleElem.className = 'episodeTitleElem';
episodeTitleElem.innerHTML = escapeHtml(episodeData.title);
episodeContainer.append(episodeTitleElem);
const episodeNumber = document.createElement('div');
episodeNumber.className = 'episodeNumber';
episodeNumber.innerHTML = escapeHtml(`Episode ${escapeHtml(episodeData.index)}`);
episodeContainer.append(episodeNumber);
episodeContainer.addEventListener('click', episodeEvent => {
episodeEvent.stopPropagation();
});
this.episodesElem.append(episodeContainer);
} }
}); });
clearInterval(this.episodesLoadTimeout); clearInterval(this.episodesLoadTimeout);
@ -20245,7 +20277,33 @@ class PlexMeetsHomeAssistant extends HTMLElement {
}, 200); }, 200);
} }
else { else {
console.log(await this.plex.getDetails(data.key.split('/')[3])); const movieDetails = await this.plex.getDetails(data.key.split('/')[3]);
const extras = movieDetails.Extras.Metadata;
this.episodesElemFreshlyLoaded = true;
if (this.episodesElem) {
this.episodesElemHidden = false;
this.episodesElem.style.display = 'block';
this.episodesElem.innerHTML = '';
this.episodesElem.style.transition = `0s`;
this.episodesElem.style.top = `${top + 2000}px`;
lodash.forEach(extras, extrasData => {
if (this.episodesElem && this.playController) {
this.episodesElem.append(createEpisodesView(this.playController, this.plexProtocol, this.config.ip, this.config.port, this.config.token, extrasData));
}
});
clearInterval(this.episodesLoadTimeout);
this.episodesLoadTimeout = setTimeout(() => {
if (this.episodesElem) {
this.episodesElem.style.transition = `0.7s`;
this.episodesElem.style.top = `${top + CSS_STYLE.expandedHeight + 16}px`;
this.resizeBackground();
}
}, 200);
clearInterval(this.episodesElemFreshlyLoadedTimeout);
this.episodesElemFreshlyLoadedTimeout = setTimeout(() => {
this.episodesElemFreshlyLoaded = false;
}, 700);
}
} }
} }
}; };

@ -10,8 +10,8 @@ const CSS_STYLE = {
const supported: any = { const supported: any = {
kodi: ['movie', 'episode'], kodi: ['movie', 'episode'],
androidtv: ['movie', 'show', 'season', 'episode'], androidtv: ['movie', 'show', 'season', 'episode', 'clip'],
plexPlayer: ['movie', 'show', 'season', 'episode'] plexPlayer: ['movie', 'show', 'season', 'episode', 'clip']
}; };
const LOREM_IPSUM = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec semper risus vitae aliquet interdum. Nulla facilisi. Pellentesque viverra sagittis lorem eget aliquet. Cras vehicula, purus vel consectetur mattis, ipsum arcu ullamcorper mi, id viverra purus ex eu dolor. Integer vehicula lacinia sem convallis iaculis. Nulla fermentum erat interdum, efficitur felis in, mollis neque. Vivamus luctus metus eget nisl pellentesque, placerat elementum magna eleifend. const LOREM_IPSUM = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec semper risus vitae aliquet interdum. Nulla facilisi. Pellentesque viverra sagittis lorem eget aliquet. Cras vehicula, purus vel consectetur mattis, ipsum arcu ullamcorper mi, id viverra purus ex eu dolor. Integer vehicula lacinia sem convallis iaculis. Nulla fermentum erat interdum, efficitur felis in, mollis neque. Vivamus luctus metus eget nisl pellentesque, placerat elementum magna eleifend.

@ -272,6 +272,14 @@ style.textContent = css`
margin-right: 10px; margin-right: 10px;
transition: 0.5s; transition: 0.5s;
} }
.movieExtras {
z-index: 4;
position: absolute;
top: 340px;
width: calc(100% - 32px);
left: 0;
padding: 16px;
}
.interactiveArea { .interactiveArea {
position: relative; position: relative;
width: 100%; width: 100%;

@ -1,6 +1,9 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-env browser */ /* eslint-env browser */
import _ from 'lodash'; import _ from 'lodash';
import { CSS_STYLE } from '../const';
import PlayController from './PlayController';
import Plex from './Plex';
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
const escapeHtml = (unsafe: any): string => { const escapeHtml = (unsafe: any): string => {
if (unsafe) { if (unsafe) {
@ -40,6 +43,86 @@ const getOffset = (el: Element): Record<string, any> => {
return { top: y, left: x }; return { top: y, left: x };
}; };
const createEpisodesView = (
playController: PlayController,
plexProtocol: string,
ip: string,
port: string,
token: string,
data: Record<string, any>
): HTMLElement => {
const episodeContainer = document.createElement('div');
episodeContainer.className = 'episodeContainer';
episodeContainer.style.width = `${CSS_STYLE.episodeWidth}px`;
const episodeThumbURL = `${plexProtocol}://${ip}:${port}/photo/:/transcode?width=${CSS_STYLE.episodeWidth}&height=${CSS_STYLE.episodeHeight}&minSize=1&upscale=1&url=${data.thumb}&X-Plex-Token=${token}`;
const episodeElem = document.createElement('div');
episodeElem.className = 'episodeElem';
episodeElem.style.width = `${CSS_STYLE.episodeWidth}px`;
episodeElem.style.height = `${CSS_STYLE.episodeHeight}px`;
episodeElem.style.backgroundImage = `url('${episodeThumbURL}')`;
episodeElem.dataset.clicked = 'false';
if (typeof data.lastViewedAt === 'undefined') {
const toViewElem = document.createElement('div');
toViewElem.className = 'toViewEpisode';
episodeElem.appendChild(toViewElem);
}
if (playController.isPlaySupported(data)) {
const episodeInteractiveArea = document.createElement('div');
episodeInteractiveArea.className = 'interactiveArea';
const episodePlayButton = document.createElement('button');
episodePlayButton.name = 'playButton';
episodePlayButton.addEventListener('click', episodeEvent => {
episodeEvent.stopPropagation();
playController.play(data, true);
});
episodeInteractiveArea.append(episodePlayButton);
episodeElem.append(episodeInteractiveArea);
}
episodeContainer.append(episodeElem);
const episodeTitleElem = document.createElement('div');
episodeTitleElem.className = 'episodeTitleElem';
episodeTitleElem.innerHTML = escapeHtml(data.title);
episodeContainer.append(episodeTitleElem);
const episodeNumber = document.createElement('div');
episodeNumber.className = 'episodeNumber';
if (data.type === 'episode') {
episodeNumber.innerHTML = escapeHtml(`Episode ${escapeHtml(data.index)}`);
} else if (data.type === 'clip') {
let text = '';
switch (data.subtype) {
case 'behindTheScenes':
text = 'Behind the Scenes';
break;
case 'trailer':
text = 'Trailer';
break;
case 'scene':
text = 'Scene';
break;
case 'sceneOrSample':
text = 'Scene';
break;
default:
text = data.subtype;
break;
}
episodeNumber.innerHTML = escapeHtml(text);
}
episodeContainer.append(episodeNumber);
episodeContainer.addEventListener('click', episodeEvent => {
episodeEvent.stopPropagation();
});
return episodeContainer;
};
const isScrolledIntoView = (elem: HTMLElement): boolean => { const isScrolledIntoView = (elem: HTMLElement): boolean => {
const rect = elem.getBoundingClientRect(); const rect = elem.getBoundingClientRect();
const elemTop = rect.top; const elemTop = rect.top;
@ -53,4 +136,4 @@ const isScrolledIntoView = (elem: HTMLElement): boolean => {
}; };
// eslint-disable-next-line import/prefer-default-export // eslint-disable-next-line import/prefer-default-export
export { escapeHtml, getOffset, isScrolledIntoView, getHeight }; export { escapeHtml, getOffset, isScrolledIntoView, getHeight, createEpisodesView };

@ -5,7 +5,7 @@ import _ from 'lodash';
import { supported, CSS_STYLE } from './const'; import { supported, CSS_STYLE } from './const';
import Plex from './modules/Plex'; import Plex from './modules/Plex';
import PlayController from './modules/PlayController'; import PlayController from './modules/PlayController';
import { escapeHtml, getOffset, isScrolledIntoView, getHeight } from './modules/utils'; import { escapeHtml, getOffset, isScrolledIntoView, getHeight, createEpisodesView } from './modules/utils';
import style from './modules/style'; import style from './modules/style';
class PlexMeetsHomeAssistant extends HTMLElement { class PlexMeetsHomeAssistant extends HTMLElement {
@ -637,58 +637,17 @@ class PlexMeetsHomeAssistant extends HTMLElement {
this.episodesElem.style.transition = `0s`; this.episodesElem.style.transition = `0s`;
this.episodesElem.style.top = `${top + 2000}px`; this.episodesElem.style.top = `${top + 2000}px`;
_.forEach(episodesData, episodeData => { _.forEach(episodesData, episodeData => {
if (this.episodesElem) { if (this.episodesElem && this.playController) {
const episodeContainer = document.createElement('div'); this.episodesElem.append(
episodeContainer.className = 'episodeContainer'; createEpisodesView(
episodeContainer.style.width = `${CSS_STYLE.episodeWidth}px`; this.playController,
const episodeThumbURL = `${this.plexProtocol}://${this.config.ip}:${this.config.port}/photo/:/transcode?width=${CSS_STYLE.episodeWidth}&height=${CSS_STYLE.episodeHeight}&minSize=1&upscale=1&url=${episodeData.thumb}&X-Plex-Token=${this.config.token}`; this.plexProtocol,
this.config.ip,
const episodeElem = document.createElement('div'); this.config.port,
episodeElem.className = 'episodeElem'; this.config.token,
episodeElem.style.width = `${CSS_STYLE.episodeWidth}px`; episodeData
episodeElem.style.height = `${CSS_STYLE.episodeHeight}px`; )
episodeElem.style.backgroundImage = `url('${episodeThumbURL}')`; );
episodeElem.dataset.clicked = 'false';
if (typeof episodeData.lastViewedAt === 'undefined') {
const toViewElem = document.createElement('div');
toViewElem.className = 'toViewEpisode';
episodeElem.appendChild(toViewElem);
}
if (this.playController && this.playController.isPlaySupported(episodeData)) {
const episodeInteractiveArea = document.createElement('div');
episodeInteractiveArea.className = 'interactiveArea';
const episodePlayButton = document.createElement('button');
episodePlayButton.name = 'playButton';
episodePlayButton.addEventListener('click', episodeEvent => {
episodeEvent.stopPropagation();
if (this.plex && this.playController) {
this.playController.play(episodeData, true);
}
});
episodeInteractiveArea.append(episodePlayButton);
episodeElem.append(episodeInteractiveArea);
}
episodeContainer.append(episodeElem);
const episodeTitleElem = document.createElement('div');
episodeTitleElem.className = 'episodeTitleElem';
episodeTitleElem.innerHTML = escapeHtml(episodeData.title);
episodeContainer.append(episodeTitleElem);
const episodeNumber = document.createElement('div');
episodeNumber.className = 'episodeNumber';
episodeNumber.innerHTML = escapeHtml(`Episode ${escapeHtml(episodeData.index)}`);
episodeContainer.append(episodeNumber);
episodeContainer.addEventListener('click', episodeEvent => {
episodeEvent.stopPropagation();
});
this.episodesElem.append(episodeContainer);
} }
}); });
clearInterval(this.episodesLoadTimeout); clearInterval(this.episodesLoadTimeout);
@ -762,7 +721,47 @@ class PlexMeetsHomeAssistant extends HTMLElement {
} }
}, 200); }, 200);
} else { } else {
console.log(await this.plex.getDetails(data.key.split('/')[3])); const movieDetails = await this.plex.getDetails(data.key.split('/')[3]);
const extras = movieDetails.Extras.Metadata;
this.episodesElemFreshlyLoaded = true;
if (this.episodesElem) {
this.episodesElemHidden = false;
this.episodesElem.style.display = 'block';
this.episodesElem.innerHTML = '';
this.episodesElem.style.transition = `0s`;
this.episodesElem.style.top = `${top + 2000}px`;
_.forEach(extras, extrasData => {
if (this.episodesElem && this.playController) {
this.episodesElem.append(
createEpisodesView(
this.playController,
this.plexProtocol,
this.config.ip,
this.config.port,
this.config.token,
extrasData
)
);
}
});
clearInterval(this.episodesLoadTimeout);
this.episodesLoadTimeout = setTimeout(() => {
if (this.episodesElem) {
this.episodesElem.style.transition = `0.7s`;
this.episodesElem.style.top = `${top + CSS_STYLE.expandedHeight + 16}px`;
this.resizeBackground();
}
}, 200);
clearInterval(this.episodesElemFreshlyLoadedTimeout);
this.episodesElemFreshlyLoadedTimeout = setTimeout(() => {
this.episodesElemFreshlyLoaded = false;
}, 700);
}
} }
} }
}; };

Loading…
Cancel
Save