Added support for Plex to Chromecast
This commit is contained in:
parent
42053dcf3b
commit
26f3842724
4 changed files with 150 additions and 25 deletions
|
@ -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)
|
||||
|
||||
|
||||
|
|
0
platypush/plugins/media/lib/__init__.py
Normal file
0
platypush/plugins/media/lib/__init__.py
Normal file
91
platypush/plugins/media/lib/plexcast.py
Normal file
91
platypush/plugins/media/lib/plexcast.py
Normal 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:
|
||||
|
|
@ -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:
|
||||
|
||||
|
|
Loading…
Reference in a new issue