Fix Plex media_player.play_media service (#27278)
* First attempt to fix play_media * More changes to media playback * Use playqueues, clean up play_media * Use similar function name, add docstring
This commit is contained in:
parent
073bdd672a
commit
0915d927df
2 changed files with 76 additions and 77 deletions
|
@ -2,10 +2,9 @@
|
|||
from datetime import timedelta
|
||||
import json
|
||||
import logging
|
||||
from xml.etree.ElementTree import ParseError
|
||||
|
||||
import plexapi.exceptions
|
||||
import plexapi.playlist
|
||||
import plexapi.playqueue
|
||||
import requests.exceptions
|
||||
|
||||
from homeassistant.components.media_player import MediaPlayerDevice
|
||||
|
@ -16,6 +15,7 @@ from homeassistant.components.media_player.const import (
|
|||
SUPPORT_NEXT_TRACK,
|
||||
SUPPORT_PAUSE,
|
||||
SUPPORT_PLAY,
|
||||
SUPPORT_PLAY_MEDIA,
|
||||
SUPPORT_PREVIOUS_TRACK,
|
||||
SUPPORT_STOP,
|
||||
SUPPORT_TURN_OFF,
|
||||
|
@ -543,9 +543,6 @@ class PlexClient(MediaPlayerDevice):
|
|||
@property
|
||||
def supported_features(self):
|
||||
"""Flag media player features that are supported."""
|
||||
if not self._is_player_active:
|
||||
return 0
|
||||
|
||||
# force show all controls
|
||||
if self.plex_server.show_all_controls:
|
||||
return (
|
||||
|
@ -555,13 +552,11 @@ class PlexClient(MediaPlayerDevice):
|
|||
| SUPPORT_STOP
|
||||
| SUPPORT_VOLUME_SET
|
||||
| SUPPORT_PLAY
|
||||
| SUPPORT_PLAY_MEDIA
|
||||
| SUPPORT_TURN_OFF
|
||||
| SUPPORT_VOLUME_MUTE
|
||||
)
|
||||
|
||||
# only show controls when we know what device is connecting
|
||||
if not self._make:
|
||||
return 0
|
||||
# no mute support
|
||||
if self.make.lower() == "shield android tv":
|
||||
_LOGGER.debug(
|
||||
|
@ -575,8 +570,10 @@ class PlexClient(MediaPlayerDevice):
|
|||
| SUPPORT_STOP
|
||||
| SUPPORT_VOLUME_SET
|
||||
| SUPPORT_PLAY
|
||||
| SUPPORT_PLAY_MEDIA
|
||||
| SUPPORT_TURN_OFF
|
||||
)
|
||||
|
||||
# Only supports play,pause,stop (and off which really is stop)
|
||||
if self.make.lower().startswith("tivo"):
|
||||
_LOGGER.debug(
|
||||
|
@ -585,8 +582,7 @@ class PlexClient(MediaPlayerDevice):
|
|||
self.entity_id,
|
||||
)
|
||||
return SUPPORT_PAUSE | SUPPORT_PLAY | SUPPORT_STOP | SUPPORT_TURN_OFF
|
||||
# Not all devices support playback functionality
|
||||
# Playback includes volume, stop/play/pause, etc.
|
||||
|
||||
if self.device and "playback" in self._device_protocol_capabilities:
|
||||
return (
|
||||
SUPPORT_PAUSE
|
||||
|
@ -595,6 +591,7 @@ class PlexClient(MediaPlayerDevice):
|
|||
| SUPPORT_STOP
|
||||
| SUPPORT_VOLUME_SET
|
||||
| SUPPORT_PLAY
|
||||
| SUPPORT_PLAY_MEDIA
|
||||
| SUPPORT_TURN_OFF
|
||||
| SUPPORT_VOLUME_MUTE
|
||||
)
|
||||
|
@ -682,49 +679,74 @@ class PlexClient(MediaPlayerDevice):
|
|||
return
|
||||
|
||||
src = json.loads(media_id)
|
||||
library = src.get("library_name")
|
||||
shuffle = src.get("shuffle", 0)
|
||||
|
||||
media = None
|
||||
|
||||
if media_type == "MUSIC":
|
||||
media = (
|
||||
self.device.server.library.section(src["library_name"])
|
||||
.get(src["artist_name"])
|
||||
.album(src["album_name"])
|
||||
.get(src["track_name"])
|
||||
)
|
||||
media = self._get_music_media(library, src)
|
||||
elif media_type == "EPISODE":
|
||||
media = self._get_tv_media(
|
||||
src["library_name"],
|
||||
src["show_name"],
|
||||
src["season_number"],
|
||||
src["episode_number"],
|
||||
)
|
||||
media = self._get_tv_media(library, src)
|
||||
elif media_type == "PLAYLIST":
|
||||
media = self.device.server.playlist(src["playlist_name"])
|
||||
media = self.plex_server.playlist(src["playlist_name"])
|
||||
elif media_type == "VIDEO":
|
||||
media = self.device.server.library.section(src["library_name"]).get(
|
||||
src["video_name"]
|
||||
)
|
||||
media = self.plex_server.library.section(library).get(src["video_name"])
|
||||
|
||||
if (
|
||||
media
|
||||
and media_type == "EPISODE"
|
||||
and isinstance(media, plexapi.playlist.Playlist)
|
||||
):
|
||||
# delete episode playlist after being loaded into a play queue
|
||||
self._client_play_media(media=media, delete=True, shuffle=src["shuffle"])
|
||||
elif media:
|
||||
self._client_play_media(media=media, shuffle=src["shuffle"])
|
||||
if media is None:
|
||||
_LOGGER.error("Media could not be found: %s", media_id)
|
||||
return
|
||||
|
||||
def _get_tv_media(self, library_name, show_name, season_number, episode_number):
|
||||
playqueue = self.plex_server.create_playqueue(media, shuffle=shuffle)
|
||||
try:
|
||||
self.device.playMedia(playqueue)
|
||||
except ParseError:
|
||||
# Temporary workaround for Plexamp / plexapi issue
|
||||
pass
|
||||
except requests.exceptions.ConnectTimeout:
|
||||
_LOGGER.error("Timed out playing on %s", self.name)
|
||||
|
||||
self.update_devices()
|
||||
|
||||
def _get_music_media(self, library_name, src):
|
||||
"""Find music media and return a Plex media object."""
|
||||
artist_name = src["artist_name"]
|
||||
album_name = src.get("album_name")
|
||||
track_name = src.get("track_name")
|
||||
track_number = src.get("track_number")
|
||||
|
||||
artist = self.plex_server.library.section(library_name).get(artist_name)
|
||||
|
||||
if album_name:
|
||||
album = artist.album(album_name)
|
||||
|
||||
if track_name:
|
||||
return album.track(track_name)
|
||||
|
||||
if track_number:
|
||||
for track in album.tracks():
|
||||
if int(track.index) == int(track_number):
|
||||
return track
|
||||
return None
|
||||
|
||||
return album
|
||||
|
||||
if track_name:
|
||||
return artist.searchTracks(track_name, maxresults=1)
|
||||
return artist
|
||||
|
||||
def _get_tv_media(self, library_name, src):
|
||||
"""Find TV media and return a Plex media object."""
|
||||
show_name = src["show_name"]
|
||||
season_number = src.get("season_number")
|
||||
episode_number = src.get("episode_number")
|
||||
target_season = None
|
||||
target_episode = None
|
||||
|
||||
show = self.device.server.library.section(library_name).get(show_name)
|
||||
show = self.plex_server.library.section(library_name).get(show_name)
|
||||
|
||||
if not season_number:
|
||||
playlist_name = f"{self.entity_id} - {show_name} Episodes"
|
||||
return self.device.server.createPlaylist(playlist_name, show.episodes())
|
||||
return show
|
||||
|
||||
for season in show.seasons():
|
||||
if int(season.seasonNumber) == int(season_number):
|
||||
|
@ -741,12 +763,7 @@ class PlexClient(MediaPlayerDevice):
|
|||
)
|
||||
else:
|
||||
if not episode_number:
|
||||
playlist_name = "{} - {} Season {} Episodes".format(
|
||||
self.entity_id, show_name, str(season_number)
|
||||
)
|
||||
return self.device.server.createPlaylist(
|
||||
playlist_name, target_season.episodes()
|
||||
)
|
||||
return target_season
|
||||
|
||||
for episode in target_season.episodes():
|
||||
if int(episode.index) == int(episode_number):
|
||||
|
@ -764,38 +781,6 @@ class PlexClient(MediaPlayerDevice):
|
|||
|
||||
return target_episode
|
||||
|
||||
def _client_play_media(self, media, delete=False, **params):
|
||||
"""Instruct Plex client to play a piece of media."""
|
||||
if not (self.device and "playback" in self._device_protocol_capabilities):
|
||||
_LOGGER.error("Client cannot play media: %s", self.entity_id)
|
||||
return
|
||||
|
||||
playqueue = plexapi.playqueue.PlayQueue.create(
|
||||
self.device.server, media, **params
|
||||
)
|
||||
|
||||
# Delete dynamic playlists used to build playqueue (ex. play tv season)
|
||||
if delete:
|
||||
media.delete()
|
||||
|
||||
server_url = self.device.server.baseurl.split(":")
|
||||
self.device.sendCommand(
|
||||
"playback/playMedia",
|
||||
**dict(
|
||||
{
|
||||
"machineIdentifier": self.device.server.machineIdentifier,
|
||||
"address": server_url[1].strip("/"),
|
||||
"port": server_url[-1],
|
||||
"key": media.key,
|
||||
"containerKey": "/playQueues/{}?window=100&own=1".format(
|
||||
playqueue.playQueueID
|
||||
),
|
||||
},
|
||||
**params,
|
||||
),
|
||||
)
|
||||
self.update_devices()
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Return the scene state attributes."""
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
"""Shared class to maintain Plex server instances."""
|
||||
import plexapi.myplex
|
||||
import plexapi.playqueue
|
||||
import plexapi.server
|
||||
from requests import Session
|
||||
|
||||
|
@ -109,3 +110,16 @@ class PlexServer:
|
|||
def show_all_controls(self):
|
||||
"""Return show_all_controls option."""
|
||||
return self.options[MP_DOMAIN][CONF_SHOW_ALL_CONTROLS]
|
||||
|
||||
@property
|
||||
def library(self):
|
||||
"""Return library attribute from server object."""
|
||||
return self._plex_server.library
|
||||
|
||||
def playlist(self, title):
|
||||
"""Return playlist from server object."""
|
||||
return self._plex_server.playlist(title)
|
||||
|
||||
def create_playqueue(self, media, **kwargs):
|
||||
"""Create playqueue on Plex server."""
|
||||
return plexapi.playqueue.PlayQueue.create(self._plex_server, media, **kwargs)
|
||||
|
|
Loading…
Add table
Reference in a new issue