Update to latest Plex API, add music support. (#2739)

* Update to latest Plex API, add music support.

* Fix PyLint errors.

* Update Plex sensor module to latest PlexAPI.

* Oops - update Python sensor import.

* According to PlexAPI docs, this is the new API for Plex Pass members.

* More pylint STFUs.

* Move pylint suppression.

* Use plexapi NA type directly.

* Pylint objects to short variable names.
This commit is contained in:
Dean Camera 2016-08-09 01:55:58 +10:00 committed by Paulus Schoutsen
parent 689939ab9d
commit 5ff9e59b79
4 changed files with 78 additions and 29 deletions

View file

@ -688,7 +688,11 @@ class MediaPlayerImageView(HomeAssistantView):
if not authenticated:
return self.Response(status=401)
response = requests.get(player.media_image_url)
image_url = player.media_image_url
if image_url:
response = requests.get(image_url)
else:
response = None
if response is None:
return self.Response(status=500)

View file

@ -12,15 +12,16 @@ from urllib.parse import urlparse
import homeassistant.util as util
from homeassistant.components.media_player import (
MEDIA_TYPE_TVSHOW, MEDIA_TYPE_VIDEO, SUPPORT_NEXT_TRACK, SUPPORT_PAUSE,
SUPPORT_PREVIOUS_TRACK, MediaPlayerDevice)
MEDIA_TYPE_TVSHOW, MEDIA_TYPE_VIDEO, MEDIA_TYPE_MUSIC, SUPPORT_NEXT_TRACK,
SUPPORT_PREVIOUS_TRACK, SUPPORT_PAUSE, SUPPORT_STOP, SUPPORT_VOLUME_SET,
MediaPlayerDevice)
from homeassistant.const import (
DEVICE_DEFAULT_NAME, STATE_IDLE, STATE_OFF, STATE_PAUSED, STATE_PLAYING,
STATE_UNKNOWN)
from homeassistant.loader import get_component
from homeassistant.helpers.event import (track_utc_time_change)
REQUIREMENTS = ['plexapi==1.1.0']
REQUIREMENTS = ['plexapi==2.0.2']
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(seconds=1)
@ -30,7 +31,8 @@ PLEX_CONFIG_FILE = 'plex.conf'
_CONFIGURING = {}
_LOGGER = logging.getLogger(__name__)
SUPPORT_PLEX = SUPPORT_PAUSE | SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK
SUPPORT_PLEX = SUPPORT_PAUSE | SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK | \
SUPPORT_STOP | SUPPORT_VOLUME_SET
def config_from_file(filename, config=None):
@ -193,6 +195,9 @@ class PlexClient(MediaPlayerDevice):
# pylint: disable=too-many-public-methods, attribute-defined-outside-init
def __init__(self, device, plex_sessions, update_devices, update_sessions):
"""Initialize the Plex device."""
from plexapi.utils import NA
self.na_type = NA
self.plex_sessions = plex_sessions
self.update_devices = update_devices
self.update_sessions = update_sessions
@ -211,20 +216,17 @@ class PlexClient(MediaPlayerDevice):
@property
def name(self):
"""Return the name of the device."""
return self.device.name or DEVICE_DEFAULT_NAME
return self.device.title or DEVICE_DEFAULT_NAME
@property
def session(self):
"""Return the session, if any."""
if self.device.machineIdentifier not in self.plex_sessions:
return None
return self.plex_sessions[self.device.machineIdentifier]
return self.plex_sessions.get(self.device.machineIdentifier, None)
@property
def state(self):
"""Return the state of the device."""
if self.session:
if self.session and self.session.player:
state = self.session.player.state
if state == 'playing':
return STATE_PLAYING
@ -243,11 +245,30 @@ class PlexClient(MediaPlayerDevice):
self.update_devices(no_throttle=True)
self.update_sessions(no_throttle=True)
# pylint: disable=no-self-use, singleton-comparison
def _convert_na_to_none(self, value):
"""Convert PlexAPI _NA() instances to None."""
# PlexAPI will return a "__NA__" object which can be compared to
# None, but isn't actually None - this converts it to a real None
# type so that lower layers don't think it's a URL and choke on it
if value is self.na_type:
return None
else:
return value
@property
def _active_media_plexapi_type(self):
"""Get the active media type required by PlexAPI commands."""
if self.media_content_type is MEDIA_TYPE_MUSIC:
return 'music'
else:
return 'video'
@property
def media_content_id(self):
"""Content ID of current playing media."""
if self.session is not None:
return self.session.ratingKey
return self._convert_na_to_none(self.session.ratingKey)
@property
def media_content_type(self):
@ -259,65 +280,82 @@ class PlexClient(MediaPlayerDevice):
return MEDIA_TYPE_TVSHOW
elif media_type == 'movie':
return MEDIA_TYPE_VIDEO
elif media_type == 'track':
return MEDIA_TYPE_MUSIC
return None
@property
def media_duration(self):
"""Duration of current playing media in seconds."""
if self.session is not None:
return self.session.duration
return self._convert_na_to_none(self.session.duration)
@property
def media_image_url(self):
"""Image url of current playing media."""
if self.session is not None:
return self.session.thumbUrl
thumb_url = self._convert_na_to_none(self.session.thumbUrl)
if str(self.na_type) in thumb_url:
# Audio tracks build their thumb urls internally before passing
# back a URL with the PlexAPI _NA type already converted to a
# string and embedded into a malformed URL
thumb_url = None
return thumb_url
@property
def media_title(self):
"""Title of current playing media."""
# find a string we can use as a title
if self.session is not None:
return self.session.title
return self._convert_na_to_none(self.session.title)
@property
def media_season(self):
"""Season of curent playing media (TV Show only)."""
from plexapi.video import Show
if isinstance(self.session, Show):
return self.session.seasons()[0].index
return self._convert_na_to_none(self.session.seasons()[0].index)
@property
def media_series_title(self):
"""The title of the series of current playing media (TV Show only)."""
from plexapi.video import Show
if isinstance(self.session, Show):
return self.session.grandparentTitle
return self._convert_na_to_none(self.session.grandparentTitle)
@property
def media_episode(self):
"""Episode of current playing media (TV Show only)."""
from plexapi.video import Show
if isinstance(self.session, Show):
return self.session.index
return self._convert_na_to_none(self.session.index)
@property
def supported_media_commands(self):
"""Flag of media commands that are supported."""
return SUPPORT_PLEX
def set_volume_level(self, volume):
"""Set volume level, range 0..1."""
self.device.setVolume(int(volume * 100),
self._active_media_plexapi_type)
def media_play(self):
"""Send play command."""
self.device.play()
self.device.play(self._active_media_plexapi_type)
def media_pause(self):
"""Send pause command."""
self.device.pause()
self.device.pause(self._active_media_plexapi_type)
def media_stop(self):
"""Send stop command."""
self.device.stop(self._active_media_plexapi_type)
def media_next_track(self):
"""Send next track command."""
self.device.skipNext()
self.device.skipNext(self._active_media_plexapi_type)
def media_previous_track(self):
"""Send previous track command."""
self.device.skipPrevious()
self.device.skipPrevious(self._active_media_plexapi_type)

View file

@ -14,7 +14,7 @@ from homeassistant.helpers.entity import Entity
from homeassistant.util import Throttle
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['plexapi==1.1.0']
REQUIREMENTS = ['plexapi==2.0.2']
CONF_SERVER = 'server'
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=1)
@ -54,15 +54,18 @@ class PlexSensor(Entity):
# pylint: disable=too-many-arguments
def __init__(self, name, plex_url, plex_user, plex_password, plex_server):
"""Initialize the sensor."""
from plexapi.utils import NA
self._na_type = NA
self._name = name
self._state = 0
self._now_playing = []
if plex_user and plex_password:
from plexapi.myplex import MyPlexUser
user = MyPlexUser.signin(plex_user, plex_password)
from plexapi.myplex import MyPlexAccount
user = MyPlexAccount.signin(plex_user, plex_password)
server = plex_server if plex_server else user.resources()[0].name
self._server = user.getResource(server).connect()
self._server = user.resource(server).connect()
else:
from plexapi.server import PlexServer
self._server = PlexServer(plex_url)
@ -93,7 +96,11 @@ class PlexSensor(Entity):
def update(self):
"""Update method for plex sensor."""
sessions = self._server.sessions()
now_playing = [(s.user.title, "{0} ({1})".format(s.title, s.year))
for s in sessions]
now_playing = []
for sess in sessions:
user = sess.user.title if sess.user is not self._na_type else ""
title = sess.title if sess.title is not self._na_type else ""
year = sess.year if sess.year is not self._na_type else ""
now_playing.append((user, "{0} ({1})".format(title, year)))
self._state = len(sessions)
self._now_playing = now_playing

View file

@ -228,7 +228,7 @@ phue==0.8
# homeassistant.components.media_player.plex
# homeassistant.components.sensor.plex
plexapi==1.1.0
plexapi==2.0.2
# homeassistant.components.sensor.serial_pm
pmsensor==0.2