mirror of
https://github.com/pkkid/python-plexapi
synced 2024-11-10 14:14:19 +00:00
Started movinf client tests into Pytest framework
This commit is contained in:
parent
ef8eb94bc5
commit
650a88d638
7 changed files with 75 additions and 27 deletions
|
@ -70,6 +70,10 @@ class PlexClient(PlexObject):
|
||||||
if connect and self._baseurl:
|
if connect and self._baseurl:
|
||||||
self.connect(timeout=timeout)
|
self.connect(timeout=timeout)
|
||||||
|
|
||||||
|
def _nextCommandId(self):
|
||||||
|
self._commandId += 0
|
||||||
|
return self._commandId
|
||||||
|
|
||||||
def connect(self, timeout=None):
|
def connect(self, timeout=None):
|
||||||
""" Alias of reload as any subsequent requests to this client will be
|
""" Alias of reload as any subsequent requests to this client will be
|
||||||
made directly to the device even if the object attributes were initially
|
made directly to the device even if the object attributes were initially
|
||||||
|
@ -113,7 +117,7 @@ class PlexClient(PlexObject):
|
||||||
headers.update(kwargs)
|
headers.update(kwargs)
|
||||||
return headers
|
return headers
|
||||||
|
|
||||||
def proxyThroughServer(self, value=True):
|
def proxyThroughServer(self, value=True, server=None):
|
||||||
""" Tells this PlexClient instance to proxy all future commands through the PlexServer.
|
""" Tells this PlexClient instance to proxy all future commands through the PlexServer.
|
||||||
Useful if you do not wish to connect directly to the Client device itself.
|
Useful if you do not wish to connect directly to the Client device itself.
|
||||||
|
|
||||||
|
@ -123,6 +127,8 @@ class PlexClient(PlexObject):
|
||||||
Raises:
|
Raises:
|
||||||
:class:`~plexapi.exceptions.Unsupported`: Cannot use client proxy with unknown server.
|
:class:`~plexapi.exceptions.Unsupported`: Cannot use client proxy with unknown server.
|
||||||
"""
|
"""
|
||||||
|
if server:
|
||||||
|
self._server = server
|
||||||
if value is True and not self._server:
|
if value is True and not self._server:
|
||||||
raise Unsupported('Cannot use client proxy with unknown server.')
|
raise Unsupported('Cannot use client proxy with unknown server.')
|
||||||
self._proxyThroughServer = value
|
self._proxyThroughServer = value
|
||||||
|
@ -165,8 +171,7 @@ class PlexClient(PlexObject):
|
||||||
raise Unsupported('Client %s doesnt support %s controller.' % (self.title, controller))
|
raise Unsupported('Client %s doesnt support %s controller.' % (self.title, controller))
|
||||||
key = '/player/%s%s' % (command, utils.joinArgs(params))
|
key = '/player/%s%s' % (command, utils.joinArgs(params))
|
||||||
headers = {'X-Plex-Target-Client-Identifier': self.machineIdentifier}
|
headers = {'X-Plex-Target-Client-Identifier': self.machineIdentifier}
|
||||||
self._commandId += 1
|
params['commandID'] = self._nextCommandId()
|
||||||
params['commandID'] = self._commandId
|
|
||||||
proxy = self._proxyThroughServer if proxy is None else proxy
|
proxy = self._proxyThroughServer if proxy is None else proxy
|
||||||
if proxy:
|
if proxy:
|
||||||
return self._server.query(key, headers=headers)
|
return self._server.query(key, headers=headers)
|
||||||
|
@ -428,6 +433,8 @@ class PlexClient(PlexObject):
|
||||||
'port': server_url[-1],
|
'port': server_url[-1],
|
||||||
'offset': offset,
|
'offset': offset,
|
||||||
'key': media.key,
|
'key': media.key,
|
||||||
|
'token': self._server._token,
|
||||||
|
'commandID': self._nextCommandId(),
|
||||||
'containerKey': '/playQueues/%s?window=100&own=1' % playqueue.playQueueID,
|
'containerKey': '/playQueues/%s?window=100&own=1' % playqueue.playQueueID,
|
||||||
}, **params))
|
}, **params))
|
||||||
|
|
||||||
|
|
|
@ -93,20 +93,17 @@ class MediaPart(PlexObject):
|
||||||
streams.append(cls(self._server, elem, self._initpath))
|
streams.append(cls(self._server, elem, self._initpath))
|
||||||
return streams
|
return streams
|
||||||
|
|
||||||
@property
|
|
||||||
def videoStreams(self):
|
def videoStreams(self):
|
||||||
""" Returns a list of :class:`~plexapi.media.VideoStream` objects in this MediaPart. """
|
""" Returns a list of :class:`~plexapi.media.VideoStream` objects in this MediaPart. """
|
||||||
return [s for s in self.streams if s.streamType == VideoStream.STREAMTYPE]
|
return [stream for stream in self.streams if stream.streamType == VideoStream.STREAMTYPE]
|
||||||
|
|
||||||
@property
|
|
||||||
def audioStreams(self):
|
def audioStreams(self):
|
||||||
""" Returns a list of :class:`~plexapi.media.AudioStream` objects in this MediaPart. """
|
""" Returns a list of :class:`~plexapi.media.AudioStream` objects in this MediaPart. """
|
||||||
return [s for s in self.streams if s.streamType == AudioStream.STREAMTYPE]
|
return [stream for stream in self.streams if stream.streamType == AudioStream.STREAMTYPE]
|
||||||
|
|
||||||
@property
|
|
||||||
def subtitleStreams(self):
|
def subtitleStreams(self):
|
||||||
""" Returns a list of :class:`~plexapi.media.SubtitleStream` objects in this MediaPart. """
|
""" Returns a list of :class:`~plexapi.media.SubtitleStream` objects in this MediaPart. """
|
||||||
return [s for s in self.streams if s.streamType == SubtitleStream.STREAMTYPE]
|
return [stream for stream in self.streams if stream.streamType == SubtitleStream.STREAMTYPE]
|
||||||
|
|
||||||
|
|
||||||
class MediaPartStream(PlexObject):
|
class MediaPartStream(PlexObject):
|
||||||
|
|
|
@ -144,7 +144,15 @@ class Movie(Video, Playable):
|
||||||
""" This does not exist in plex xml response but is added to have a common
|
""" This does not exist in plex xml response but is added to have a common
|
||||||
interface to get the location of the Movie/Show/Episode
|
interface to get the location of the Movie/Show/Episode
|
||||||
"""
|
"""
|
||||||
return [p.file for p in self.iterParts() if p]
|
return [part.file for part in self.iterParts() if part]
|
||||||
|
|
||||||
|
def subtitleStreams(self):
|
||||||
|
""" Returns a list of :class:`~plexapi.media.SubtitleStream` objects for all MediaParts. """
|
||||||
|
streams = []
|
||||||
|
for elem in self.media:
|
||||||
|
for part in elem.parts:
|
||||||
|
streams += part.subtitleStreams()
|
||||||
|
return streams
|
||||||
|
|
||||||
def _prettyfilename(self):
|
def _prettyfilename(self):
|
||||||
# This is just for compat.
|
# This is just for compat.
|
||||||
|
|
|
@ -30,7 +30,7 @@ MYPLEX_PASSWORD = plexapi.CONFIG.get('auth.myplex_password')
|
||||||
CLIENT_BASEURL = plexapi.CONFIG.get('auth.client_baseurl')
|
CLIENT_BASEURL = plexapi.CONFIG.get('auth.client_baseurl')
|
||||||
CLIENT_TOKEN = plexapi.CONFIG.get('auth.client_token')
|
CLIENT_TOKEN = plexapi.CONFIG.get('auth.client_token')
|
||||||
|
|
||||||
MIN_DATETIME = datetime(2008, 1, 1)
|
MIN_DATETIME = datetime(1999, 1, 1)
|
||||||
REGEX_EMAIL = r'(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)'
|
REGEX_EMAIL = r'(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)'
|
||||||
REGEX_IPADDR = r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$'
|
REGEX_IPADDR = r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$'
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ def plex2():
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def client(request):
|
def client(request):
|
||||||
return PlexClient(baseurl=CLIENT_BASEURL, token=CLIENT_TOKEN)
|
return PlexClient(plex(), baseurl=CLIENT_BASEURL, token=CLIENT_TOKEN)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
|
|
|
@ -2,6 +2,18 @@
|
||||||
import pytest, time
|
import pytest, time
|
||||||
|
|
||||||
|
|
||||||
|
def _check_capabilities(client, capabilities):
|
||||||
|
supported = client.protocolCapabilities
|
||||||
|
for capability in capabilities:
|
||||||
|
if capability not in supported:
|
||||||
|
pytest.skip('Client doesnt support %s capability.', capability)
|
||||||
|
|
||||||
|
|
||||||
|
def _check_proxy(plex, client, proxy):
|
||||||
|
if proxy:
|
||||||
|
client.proxyThroughServer(server=plex)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.client
|
@pytest.mark.client
|
||||||
def test_list_clients(account, plex):
|
def test_list_clients(account, plex):
|
||||||
assert account.resources(), 'MyPlex is not listing any devlices.'
|
assert account.resources(), 'MyPlex is not listing any devlices.'
|
||||||
|
@ -10,17 +22,10 @@ def test_list_clients(account, plex):
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.client
|
@pytest.mark.client
|
||||||
def test_client_navigation_direct(plex, client, episode, artist):
|
@pytest.mark.parametrize('proxy', [False, True])
|
||||||
_navigate(plex, client, episode, artist)
|
def test_client_navigation(plex, client, episode, artist, proxy):
|
||||||
|
_check_capabilities(client, ['navigation'])
|
||||||
|
_check_proxy(plex, client, proxy)
|
||||||
@pytest.mark.client
|
|
||||||
def test_client_navigation_via_proxy(plex, client, episode, artist):
|
|
||||||
client.proxyThroughServer()
|
|
||||||
_navigate(plex, client, episode, artist)
|
|
||||||
|
|
||||||
|
|
||||||
def _navigate(plex, client, episode, artist):
|
|
||||||
# Browse around a bit..
|
# Browse around a bit..
|
||||||
client.moveDown(); time.sleep(0.5)
|
client.moveDown(); time.sleep(0.5)
|
||||||
client.moveDown(); time.sleep(0.5)
|
client.moveDown(); time.sleep(0.5)
|
||||||
|
@ -44,6 +49,36 @@ def _navigate(plex, client, episode, artist):
|
||||||
client.goBack(); time.sleep(5)
|
client.goBack(); time.sleep(5)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.client
|
||||||
|
@pytest.mark.parametrize('proxy', [False, True])
|
||||||
|
def test_video_playback(plex, client, movie, proxy):
|
||||||
|
_check_capabilities(client, ['playback'])
|
||||||
|
_check_proxy(plex, client, proxy)
|
||||||
|
try:
|
||||||
|
# Need a movie with subtitles
|
||||||
|
movie = plex.library.section('Movies').get('Moana').reload()
|
||||||
|
mtype = 'video'
|
||||||
|
subs = [stream for stream in movie.subtitleStreams() if stream.language == 'English']
|
||||||
|
# Basic play ability
|
||||||
|
print('Basic play ability')
|
||||||
|
client.playMedia(movie); time.sleep(5)
|
||||||
|
client.pause(mtype); time.sleep(2)
|
||||||
|
# Step forward, back, seek in time
|
||||||
|
print('Step forward, back, seek in time')
|
||||||
|
client.stepForward(mtype); time.sleep(5)
|
||||||
|
client.play(mtype); time.sleep(3)
|
||||||
|
client.stepBack(mtype); time.sleep(5)
|
||||||
|
client.play(mtype); time.sleep(3)
|
||||||
|
client.seekTo(10*60*1000); time.sleep(5)
|
||||||
|
# Enable subtitles
|
||||||
|
print('Enable subtitles')
|
||||||
|
client.setSubtitleStream(0, mtype); time.sleep(10)
|
||||||
|
client.setSubtitleStream(subs[0].id, mtype); time.sleep(10)
|
||||||
|
client.stop(mtype); time.sleep(1)
|
||||||
|
finally:
|
||||||
|
movie.markWatched()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.client
|
@pytest.mark.client
|
||||||
def _test_client_PlexClient__loadData(pms):
|
def _test_client_PlexClient__loadData(pms):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -20,7 +20,7 @@ def test_server_attr(plex):
|
||||||
assert plex.platform in ('Linux', 'Windows')
|
assert plex.platform in ('Linux', 'Windows')
|
||||||
assert len(plex.platformVersion) >= 5
|
assert len(plex.platformVersion) >= 5
|
||||||
assert plex._token == utils.SERVER_TOKEN
|
assert plex._token == utils.SERVER_TOKEN
|
||||||
assert plex.transcoderActiveVideoSessions == 0
|
assert utils.is_int(plex.transcoderActiveVideoSessions, gte=0)
|
||||||
assert utils.is_datetime(plex.updatedAt)
|
assert utils.is_datetime(plex.updatedAt)
|
||||||
assert len(plex.version) >= 5
|
assert len(plex.version) >= 5
|
||||||
|
|
||||||
|
@ -168,7 +168,7 @@ def test_server_client_not_found(plex):
|
||||||
|
|
||||||
|
|
||||||
def test_server_Server_sessions(plex):
|
def test_server_Server_sessions(plex):
|
||||||
assert len(plex.sessions()) == 0
|
assert len(plex.sessions()) >= 0
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.client
|
@pytest.mark.client
|
||||||
|
|
|
@ -81,7 +81,8 @@ def test_video_Movie_attrs(movies):
|
||||||
assert movie.originallyAvailableAt.strftime('%Y-%m-%d') in ('2008-01-11', '2008-02-11')
|
assert movie.originallyAvailableAt.strftime('%Y-%m-%d') in ('2008-01-11', '2008-02-11')
|
||||||
assert movie.player is None
|
assert movie.player is None
|
||||||
assert movie.playlistItemID is None
|
assert movie.playlistItemID is None
|
||||||
assert movie.primaryExtraKey is None
|
if movie.primaryExtraKey:
|
||||||
|
assert utils.is_metadata(movie.primaryExtraKey)
|
||||||
assert [i.tag for i in movie.producers] == []
|
assert [i.tag for i in movie.producers] == []
|
||||||
assert float(movie.rating) >= 6.4
|
assert float(movie.rating) >= 6.4
|
||||||
assert movie.ratingImage == 'rottentomatoes://image.rating.ripe'
|
assert movie.ratingImage == 'rottentomatoes://image.rating.ripe'
|
||||||
|
@ -460,7 +461,7 @@ def test_video_Episode_attrs(episode):
|
||||||
def test_video_Season(show):
|
def test_video_Season(show):
|
||||||
seasons = show.seasons()
|
seasons = show.seasons()
|
||||||
assert len(seasons) >= 1
|
assert len(seasons) >= 1
|
||||||
assert ['Season 1', 'Season 2'] == [s.title for s in seasons]
|
assert ['Season 1', 'Season 2'] == [s.title for s in seasons[:2]]
|
||||||
assert show.season('Season 1') == seasons[0]
|
assert show.season('Season 1') == seasons[0]
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue