Merge pull request #463 from adrienbrault/plex

Finish plex implementation
This commit is contained in:
Paulus Schoutsen 2015-10-01 18:30:24 -07:00
commit cc5217d818
2 changed files with 104 additions and 55 deletions

View File

@ -30,16 +30,20 @@ The Plex password
"""
import logging
from datetime import timedelta
from homeassistant.components.media_player import (
MediaPlayerDevice, SUPPORT_PAUSE, SUPPORT_PREVIOUS_TRACK,
SUPPORT_NEXT_TRACK, MEDIA_TYPE_TVSHOW, MEDIA_TYPE_VIDEO)
from homeassistant.const import (
STATE_IDLE, STATE_PLAYING, STATE_PAUSED, STATE_UNKNOWN)
STATE_IDLE, STATE_PLAYING, STATE_PAUSED, STATE_OFF, STATE_UNKNOWN)
import homeassistant.util as util
REQUIREMENTS = ['https://github.com/miniconfig/python-plexapi/archive/'
'437e36dca3b7780dc0cb73941d662302c0cd2fa9.zip'
REQUIREMENTS = ['https://github.com/adrienbrault/python-plexapi/archive/'
'df2d0847e801d6d5cda920326d693cf75f304f1a.zip'
'#python-plexapi==1.0.2']
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(seconds=1)
_LOGGER = logging.getLogger(__name__)
@ -52,15 +56,57 @@ SUPPORT_PLEX = SUPPORT_PAUSE | SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the plex platform. """
from plexapi.myplex import MyPlexUser
from plexapi.exceptions import BadRequest
name = config.get('name', '')
user = config.get('user', '')
password = config.get('password', '')
plexuser = MyPlexUser.signin(user, password)
plexserver = plexuser.getResource(name).connect()
dev = plexserver.clients()
for device in dev:
if "PlayStation" not in device.name:
add_devices([PlexClient(device.name, plexserver)])
plex_clients = {}
plex_sessions = {}
@util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS)
def update_devices():
""" Updates the devices objects """
try:
devices = plexuser.devices()
except BadRequest:
_LOGGER.exception("Error listing plex devices")
return
new_plex_clients = []
for device in devices:
if (all(x not in ['client', 'player'] for x in device.provides)
or 'PlexAPI' == device.product):
continue
if device.clientIdentifier not in plex_clients:
new_client = PlexClient(device, plex_sessions, update_devices,
update_sessions)
plex_clients[device.clientIdentifier] = new_client
new_plex_clients.append(new_client)
else:
plex_clients[device.clientIdentifier].set_device(device)
if new_plex_clients:
add_devices(new_plex_clients)
@util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS)
def update_sessions():
""" Updates the sessions objects """
try:
sessions = plexserver.sessions()
except BadRequest:
_LOGGER.exception("Error listing plex sessions")
return
plex_sessions.clear()
for session in sessions:
plex_sessions[session.player.machineIdentifier] = session
update_devices()
update_sessions()
class PlexClient(MediaPlayerDevice):
@ -68,50 +114,61 @@ class PlexClient(MediaPlayerDevice):
# pylint: disable=too-many-public-methods
def __init__(self, name, plexserver):
self.client = plexserver.client(name)
self._name = name
self._media = None
self.update()
self.server = plexserver
def __init__(self, device, plex_sessions, update_devices, update_sessions):
self.plex_sessions = plex_sessions
self.update_devices = update_devices
self.update_sessions = update_sessions
self.set_device(device)
def set_device(self, device):
""" Sets the device property """
self.device = device
@property
def session(self):
""" Returns the session, if any """
if self.device.clientIdentifier not in self.plex_sessions:
return None
return self.plex_sessions[self.device.clientIdentifier]
@property
def name(self):
""" Returns the name of the device. """
return self._name
return self.device.name or self.device.product or self.device.device
@property
def state(self):
""" Returns the state of the device. """
if self._media is None:
return STATE_IDLE
else:
state = self._media.get('state')
if self.session:
state = self.session.player.state
if state == 'playing':
return STATE_PLAYING
elif state == 'paused':
return STATE_PAUSED
elif self.device.isReachable:
return STATE_IDLE
else:
return STATE_OFF
return STATE_UNKNOWN
def update(self):
timeline = self.client.timeline()
for timeline_item in timeline:
if timeline_item.get('state') in ('playing', 'paused'):
self._media = timeline_item
self.update_devices(no_throttle=True)
self.update_sessions(no_throttle=True)
@property
def media_content_id(self):
""" Content ID of current playing media. """
if self._media is not None:
return self._media.get('ratingKey')
if self.session is not None:
return self.session.ratingKey
@property
def media_content_type(self):
""" Content type of current playing media. """
if self._media is None:
if self.session is None:
return None
media_type = self.server.library.getByKey(
self.media_content_id).type
media_type = self.session.type
if media_type == 'episode':
return MEDIA_TYPE_TVSHOW
elif media_type == 'movie':
@ -121,50 +178,42 @@ class PlexClient(MediaPlayerDevice):
@property
def media_duration(self):
""" Duration of current playing media in seconds. """
if self._media is not None:
total_time = self._media.get('duration')
return total_time
if self.session is not None:
return self.session.duration
@property
def media_image_url(self):
""" Image url of current playing media. """
if self._media is not None:
return self.server.library.getByKey(self.media_content_id).thumbUrl
return None
if self.session is not None:
return self.session.thumbUrl
@property
def media_title(self):
""" Title of current playing media. """
# find a string we can use as a title
if self._media is not None:
return self.server.library.getByKey(self.media_content_id).title
if self.session is not None:
return self.session.title
@property
def media_season(self):
""" Season of curent playing media. (TV Show only) """
if self._media is not None:
show_season = self.server.library.getByKey(
self.media_content_id).season().index
return show_season
return None
from plexapi.video import Show
if isinstance(self.session, Show):
return self.session.seasons()[0].index
@property
def media_series_title(self):
""" Series title of current playing media. (TV Show only)"""
if self._media is not None:
series_title = self.server.library.getByKey(
self.media_content_id).show().title
return series_title
return None
from plexapi.video import Show
if isinstance(self.session, Show):
return self.session.grandparentTitle
@property
def media_episode(self):
""" Episode of current playing media. (TV Show only) """
if self._media is not None:
show_episode = self.server.library.getByKey(
self.media_content_id).index
return show_episode
return None
from plexapi.video import Show
if isinstance(self.session, Show):
return self.session.index
@property
def supported_media_commands(self):
@ -173,16 +222,16 @@ class PlexClient(MediaPlayerDevice):
def media_play(self):
""" media_play media player. """
self.client.play()
self.device.play({'type': 'video'})
def media_pause(self):
""" media_pause media player. """
self.client.pause()
self.device.pause({'type': 'video'})
def media_next_track(self):
""" Send next track command. """
self.client.skipNext()
self.device.skipNext({'type': 'video'})
def media_previous_track(self):
""" Send previous track command. """
self.client.skipPrevious()
self.device.skipPrevious({'type': 'video'})

View File

@ -136,4 +136,4 @@ https://github.com/balloob/home-assistant-vera-api/archive/a8f823066ead6c7da6fb5
SoCo==0.11.1
# PlexAPI (media_player.plex)
https://github.com/miniconfig/python-plexapi/archive/437e36dca3b7780dc0cb73941d662302c0cd2fa9.zip#python-plexapi==1.0.2
https://github.com/adrienbrault/python-plexapi/archive/df2d0847e801d6d5cda920326d693cf75f304f1a.zip#python-plexapi==1.0.2