Skip to content

Commit

Permalink
Merge 368482f into 0e4d53a
Browse files Browse the repository at this point in the history
  • Loading branch information
Hellowlol committed Oct 26, 2017
2 parents 0e4d53a + 368482f commit 826841e
Show file tree
Hide file tree
Showing 17 changed files with 166 additions and 57 deletions.
7 changes: 6 additions & 1 deletion .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ exclude_lines =
pragma: no cover
raise NotImplementedError
raise Unsupported
raise Exception
except ImportError
except BadRequest
def __repr__
def __bool__
if __name__ == .__main__.:
def __iter__
def __hash__
def __len__
if __name__ == .__main__.:
8 changes: 4 additions & 4 deletions plexapi/alert.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def run(self):
url = self._server.url(self.key).replace('http', 'ws')
log.info('Starting AlertListener: %s', url)
self._ws = websocket.WebSocketApp(url, on_message=self._onMessage,
on_error=self._onError)
on_error=self._onError)
self._ws.run_forever()

def stop(self):
Expand All @@ -47,12 +47,12 @@ def _onMessage(self, ws, message):
""" Called when websocket message is recieved. """
try:
data = json.loads(message)['NotificationContainer']
log.debug('Alert: %s', data)
log.debug('Alert: %s %s %s', *data)
if self._callback:
self._callback(data)
except Exception as err:
except Exception as err: # pragma: no cover
log.error('AlertListener Msg Error: %s', err)

def _onError(self, ws, err):
def _onError(self, ws, err): # pragma: no cover
""" Called when websocket error is recieved. """
log.error('AlertListener Error: %s' % err)
2 changes: 1 addition & 1 deletion plexapi/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ def playMedia(self, media, offset=0, **params):
if not self.product != 'OpenPHT':
try:
self.sendCommand('timeline/subscribe', port=server_url[1].strip('/'), protocol='http')
except:
except: # noqa: E722
# some clients dont need or like this and raises http 400.
# We want to include the exception in the log,
# but it might still work so we swallow it.
Expand Down
2 changes: 1 addition & 1 deletion plexapi/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def get(self, key, default=None, cast=None):
section, name = key.lower().split('.')
value = self.data.get(section, {}).get(name, default)
return cast(value) if cast else value
except:
except: # noqa: E722
return default

def _asDict(self):
Expand Down
2 changes: 1 addition & 1 deletion plexapi/media.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ def _loadData(self, data):
self.type = cast(int, data.attrib.get('streamType'))

@staticmethod
def parse(server, data, initpath):
def parse(server, data, initpath): # pragma: no cover seems to be dead code.
""" Factory method returns a new MediaPartStream from xml data. """
STREAMCLS = {1: VideoStream, 2: AudioStream, 3: SubtitleStream}
stype = cast(int, data.attrib.get('streamType'))
Expand Down
4 changes: 2 additions & 2 deletions plexapi/myplex.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,10 @@ def query(self, url, method=None, headers=None, timeout=None, **kwargs):
log.debug('%s %s %s', method.__name__.upper(), url, kwargs.get('json', ''))
headers = self._headers(**headers or {})
response = method(url, headers=headers, timeout=timeout, **kwargs)
if response.status_code not in (200, 201, 204):
if response.status_code not in (200, 201, 204): # pragma: no cover
codename = codes.get(response.status_code)[0]
errtext = response.text.replace('\n', ' ')
log.warn('BadRequest (%s) %s %s; %s' % (response.status_code, codename, response.url, errtext))
log.warning('BadRequest (%s) %s %s; %s' % (response.status_code, codename, response.url, errtext))
raise BadRequest('(%s) %s %s; %s' % (response.status_code, codename, response.url, errtext))
data = response.text.encode('utf8')
return ElementTree.fromstring(data) if data.strip() else None
Expand Down
11 changes: 2 additions & 9 deletions plexapi/photo.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def albums(self, **kwargs):
def album(self, title):
""" Returns the :class:`~plexapi.photo.Photoalbum` that matches the specified title. """
for album in self.albums():
if album.attrib.get('title').lower() == title.lower():
if album.title.lower() == title.lower():
return album
raise NotFound('Unable to find album: %s' % title)

Expand All @@ -66,17 +66,10 @@ def photos(self, **kwargs):
def photo(self, title):
""" Returns the :class:`~plexapi.photo.Photo` that matches the specified title. """
for photo in self.photos():
if photo.attrib.get('title').lower() == title.lower():
if photo.title.lower() == title.lower():
return photo
raise NotFound('Unable to find photo: %s' % title)

def reload(self):
""" Reload the data for this object from self.key. """
self._initpath = self.key
data = self._server.query(self.key)
self._loadData(data)
return self


@utils.registerPlexObject
class Photo(PlexPartialObject):
Expand Down
10 changes: 5 additions & 5 deletions plexapi/playlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@ def _loadData(self, data):
self.updatedAt = toDatetime(data.attrib.get('updatedAt'))
self._items = None # cache for self.items

def __len__(self):
def __len__(self): # pragma: no cover
return len(self.items())

def __contains__(self, other):
def __contains__(self, other): # pragma: no cover
return any(i.key == other.key for i in self.items())

def __getitem__(self, key):
def __getitem__(self, key): # pragma: no cover
return self.items()[key]

def items(self):
Expand All @@ -57,7 +57,7 @@ def addItems(self, items):
items = [items]
ratingKeys = []
for item in items:
if item.listType != self.playlistType:
if item.listType != self.playlistType: # pragma: no cover
raise BadRequest('Can not mix media types when building a playlist: %s and %s' %
(self.playlistType, item.listType))
ratingKeys.append(str(item.ratingKey))
Expand Down Expand Up @@ -108,7 +108,7 @@ def create(cls, server, title, items):
items = [items]
ratingKeys = []
for item in items:
if item.listType != items[0].listType:
if item.listType != items[0].listType: # pragma: no cover
raise BadRequest('Can not mix media types when building a playlist')
ratingKeys.append(str(item.ratingKey))
ratingKeys = ','.join(ratingKeys)
Expand Down
23 changes: 8 additions & 15 deletions plexapi/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,6 @@ def cast(func, value):
return value


def getattributeOrNone(obj, self, attr):
""" Returns result from __getattribute__ or None if not found. """
try:
return super(obj, self).__getattribute__(attr)
except AttributeError:
return None


def joinArgs(args):
""" Returns a query string (uses for HTTP URLs) where only the value is URL encoded.
Example return value: '?genre=action&type=1337'.
Expand Down Expand Up @@ -129,7 +121,7 @@ def rget(obj, attrstr, default=None, delim='.'): # pragma: no cover
if attrstr:
return rget(value, attrstr, default, delim)
return value
except:
except: # noqa: E722
return default


Expand Down Expand Up @@ -198,7 +190,8 @@ def toList(value, itemcast=None, delim=','):
return [itemcast(item) for item in value.split(delim) if item != '']


def downloadSessionImages(server, filename=None, height=150, width=150, opacity=100, saturation=100):
def downloadSessionImages(server, filename=None, height=150, width=150,
opacity=100, saturation=100): # pragma: no cover
""" Helper to download a bif image or thumb.url from plex.server.sessions.
Parameters:
Expand Down Expand Up @@ -243,7 +236,7 @@ def download(url, filename=None, savepath=None, session=None, chunksize=4024,
mocked (bool): Helper to do evertything except write the file.
unpack (bool): Unpack the zip file.
showstatus(bool): Display a progressbar.
Example:
>>> download(a_episode.getStreamURL(), a_episode.location)
/path/to/file
Expand Down Expand Up @@ -278,7 +271,7 @@ def download(url, filename=None, savepath=None, session=None, chunksize=4024,

# save the file to disk
log.info('Downloading: %s', fullpath)
if showstatus:
if showstatus: # pragma: no cover
total = int(response.headers.get('content-length', 0))
bar = tqdm(unit='B', unit_scale=True, total=total, desc=filename)

Expand All @@ -288,7 +281,7 @@ def download(url, filename=None, savepath=None, session=None, chunksize=4024,
if showstatus:
bar.update(len(chunk))

if showstatus:
if showstatus: # pragma: no cover
bar.close()
# check we want to unzip the contents
if fullpath.endswith('zip') and unpack:
Expand All @@ -314,7 +307,7 @@ def tag_helper(tag, items, locked=True, remove=False):
return data


def getMyPlexAccount(opts=None):
def getMyPlexAccount(opts=None): # pragma: no cover
""" Helper function tries to get a MyPlex Account instance by checking
the the following locations for a username and password. This is
useful to create user-friendly command line tools.
Expand All @@ -341,7 +334,7 @@ def getMyPlexAccount(opts=None):
return MyPlexAccount(username, password)


def choose(msg, items, attr):
def choose(msg, items, attr): # pragma: no cover
""" Command line helper to display a list of choices, asking the
user to choose one of the options.
"""
Expand Down
19 changes: 18 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,27 @@ def monkeydownload(request, monkeypatch):
monkeypatch.undo()


def callable_http_patch(mocker):
# mocker is a fixture
# but this is a normal func so we can do http calls inside the tests
return mocker.patch('plexapi.server.requests.sessions.Session.send',
return_value=mocker.MagicMock(status_code=200,
text='<xml><child></child></xml>'))

@pytest.fixture()
def empty_response(mocker):
response = mocker.MagicMock(status_code=200, text='<xml><child></child></xml>')
return response


@pytest.fixture()
def patched_http_call(mocker):
return callable_http_patch(mocker)


# ---------------------------------
# Utility Functions
# ---------------------------------

def is_datetime(value):
return value > MIN_DATETIME

Expand Down
5 changes: 5 additions & 0 deletions tests/test_library.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ def test_library_MovieSection_update(movies):
movies.update()


def test_library_MovieSection_search_genre(movie, movies):
# assert len(movie.genres[0].items()) # TODO
assert len(movies.search(genre=movie.genres[0])) > 1


def test_library_MovieSection_cancelUpdate(movies):
movies.cancelUpdate()

Expand Down
68 changes: 67 additions & 1 deletion tests/test_myplex.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# -*- coding: utf-8 -*-
import pytest
from plexapi.exceptions import BadRequest
from . import conftest as utils


def test_myplex_accounts(account, plex):
Expand All @@ -16,7 +19,7 @@ def test_myplex_accounts(account, plex):
account = plex.account()
print('Local PlexServer.account():')
print('username: %s' % account.username)
print('authToken: %s' % account.authToken)
#print('authToken: %s' % account.authToken)
print('signInState: %s' % account.signInState)
assert account.username, 'Account has no username'
assert account.authToken, 'Account has no authToken'
Expand Down Expand Up @@ -51,6 +54,10 @@ def test_myplex_devices(account):
assert devices, 'No devices found for account: %s' % account.name


def test_myplex_device(account):
assert account.device('pkkid-plexapi')


def _test_myplex_connect_to_device(account):
devices = account.devices()
for device in devices:
Expand All @@ -67,3 +74,62 @@ def test_myplex_users(account):
user = account.user(users[0].title)
print('Found user: %s' % user)
assert user, 'Could not find user %s' % users[0].title

assert len(users[0].servers[0].sections()) == 7, "Could'nt info about the shared libraries"


def test_myplex_resource(account):
assert account.resource('pkkid-plexapi')


def test_myplex_webhooks(account):
with pytest.raises(BadRequest):
account.webhooks()


def test_myplex_addwebhooks(account):
with pytest.raises(BadRequest):
account.addWebhook('http://site.com')


def test_myplex_deletewebhooks(account):
with pytest.raises(BadRequest):
account.deleteWebhook('http://site.com')


def test_myplex_optout(account):
def enabled():
ele = account.query('https://plex.tv/api/v2/user/privacy')
lib = ele.attrib.get('optOutLibraryStats')
play = ele.attrib.get('optOutPlayback')
return bool(int(lib)), bool(int(play))

# This should be False False
library_enabled, playback_enabled = enabled()

account.optOut(library=True, playback=True)

assert all(enabled())

account.optOut(library=False, playback=False)

assert not all(enabled())


def test_myplex_inviteFriend_remove(account, plex, mocker):
inv_user = 'hellowlol'
vid_filter = {'contentRating': ['G'], 'label': ['foo']}
secs = plex.library.sections()

ids = account._getSectionIds(plex.machineIdentifier, secs)
with mocker.patch.object(account, '_getSectionIds', return_value=ids):
with utils.callable_http_patch(mocker):

account.inviteFriend(inv_user, plex, secs, allowSync=True, allowCameraUpload=True,
allowChannels=False, filterMovies=vid_filter, filterTelevision=vid_filter,
filterMusic={'label': ['foo']})

assert inv_user not in [u.title for u in account.users()]

with utils.callable_http_patch(mock):
account.removeFriend(inv_user)
9 changes: 9 additions & 0 deletions tests/test_photo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@


def test_photo_Photoalbum(photoalbum):
assert len(photoalbum.albums()) == 3
assert len(photoalbum.photos()) == 3
cats_in_bed = photoalbum.album('Cats in bed')
assert len(cats_in_bed.photos()) == 7
a_pic = cats_in_bed.photo('maxresdefault')
assert a_pic
10 changes: 4 additions & 6 deletions tests/test_playlist.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
import pytest, time
import time
import pytest


def test_create_playlist(plex, show):
Expand Down Expand Up @@ -99,14 +100,11 @@ def test_playqueues(plex):


def test_copyToUser(plex, show, fresh_plex):
# Skip out if we do not have plexpass
if not plex.myPlexSubscription:
pytest.skip('PlexPass subscription required for test.')
episodes = show.episodes()
playlist = plex.createPlaylist('shared_from_test_plexapi', episodes)
try:
playlist.copyToUser('plexapi2')
user = plex.myPlexAccount().user('plexapi2')
playlist.copyToUser('PKKid')
user = plex.myPlexAccount().user('PKKid')
user_plex = fresh_plex(plex._baseurl, user.get_token(plex.machineIdentifier))
assert playlist.title in [p.title for p in user_plex.playlists()]
finally:
Expand Down

0 comments on commit 826841e

Please sign in to comment.