From 365baf91b11d570cbaacc00bbfc109e879611a00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraj=20Nyi=CC=81ri?= Date: Thu, 2 Sep 2021 18:21:10 +0200 Subject: [PATCH] WIP cast --- dist/plex-meets-homeassistant.js | 55 ++++++++++++++++++++++++++++++-- src/const.ts | 2 +- src/modules/PlayController.ts | 46 +++++++++++++++++++++++++- src/modules/Plex.ts | 12 +++++++ 4 files changed, 111 insertions(+), 4 deletions(-) diff --git a/dist/plex-meets-homeassistant.js b/dist/plex-meets-homeassistant.js index 1450540..34afd6e 100644 --- a/dist/plex-meets-homeassistant.js +++ b/dist/plex-meets-homeassistant.js @@ -17207,7 +17207,7 @@ const supported = { kodi: ['movie', 'episode'], androidtv: ['movie', 'show', 'season', 'episode', 'clip'], plexPlayer: ['movie', 'show', 'season', 'episode', 'clip'], - cast: ['movie', 'episode'] + cast: ['movie', 'episode', 'epg'] }; var bind = function bind(fn, thisArg) { @@ -18942,6 +18942,13 @@ class Plex { timeout: this.requestTimeout })).data.MediaContainer; }; + this.tune = async (channelID, session) => { + // Todo: what is 12? do we need to get this from somewhere and change? + const url = this.authorizeURL(`${this.getBasicURL()}/livetv/dvrs/12/channels/${channelID}/tune?X-Plex-Session-Identifier=${session}`); + return (await axios.post(url, { + timeout: this.requestTimeout + })).data.MediaContainer; + }; this.getContinueWatching = async () => { const hubs = await this.getHubs(); let continueWatchingData = {}; @@ -19309,7 +19316,43 @@ class PlayController { await this.playViaPlexPlayer(entity.value, processData.key.split('/')[3]); break; case 'cast': - if (this.hass.services.plex) { + if (!lodash.isNil(data.epg)) { + const session = `${Math.floor(Date.now() / 1000)}`; + const streamData = await this.plex.tune(data.channelIdentifier, session); + console.log(streamData.MediaSubscription[0].MediaGrabOperation[0].Metadata.key); + let startURL = `/video/:/transcode/universal/start`; + startURL += `?hasMDE=1`; + startURL += `&path=${streamData.MediaSubscription[0].MediaGrabOperation[0].Metadata.key}`; + startURL += `&mediaIndex=0`; + startURL += `&partIndex=0`; + startURL += `&protocol=http`; + startURL += `&fastSeek=1`; + startURL += `&directPlay=0`; + startURL += `&directStream=1`; + startURL += `&subtitleSize=100`; + startURL += `&audioBoost=100`; + startURL += `&location=lan`; + startURL += `&directStreamAudio=1`; + startURL += `&mediaBufferSize=30720`; + startURL += `&session=${session}`; + startURL += `&offset=0`; + startURL += `&subtitles=burn`; + startURL += `©ts=0`; + startURL += `&X-Plex-Session-Identifier=${session}`; + startURL += `&X-Plex-Client-Profile-Extra=add-transcode-target-audio-codec%28type%3DvideoProfile%26context%3Dstreaming%26protocol%3Dhttp%26audioCodec%3Dac3%29%2Badd-limitation%28scope%3DvideoAudioCodec%26scopeName%3Dac3%26type%3DupperBound%26name%3Daudio.channel%26value%3D6%29%2Badd-transcode-target-audio-codec%28type%3DvideoProfile%26context%3Dstreaming%26protocol%3Dhttp%26audioCodec%3Deac3%29%2Badd-limitation%28scope%3DvideoAudioCodec%26scopeName%3Deac3%26type%3DupperBound%26name%3Daudio.channel%26value%3D6%29%2Badd-limitation%28scope%3DvideoAudioCodec%26scopeName%3Daac%26type%3DupperBound%26name%3Daudio.channel%26value%3D2%29%2Badd-limitation%28scope%3DvideoTranscodeTarget%26scopeName%3Dhevc%26scopeType%3DvideoCodec%26context%3Dstreaming%26protocol%3Dhttp%26type%3Dmatch%26name%3Dvideo.colorTrc%26list%3Dbt709%7Cbt470m%7Cbt470bg%7Csmpte170m%7Csmpte240m%7Cbt2020-10%7Csmpte2084%26isRequired%3Dfalse%29`; + startURL += `&X-Plex-Chunked=1`; + startURL += `&X-Plex-Product=Plex%20Cast`; + startURL += `&X-Plex-Version=4.54.1`; + startURL += `&X-Plex-Client-Identifier=3ievywhylzj29yvxxjmoyq7h`; + startURL += `&X-Plex-Platform-Version=86.0`; + startURL += `&X-Plex-Device=Android`; + startURL += `&X-Plex-Device-Name=Chromecast`; + startURL += `&X-Plex-Device-Screen-Resolution=1280x720%2C960x540`; + startURL += `&X-Plex-Token=transient-ba74f27d-1aae-4518-9047-b7fcfdd88dbb`; + console.log(startURL); + this.playViaCast(entity.value, startURL); + } + else if (this.hass.services.plex) { const libraryName = lodash.isNil(processData.librarySectionTitle) ? this.libraryName : processData.librarySectionTitle; @@ -19470,6 +19513,14 @@ class PlayController { } }; this.playViaCast = (entityName, mediaLink) => { + console.log({ + // eslint-disable-next-line @typescript-eslint/camelcase + entity_id: entityName, + // eslint-disable-next-line @typescript-eslint/camelcase + media_content_type: 'video', + // eslint-disable-next-line @typescript-eslint/camelcase + media_content_id: this.plex.authorizeURL(`${this.plex.getBasicURL()}${mediaLink}`) + }); this.hass.callService('media_player', 'play_media', { // eslint-disable-next-line @typescript-eslint/camelcase entity_id: entityName, diff --git a/src/const.ts b/src/const.ts index 12de975..e4371a4 100644 --- a/src/const.ts +++ b/src/const.ts @@ -12,7 +12,7 @@ const supported: any = { kodi: ['movie', 'episode'], androidtv: ['movie', 'show', 'season', 'episode', 'clip'], plexPlayer: ['movie', 'show', 'season', 'episode', 'clip'], - cast: ['movie', 'episode'] + cast: ['movie', 'episode', 'epg'] }; 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 91e04ae..4bc3438 100644 --- a/src/modules/PlayController.ts +++ b/src/modules/PlayController.ts @@ -131,7 +131,43 @@ class PlayController { await this.playViaPlexPlayer(entity.value, processData.key.split('/')[3]); break; case 'cast': - if (this.hass.services.plex) { + if (!_.isNil(data.epg)) { + const session = `${Math.floor(Date.now() / 1000)}`; + const streamData = await this.plex.tune(data.channelIdentifier, session); + console.log(streamData.MediaSubscription[0].MediaGrabOperation[0].Metadata.key); + let startURL = `/video/:/transcode/universal/start`; + startURL += `?hasMDE=1`; + startURL += `&path=${streamData.MediaSubscription[0].MediaGrabOperation[0].Metadata.key}`; + startURL += `&mediaIndex=0`; + startURL += `&partIndex=0`; + startURL += `&protocol=http`; + startURL += `&fastSeek=1`; + startURL += `&directPlay=0`; + startURL += `&directStream=1`; + startURL += `&subtitleSize=100`; + startURL += `&audioBoost=100`; + startURL += `&location=lan`; + startURL += `&directStreamAudio=1`; + startURL += `&mediaBufferSize=30720`; + startURL += `&session=${session}`; + startURL += `&offset=0`; + startURL += `&subtitles=burn`; + startURL += `©ts=0`; + startURL += `&X-Plex-Session-Identifier=${session}`; + startURL += `&X-Plex-Client-Profile-Extra=add-transcode-target-audio-codec%28type%3DvideoProfile%26context%3Dstreaming%26protocol%3Dhttp%26audioCodec%3Dac3%29%2Badd-limitation%28scope%3DvideoAudioCodec%26scopeName%3Dac3%26type%3DupperBound%26name%3Daudio.channel%26value%3D6%29%2Badd-transcode-target-audio-codec%28type%3DvideoProfile%26context%3Dstreaming%26protocol%3Dhttp%26audioCodec%3Deac3%29%2Badd-limitation%28scope%3DvideoAudioCodec%26scopeName%3Deac3%26type%3DupperBound%26name%3Daudio.channel%26value%3D6%29%2Badd-limitation%28scope%3DvideoAudioCodec%26scopeName%3Daac%26type%3DupperBound%26name%3Daudio.channel%26value%3D2%29%2Badd-limitation%28scope%3DvideoTranscodeTarget%26scopeName%3Dhevc%26scopeType%3DvideoCodec%26context%3Dstreaming%26protocol%3Dhttp%26type%3Dmatch%26name%3Dvideo.colorTrc%26list%3Dbt709%7Cbt470m%7Cbt470bg%7Csmpte170m%7Csmpte240m%7Cbt2020-10%7Csmpte2084%26isRequired%3Dfalse%29`; + startURL += `&X-Plex-Chunked=1`; + startURL += `&X-Plex-Product=Plex%20Cast`; + startURL += `&X-Plex-Version=4.54.1`; + startURL += `&X-Plex-Client-Identifier=3ievywhylzj29yvxxjmoyq7h`; + startURL += `&X-Plex-Platform-Version=86.0`; + startURL += `&X-Plex-Device=Android`; + startURL += `&X-Plex-Device-Name=Chromecast`; + startURL += `&X-Plex-Device-Screen-Resolution=1280x720%2C960x540`; + startURL += `&X-Plex-Token=transient-ba74f27d-1aae-4518-9047-b7fcfdd88dbb`; + console.log(startURL); + + this.playViaCast(entity.value, startURL); + } else if (this.hass.services.plex) { const libraryName = _.isNil(processData.librarySectionTitle) ? this.libraryName : processData.librarySectionTitle; @@ -305,6 +341,14 @@ class PlayController { }; private playViaCast = (entityName: string, mediaLink: string): void => { + console.log({ + // eslint-disable-next-line @typescript-eslint/camelcase + entity_id: entityName, + // eslint-disable-next-line @typescript-eslint/camelcase + media_content_type: 'video', + // eslint-disable-next-line @typescript-eslint/camelcase + media_content_id: this.plex.authorizeURL(`${this.plex.getBasicURL()}${mediaLink}`) + }); this.hass.callService('media_player', 'play_media', { // eslint-disable-next-line @typescript-eslint/camelcase entity_id: entityName, diff --git a/src/modules/Plex.ts b/src/modules/Plex.ts index 2bea9bf..39e7e06 100644 --- a/src/modules/Plex.ts +++ b/src/modules/Plex.ts @@ -365,6 +365,18 @@ class Plex { ).data.MediaContainer; }; + tune = async (channelID: string, session: string): Promise => { + // Todo: what is 12? do we need to get this from somewhere and change? + const url = this.authorizeURL( + `${this.getBasicURL()}/livetv/dvrs/12/channels/${channelID}/tune?X-Plex-Session-Identifier=${session}` + ); + return ( + await axios.post(url, { + timeout: this.requestTimeout + }) + ).data.MediaContainer; + }; + getContinueWatching = async (): Promise => { const hubs = await this.getHubs(); let continueWatchingData: Record = {};