FORUMS
Remove All Ads from XDA

Plex / Python / Chromecast

10 posts
Thanks Meter: 4
 
By linc-thra, Junior Member on 16th December 2016, 10:51 PM
Post Reply Email Thread
1st January 2017, 05:54 PM |#11  
Senior Member
Thanks Meter: 374
 
More
Is there a reason why you are trying to do this and not use the Plex App? Is it just a coding experiment or is there some reason why you want to bypass the Plex app?
 
 
1st January 2017, 07:09 PM |#12  
OP Junior Member
Thanks Meter: 4
 
More
Quote:
Originally Posted by Asphyx

Is there a reason why you are trying to do this and not use the Plex App? Is it just a coding experiment or is there some reason why you want to bypass the Plex app?

There is a reason. Now that this is working, I have tied it to my Google home without waiting a year for Plex to get their support sorted. I can now say, "Okay Google, watch Westworld season 1 episode 1" and it will start playing the appropriate episode on my Chromecast from Plex.
The Following User Says Thank You to linc-thra For This Useful Post: [ View ] Gift linc-thra Ad-Free
1st January 2017, 07:14 PM |#13  
Senior Member
Thanks Meter: 374
 
More
Ahhhh...Good reason!
I agree Plex Devs are pretty slow at keeping up with the world around it.
2nd January 2017, 02:52 AM |#14  
Junior Member
Thanks Meter: 2
 
More
Quote:
Originally Posted by MechaTech84

Can you provide some more details on how you did the LOAD command part? That's the part I'm having the most trouble with.

I grabbed the relevant byte code cleaning it up I ended up with:
Code:
{"type":"LOAD","requestId":481982064,"sessionId":"81c3b38d-b2f4-4c33-929a-5365af184d70","media":
    {"contentId":"/library/metadata/105","streamType":"BUFFERED","contentType":"video","customData":
        {"offset":0,"directPlay":true,"directStream":true,"subtitleSize":100,"audioBoost":100,"server":
            {       
"machineIdentifier":"9a35df949e05bc86d0aa792c56e3db68c0c36250","transcoderVideo":true,"transcoderVideoRemuxOnly":false,"transcoderAudio":true,"version":"1.1.4.2757","myPlexSubscription":true,"isVerifiedHostname":false,"protocol":"http","address":"10.1.3.200","port":"32400","accessToken":"transient-74cce00a-4048-4fcc-a571-38f2fd9a2acf"
            },
            "user":{"username":"XXXXXXXX"},
            "containerKey":"/playQueues/1635?own=1&window=200"
        }
    },
    "autoplay":true,"currentTime":0
}
I haven't finished the method but here's what I have all the variables (except the last 2) are currently hardcoded.
Code:
def play_item(self, key, content_data, play_data, server_data, user_data, access_token, play_queue_id):
        key = "/library/metadata/105"
        requestId = self._socket_client._request_id
        sessionId = self._socket_client.session_id
        play_data = {"offset":0,"directPlay":True,"directStream":True,"subtitleSize":100,"audioBoost":100}
        server_data = {"machineIdentifier":"9a35df949e05bc86d0aa792c56e3db68c0c36250","transcoderVideo":True,"transcoderVideoRemuxOnly":False,"transcoderAudio":True,"version":"1.1.4.2757","myPlexSubscription":True,"isVerifiedHostname":False,"protocol":"http","address":"10.1.3.200","port":"32400"}
        user_data = {"username":"XXXXXX"}
        content_data = {"streamType":"BUFFERED","contentType":"video"}
        msg = {MESSAGE_TYPE:TYPE_LOAD,'requestId':requestId,'sessionId':sessionId}
        msg['media'] = {'contentId':key}
        msg['media'].update(content_data)
        msg['media']['customData'] = play_data.copy()
        msg['media']['customData']['server'] = server_data.copy()
        msg['media']['customData']['server']['accessToken'] = access_token
        msg['media']['customData']['user'] = user_data.copy()
        msg['media']['customData']['containerKey'] = '/playQueues/%s?own=1&window=200' % play_queue_id
        msg.update({'autoplay':True, 'currentTime':0})
        self.namespace = 'urn:x-cast:com.google.cast.media'
        self.send_message(msg)
        self.namesapce = 'urn:x-cast:plex'
Here's my full controller code.
I'm not 100% sure that I have the right session and request Ids, but assume they are the most likely to use.

Hopefully this helps.

Code:
MESSAGE_TYPE = 'type'
TYPE_SHOWDETAILS = 'SHOWDETAILS'
TYPE_LOAD = 'LOAD'

class MyPlexController(PlexController):
    def __init__(self):
        super().__init__()

    def receive_message(self, message, data):
        logging.info('PlexController: I received this message: {}'.format(data))
        return True  # indicate you handled this message

    def send_message(self, data, inc_session_id=False, callback_function=None):
        logging.info('PlexController: I send this message: {}'.format(data))
        super().send_message(data, inc_session_id, callback_function)
    
    def show_details(self, key, content_data, server_data, user_data, access_token):
        key = "/library/metadata/105"
        server_data = {"machineIdentifier":"9a35df949e05bc86d0aa792c56e3db68c0c36250","transcoderVideo":True,"transcoderVideoRemuxOnly":False,"transcoderAudio":True,"version":"1.1.4.2757","myPlexSubscription":True,"isVerifiedHostname":False,"protocol":"http","address":"10.1.3.200","port":"32400"}
        user_data = {"username":"XXXXXX"}
        content_data = {"streamType":"BUFFERED","contentType":"video"}
        msg = {MESSAGE_TYPE:TYPE_SHOWDETAILS}
        msg['media'] = {'contentId':key}
        msg['media'].update(content_data)
        msg['media']['customData'] = {}
        msg['media']['customData']['server'] = server_data.copy()
        msg['media']['customData']['server']['accessToken'] = access_token
        msg['media']['customData']['user'] = user_data.copy()
        self.send_message(msg)
# {"type":"SHOWDETAILS","media":
    # {"contentId":"/library/metadata/105","streamType":"BUFFERED","contentType":"video","customData":
        # {"server":
            # {"machineIdentifier":"9a35df949e05bc86d0aa792c56e3db68c0c36250","transcoderVideo":true,"transcoderVideoRemuxOnly":false,"transcoderAudio":true,"version":"1.1.4.2757","myPlexSubscription":true,"isVerifiedHostname":false,"protocol":"http","address":"10.1.3.200","port":"32400","accessToken":"transient-73832b14-e2bf-4943-b97d-468b9ae85a34"},
            # "user": {"username":"XXXXXX"}
        # }
    # }
# }
    
    def play_item(self, key, content_data, play_data, server_data, user_data, access_token, play_queue_id):
        key = "/library/metadata/105"
        requestId = self._socket_client._request_id
        sessionId = self._socket_client.session_id
        play_data = {"offset":0,"directPlay":True,"directStream":True,"subtitleSize":100,"audioBoost":100}
        server_data = {"machineIdentifier":"9a35df949e05bc86d0aa792c56e3db68c0c36250","transcoderVideo":True,"transcoderVideoRemuxOnly":False,"transcoderAudio":True,"version":"1.1.4.2757","myPlexSubscription":True,"isVerifiedHostname":False,"protocol":"http","address":"10.1.3.200","port":"32400"}
        user_data = {"username":"XXXXXX"}
        content_data = {"streamType":"BUFFERED","contentType":"video"}
        msg = {MESSAGE_TYPE:TYPE_LOAD,'requestId':requestId,'sessionId':sessionId}
        msg['media'] = {'contentId':key}
        msg['media'].update(content_data)
        msg['media']['customData'] = play_data.copy()
        msg['media']['customData']['server'] = server_data.copy()
        msg['media']['customData']['server']['accessToken'] = access_token
        msg['media']['customData']['user'] = user_data.copy()
        msg['media']['customData']['containerKey'] = '/playQueues/%s?own=1&window=200' % play_queue_id
        msg.update({'autoplay':True, 'currentTime':0})
        self.namespace = 'urn:x-cast:com.google.cast.media'
        self.send_message(msg)
        self.namesapce = 'urn:x-cast:plex'
# {"type":"LOAD","requestId":481982064,"sessionId":"81c3b38d-b2f4-4c33-929a-5365af184d70","media":
    # {"contentId":"/library/metadata/105","streamType":"BUFFERED","contentType":"video","customData":
        # {"offset":0,"directPlay":true,"directStream":true,"subtitleSize":100,"audioBoost":100,"server":
            # {
            # "machineIdentifier":"9a35df949e05bc86d0aa792c56e3db68c0c36250","transcoderVideo":true,"transcoderVideoRemuxOnly":false,"transcoderAudio":true,"version":"1.1.4.2757","myPlexSubscription":true,"isVerifiedHostname":false,"protocol":"http","address":"10.1.3.200","port":"32400","accessToken":"transient-74cce00a-4048-4fcc-a571-38f2fd9a2acf"
            # },
            # "user":{"username":"XXXXXX"},
            # "containerKey":"/playQueues/1635?own=1&window=200"
        # }
    # },
    # "autoplay":true,"currentTime":0
# }
4th January 2017, 02:59 AM |#15  
Junior Member
Thanks Meter: 2
 
More
Quote:
Originally Posted by linc-thra

Ah, I had NOT indeed worked out the transient token bit. I felt like maybe I needed to fetch one, but hadn't quite worked out how. Are you sending a SETSTREAM and a LOAD or just a LOAD to actually get things playing? If a SETSTREAM, is that going to the plex namespace or the media namespace?

I just used a LOAD.
For SETSTREAM I'm using the following in my controller (using the Plex namespace).

Code:
def _send_command(self, chromecast, command):
        chromecast.register_handler(self)
        mc = chromecast.media_controller
        if mc.status is None or mc.status.media_session_id is None:
            raise PlexControllerException('No media_session_id was found unable to send command {}'.format(command))
        command['mediaSessionId'] = mc.status.media_session_id
        self.send_message(command, inc_session_id=True)

    def set_quality(self, chromecast, bitrate): self._send_command(chromecast, {"type":"SETQUALITY","bitrate":bitrate})
    def set_subtitles(self, chromecast, subtitle_id): self._send_command(chromecast, {"type":"SETSTREAM","stream":{"type":"subtitles","id":subtitle_id}})
    def disable_subtitles(self, chromecast): self._send_command(chromecast, {"type":"SETSTREAM","stream":{"type":"subtitles","id":0}})
    def set_audio(self, chromecast, audio_id): self._send_command(chromecast, {"type":"SETSTREAM","stream":{"type":"audio","id":audio_id}})
    def set_video(self, chromecast, video_id): self._send_command(chromecast, {"type":"SETSTREAM","stream":{"type":"video","id":video_id}})
You can get the resprective Ids from via the plexapi
Code:
videostreams = [s.id for s in video.videoStreams]
audiostreams = [s.id for s in video.audioStreams]
subtitlestreams = [s.id for s in video.subtitleStreams]
24th May 2017, 10:52 PM |#16  
Member
Thanks Meter: 3
 
More
Quote:
Originally Posted by linc-thra

I'm headed out to dinner and won't be back for a while, BUT, it requires editing the plex.py file in the controller folder of pychromecast. I JUST got it to start playing and the like myself thanks to mcneishh's hints. I'm attaching a basic functional PlexApiController:

http://pastebin.com/qeLYZpW4

I'll try to improve it later.
An example of how to use this:

Code:
import pychromecast.controllers.plexapi as px
import pychromecast
from plexapi.myplex import MyPlexAccount
account = MyPlexAccount.signin('<USERNAME>', '<PASSWORD')
plex = account.resource('<SERVER_NAME>').connect()
pxr = px.PlexController()
cast = pychromecast.Chromecast("<CHROMECASTIP")
cast.register_handler(pxr)
pxr.namespace = 'urn:x-cast:com.google.cast.sse'
white = plex.library.section("TV Shows").get("White Collar")
epi = white.seasons()[0].episodes()[0]
pxr.play_media(epi,plex)

Hi, thanks a lot for your code! I managed to connect to the Chromecast using pychromecast and to plexapi, but unfortunately every time I try to play something I get "unable to cast this media is currently unavailable" on my Chromecast. Starting it manually works.

Do you have any ideas?
25th May 2017, 07:30 PM |#17  
OP Junior Member
Thanks Meter: 4
 
More
Quote:
Originally Posted by bluebird11

Hi, thanks a lot for your code! I managed to connect to the Chromecast using pychromecast and to plexapi, but unfortunately every time I try to play something I get "unable to cast this media is currently unavailable" on my Chromecast. Starting it manually works.

Do you have any ideas?

Hmmm... not really, other than possibly you're calling or referencing the episode wrong from plexapi?
28th May 2017, 08:27 PM |#18  
Member
Thanks Meter: 3
 
More
Quote:
Originally Posted by linc-thra

Hmmm... not really, other than possibly you're calling or referencing the episode wrong from plexapi?

Hmm, I'm getting it with the following code:

Code:
movies = plex.library.section('Movies')
for video in movies.search(unwatched=True):
    print(video.title)
    epi = plex.library.section('Movies').get(video.title)
    break
Another question: Do I need to connect a client first? I.e. do I have to connect to Plex with a browser or the app before I can do anything?
29th May 2017, 06:35 PM |#19  
OP Junior Member
Thanks Meter: 4
 
More
Quote:
Originally Posted by bluebird11

Hmm, I'm getting it with the following code:

Code:
movies = plex.library.section('Movies')
for video in movies.search(unwatched=True):
    print(video.title)
    epi = plex.library.section('Movies').get(video.title)
    break
Another question: Do I need to connect a client first? I.e. do I have to connect to Plex with a browser or the app before I can do anything?

To answer your second question first: No, the controller should connect itself just find with no browser involved. The first question is more complicated... mainly because your code works for me.... so long as you are trying to get the first unwatched movie in the movie section. I didn't have any marked as "unwatched" to start with and it fell over, but once I marked one as unwatched, that code ran for me fine.
2nd June 2017, 09:32 PM |#20  
Member
Thanks Meter: 3
 
More
Quote:
Originally Posted by linc-thra

To answer your second question first: No, the controller should connect itself just find with no browser involved. The first question is more complicated... mainly because your code works for me.... so long as you are trying to get the first unwatched movie in the movie section. I didn't have any marked as "unwatched" to start with and it fell over, but once I marked one as unwatched, that code ran for me fine.

Thanks for the reply, it works now. Connecting directly to the plex server in the local network doesn't seem to work. What worked is connecting over plex.tv as shown here... I thought that it doesn't matter.

Anyway, what I was hoping for is that after connecting it would show up as a device / client in the Plex app so I could control it from there (and from amazon echo), but it doesn't show up Do you think that would be possible? If I connect manually from a browser it shows up as "Plex Web (Chrome)".
3rd June 2017, 05:47 PM |#21  
OP Junior Member
Thanks Meter: 4
 
More
Quote:
Originally Posted by bluebird11

Thanks for the reply, it works now. Connecting directly to the plex server in the local network doesn't seem to work. What worked is connecting over plex.tv as shown here... I thought that it doesn't matter.

Anyway, what I was hoping for is that after connecting it would show up as a device / client in the Plex app so I could control it from there (and from amazon echo), but it doesn't show up Do you think that would be possible? If I connect manually from a browser it shows up as "Plex Web (Chrome)".

Well, once you are telling it to play on the Chromecast, the Chromecast is the 'client,' not the python script. Personally, my setup is that IFTTT catches commands from my Home Assistant, and then over their "Maker" app, I send a command to a URL on my personal web server, which then sends a command to my Chromecast. I'd think what'd you need to do is get Alexa to send commands to the Chromecast in whatever way, not some other player? Maybe Alexa->IFTTT->Maker->Web Server->Python Script->Chromecast?

All that said, I think I remember seeing that Plex HAS an Alexa app at this point?
Post Reply Subscribe to Thread

Guest Quick Reply (no urls or BBcode)
Message:
Previous Thread Next Thread
Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes