diff --git a/dist/plex-meets-homeassistant.js b/dist/plex-meets-homeassistant.js index 6dad0db..cafb176 100644 --- a/dist/plex-meets-homeassistant.js +++ b/dist/plex-meets-homeassistant.js @@ -17205,7 +17205,8 @@ const CSS_STYLE = { }; const supported = { kodi: ['movie', 'episode'], - androidtv: ['movie', 'show', 'season', 'episode'] + androidtv: ['movie', 'show', 'season', 'episode'], + plexPlayer: ['movie', 'show', 'season', 'episode'] }; var bind = function bind(fn, thisArg) { @@ -18777,10 +18778,49 @@ class PlayController { case 'androidtv': await this.playViaAndroidTV(data.key.split('/')[3], instantPlay); break; + case 'plexPlayer': + await this.playViaPlexPlayer(data.key.split('/')[3]); + break; default: throw Error(`No service available to play ${data.title}!`); } }; + this.plexPlayerCreateQueue = async (movieID) => { + const url = `${this.plex.protocol}://${this.plex.ip}:${this.plex.port}/playQueues?type=video&shuffle=0&repeat=0&continuous=1&own=1&uri=server://${await this.plex.getServerID()}/com.plexapp.plugins.library/library/metadata/${movieID}`; + const plexResponse = await axios({ + method: 'post', + url, + headers: { + 'X-Plex-Token': this.plex.token, + 'X-Plex-Client-Identifier': 'PlexMeetsHomeAssistant' + } + }); + if (plexResponse.status !== 200) { + throw Error('Error reaching Plex to generate queue'); + } + return { + playQueueID: plexResponse.data.MediaContainer.playQueueID, + playQueueSelectedMetadataItemID: plexResponse.data.MediaContainer.playQueueSelectedMetadataItemID + }; + }; + this.playViaPlexPlayer = async (movieID) => { + const { playQueueID, playQueueSelectedMetadataItemID } = await this.plexPlayerCreateQueue(movieID); + const url = `${this.plex.protocol}://${this.plex.ip}:${this.plex.port}/player/playback/playMedia?address=${this.plex.ip}&commandID=1&containerKey=/playQueues/${playQueueID}?window=100%26own=1&key=/library/metadata/${playQueueSelectedMetadataItemID}&machineIdentifier=${await this.plex.getServerID()}&offset=0&port=${this.plex.port}&token=${this.plex.token}&type=video&protocol=${this.plex.protocol}`; + const plexResponse = await axios({ + method: 'post', + url, + headers: { + 'X-Plex-Target-Client-Identifier': this.entity.plexPlayer, + 'X-Plex-Client-Identifier': 'PlexMeetsHomeAssistant' + } + }); + if (plexResponse.status !== 200) { + throw Error('Error while asking plex to play a movie - server request error.'); + } + if (!lodash.includes(plexResponse.data, 'status="OK"')) { + throw Error('Error while asking plex to play a movie - target device not available.'); + } + }; this.playViaKodi = async (data, type) => { if (type === 'movie') { const kodiData = await this.getKodiSearch(data.title); @@ -18841,7 +18881,9 @@ class PlayController { let service = ''; lodash.forEach(this.entity, (value, key) => { if (lodash.includes(this.supported[key], data.type)) { - if ((key === 'kodi' && this.isKodiSupported()) || (key === 'androidtv' && this.isAndroidTVSupported())) { + if ((key === 'kodi' && this.isKodiSupported()) || + (key === 'androidtv' && this.isAndroidTVSupported()) || + (key === 'plexPlayer' && this.isPlexPlayerSupported())) { service = key; return false; } @@ -18849,6 +18891,10 @@ class PlayController { }); return service; }; + // todo: finish check + this.isPlexPlayerSupported = () => { + return true; + }; this.isPlaySupported = (data) => { return !lodash.isEmpty(this.getPlayService(data)); }; diff --git a/src/const.ts b/src/const.ts index 6f7313d..2288ea8 100644 --- a/src/const.ts +++ b/src/const.ts @@ -10,7 +10,8 @@ const CSS_STYLE = { const supported: any = { kodi: ['movie', 'episode'], - androidtv: ['movie', 'show', 'season', 'episode'] + androidtv: ['movie', 'show', 'season', 'episode'], + plexPlayer: ['movie', 'show', 'season', 'episode'] }; 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. diff --git a/src/modules/PlayController.ts b/src/modules/PlayController.ts index 8e93d01..03a350b 100644 --- a/src/modules/PlayController.ts +++ b/src/modules/PlayController.ts @@ -2,6 +2,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { HomeAssistant } from 'custom-card-helpers'; import _ from 'lodash'; +import axios from 'axios'; import Plex from './Plex'; import { supported } from '../const'; @@ -88,11 +89,60 @@ class PlayController { case 'androidtv': await this.playViaAndroidTV(data.key.split('/')[3], instantPlay); break; + case 'plexPlayer': + await this.playViaPlexPlayer(data.key.split('/')[3]); + break; default: throw Error(`No service available to play ${data.title}!`); } }; + private plexPlayerCreateQueue = async (movieID: number): Promise> => { + const url = `${this.plex.protocol}://${this.plex.ip}:${ + this.plex.port + }/playQueues?type=video&shuffle=0&repeat=0&continuous=1&own=1&uri=server://${await this.plex.getServerID()}/com.plexapp.plugins.library/library/metadata/${movieID}`; + + const plexResponse = await axios({ + method: 'post', + url, + headers: { + 'X-Plex-Token': this.plex.token, + 'X-Plex-Client-Identifier': 'PlexMeetsHomeAssistant' + } + }); + if (plexResponse.status !== 200) { + throw Error('Error reaching Plex to generate queue'); + } + return { + playQueueID: plexResponse.data.MediaContainer.playQueueID, + playQueueSelectedMetadataItemID: plexResponse.data.MediaContainer.playQueueSelectedMetadataItemID + }; + }; + + private playViaPlexPlayer = async (movieID: number): Promise => { + const { playQueueID, playQueueSelectedMetadataItemID } = await this.plexPlayerCreateQueue(movieID); + + const url = `${this.plex.protocol}://${this.plex.ip}:${this.plex.port}/player/playback/playMedia?address=${ + this.plex.ip + }&commandID=1&containerKey=/playQueues/${playQueueID}?window=100%26own=1&key=/library/metadata/${playQueueSelectedMetadataItemID}&machineIdentifier=${await this.plex.getServerID()}&offset=0&port=${ + this.plex.port + }&token=${this.plex.token}&type=video&protocol=${this.plex.protocol}`; + const plexResponse = await axios({ + method: 'post', + url, + headers: { + 'X-Plex-Target-Client-Identifier': this.entity.plexPlayer, + 'X-Plex-Client-Identifier': 'PlexMeetsHomeAssistant' + } + }); + if (plexResponse.status !== 200) { + throw Error('Error while asking plex to play a movie - server request error.'); + } + if (!_.includes(plexResponse.data, 'status="OK"')) { + throw Error('Error while asking plex to play a movie - target device not available.'); + } + }; + private playViaKodi = async (data: Record, type: string): Promise => { if (type === 'movie') { const kodiData = await this.getKodiSearch(data.title); @@ -159,7 +209,11 @@ class PlayController { let service = ''; _.forEach(this.entity, (value, key) => { if (_.includes(this.supported[key], data.type)) { - if ((key === 'kodi' && this.isKodiSupported()) || (key === 'androidtv' && this.isAndroidTVSupported())) { + if ( + (key === 'kodi' && this.isKodiSupported()) || + (key === 'androidtv' && this.isAndroidTVSupported()) || + (key === 'plexPlayer' && this.isPlexPlayerSupported()) + ) { service = key; return false; } @@ -168,6 +222,11 @@ class PlayController { return service; }; + // todo: finish check + isPlexPlayerSupported = (): boolean => { + return true; + }; + isPlaySupported = (data: Record): boolean => { return !_.isEmpty(this.getPlayService(data)); };