Added support for Plex to Chromecast

This commit is contained in:
Fabio Manganiello 2018-11-13 01:29:24 +01:00
parent 42053dcf3b
commit 26f3842724
4 changed files with 150 additions and 25 deletions

View file

@ -59,7 +59,7 @@ class MediaChromecastPlugin(Plugin):
} for cc in pychromecast.get_chromecasts() ]
def _get_chromecast(self, chromecast=None):
def get_chromecast(self, chromecast=None):
if not chromecast:
if not self.chromecast:
raise RuntimeError('No Chromecast specified nor default Chromecast configured')
@ -67,16 +67,12 @@ class MediaChromecastPlugin(Plugin):
if chromecast not in self.chromecasts:
chromecasts = pychromecast.get_chromecasts()
cast = next(cc for cc in pychromecast.get_chromecasts()
self.chromecasts = pychromecast.get_chromecasts()
cast = next(cc for cc in self.chromecasts
if cc.device.friendly_name == chromecast)
self.chromecasts[chromecast] = cast
else:
cast = self.chromecasts[chromecast]
if not cast:
raise RuntimeError('No such Chromecast: {}'.format(chromecast))
return cast
@action
@ -125,9 +121,11 @@ class MediaChromecastPlugin(Plugin):
:type subtitle_id: int
"""
cast = self._get_chromecast(chromecast)
cast = self.get_chromecast(chromecast)
cast.wait()
mc = cast.media_controller
mc.namespace = 'urn:x-cast:com.google.cast.sse'
mc.play_media(media, content_type, title=title, thumb=image_url,
current_time=current_time, autoplay=autoplay,
stream_type=stream_type, subtitles=subtitles,
@ -151,7 +149,7 @@ class MediaChromecastPlugin(Plugin):
:type blocking: bool
"""
cast = self._get_chromecast(chromecast)
cast = self.get_chromecast(chromecast)
cast.disconnect(timeout=timeout, blocking=blocking)
@action
@ -169,7 +167,7 @@ class MediaChromecastPlugin(Plugin):
:type blocking: bool
"""
cast = self._get_chromecast(chromecast)
cast = self.get_chromecast(chromecast)
cast.join(timeout=timeout, blocking=blocking)
@action
@ -181,7 +179,7 @@ class MediaChromecastPlugin(Plugin):
:type chromecast: str
"""
cast = self._get_chromecast(chromecast)
cast = self.get_chromecast(chromecast)
cast.quit_app()
@action
@ -193,7 +191,7 @@ class MediaChromecastPlugin(Plugin):
:type chromecast: str
"""
cast = self._get_chromecast(chromecast)
cast = self.get_chromecast(chromecast)
cast.reboot()
@action
@ -208,7 +206,7 @@ class MediaChromecastPlugin(Plugin):
:type chromecast: str
"""
cast = self._get_chromecast(chromecast)
cast = self.get_chromecast(chromecast)
cast.set_volume(volume/100)
@action
@ -223,7 +221,7 @@ class MediaChromecastPlugin(Plugin):
:type delta: float
"""
cast = self._get_chromecast(chromecast)
cast = self.get_chromecast(chromecast)
delta /= 100
cast.volume_up(min(delta, 1))
@ -240,7 +238,7 @@ class MediaChromecastPlugin(Plugin):
:type delta: float
"""
cast = self._get_chromecast(chromecast)
cast = self.get_chromecast(chromecast)
delta /= 100
cast.volume_down(max(delta, 0))
@ -254,7 +252,7 @@ class MediaChromecastPlugin(Plugin):
:type chromecast: str
"""
cast = self._get_chromecast(chromecast)
cast = self.get_chromecast(chromecast)
cast.set_volume_muted(not cast.status.volume_muted)

View file

View file

@ -0,0 +1,91 @@
"""
Controller for Plex content on a Chromecast device
"""
from pychromecast.controllers import BaseController
MESSAGE_TYPE = 'type'
TYPE_PLAY = "PLAY"
TYPE_PAUSE = "PAUSE"
TYPE_STOP = "STOP"
class PlexController(BaseController):
""" Controller to interact with Plex namespace. """
def __init__(self):
super(PlexController, self).__init__(
"urn:x-cast:plex", "9AC194DC")
self.app_id="9AC194DC"
self.namespace="urn:x-cast:plex"
self.request_id = 0
def stop(self):
""" Send stop command. """
self.namespace = "urn:x-cast:plex"
self.request_id += 1
self.send_message({MESSAGE_TYPE: TYPE_STOP})
def pause(self):
""" Send pause command. """
self.namespace = "urn:x-cast:plex"
self.request_id += 1
self.send_message({MESSAGE_TYPE: TYPE_PAUSE})
def play(self):
""" Send play command. """
self.namespace = "urn:x-cast:plex"
self.request_id += 1
self.send_message({MESSAGE_TYPE: TYPE_PLAY})
def play_media(self,item,server,medtype="LOAD"):
def app_launched_callback():
self.set_load(item,server,medtype)
receiver_ctrl = self._socket_client.receiver_controller
if receiver_ctrl.status.app_id != self.app_id:
receiver_ctrl.launch_app(self.app_id,
callback_function=app_launched_callback)
def set_load(self,item,server,medtype="LOAD"):
transient_token = server.query("/security/token?type=delegation&scope=all").attrib.get('token')
playqueue = server.createPlayQueue(item).playQueueID
self.request_id += 1
address = server.url('').split(":")[1][2:]
self.namespace="urn:x-cast:com.google.cast.media"
msg = {
"type": medtype,
"requestId": self.request_id,
"sessionId": "python_player",
"autoplay": True,
"currentTime": 0,
"media":{
"contentId": item.key,
"streamType": "BUFFERED",
"contentType": "video",
"customData": {
"offset": 0,
"server": {
"machineIdentifier": server.machineIdentifier,
"transcoderVideo": True,
"transcoderVideoRemuxOnly": False,
"transcoderAudio": True,
"version": "1.3.3.3148",
"myPlexSubscription": False,
"isVerifiedHostname": True,
"protocol": "https",
"address": address,
"port": "32400",
"accessToken": transient_token,
},
"user": {"username": server.myPlexUsername},
"containerKey": "/playQueues/{}?own=1&window=200".format(playqueue),
},
}
}
self.send_message(msg, inc_session_id=True)
# vim:sw=4:ts=4:et:

View file

@ -1,6 +1,7 @@
from plexapi.myplex import MyPlexAccount
from plexapi.video import Movie, Show
from platypush.context import get_plugin
from platypush.plugins import Plugin, action
@ -123,34 +124,68 @@ class MediaPlexPlugin(Plugin):
]
def get_chromecast(self, chromecast):
from .lib.plexcast import PlexController
hndl = PlexController()
hndl.namespace = 'urn:x-cast:com.google.cast.sse'
cast = get_plugin('media.chromecast').get_chromecast(chromecast)
cast.register_handler(hndl)
return (cast, hndl)
@action
def play(self, client, **kwargs):
def play(self, client=None, chromecast=None, **kwargs):
"""
Search and play content on a client. If no search filter is specified,
a play event will be sent to the specified client.
Search and play content on a client or a Chromecast. If no search filter
is specified, a play event will be sent to the specified client.
NOTE: Adding and managing play queues through the Plex API isn't fully
supported yet, therefore in case multiple items are returned from the
search only the first one will be played.
:param client: Client name
:type client str
:type client: str
:param chromecast: Chromecast name
:type chromecast: str
:param kwargs: Search filter (e.g. title, section, unwatched, director etc.)
:type kwargs: dict
"""
client = plex.client(client)
if not kwargs:
return client.play()
if not client and not chromecast:
raise RuntimeError('No client nor chromecast specified')
results = self.search(**kwargs)
if client:
client = plex.client(client)
elif chromecast:
(chromecast, handler) = self.get_chromecast(chromecast)
if not kwargs:
if client:
return client.play()
elif chromecast:
return handler.play()
if 'section' in kwargs:
library = self.plex.library.section(kwargs.pop('section'))
else:
library = self.plex.library
results = library.search(**kwargs)
if not results:
self.logger.info('No results for {}'.format(kwargs))
return
item = results[0]
client.playMedia(results[0])
self.logger.info('Playing {} on {}'.format(item.title, client or chromecast))
if client:
return client.playMedia(item)
elif chromecast:
return handler.play_media(item, self.plex)
@action
@ -424,5 +459,6 @@ class MediaPlexPlugin(Plugin):
return _item
# vim:sw=4:ts=4:et: