From e3abfeeb0f3a89c1dde89fafd1e76bf857539476 Mon Sep 17 00:00:00 2001 From: Michael Shepanski Date: Wed, 8 Feb 2017 00:36:22 -0500 Subject: [PATCH] Audio tests passing --- plexapi/__init__.py | 2 +- plexapi/audio.py | 33 +++++++++++++-------------------- plexapi/base.py | 14 ++++++++------ plexapi/media.py | 2 +- plexapi/utils.py | 7 +++++++ plexapi/video.py | 26 +++++++++++++------------- tests/test_audio.py | 2 +- 7 files changed, 44 insertions(+), 42 deletions(-) diff --git a/plexapi/__init__.py b/plexapi/__init__.py index ed275233..b50c7418 100644 --- a/plexapi/__init__.py +++ b/plexapi/__init__.py @@ -32,7 +32,7 @@ BASE_HEADERS = reset_base_headers() log = logging.getLogger('plexapi') logfile = CONFIG.get('logging.path') logformat = CONFIG.get('logging.format', '%(asctime)s %(module)12s:%(lineno)-4s %(levelname)-9s %(message)s') -loglevel = CONFIG.get('logging.level', 'INFO') +loglevel = CONFIG.get('logging.level', 'INFO').upper() loghandler = logging.NullHandler() if logfile: logbackups = CONFIG.get('logging.backup_count', 3, int) diff --git a/plexapi/audio.py b/plexapi/audio.py index 58f8f19c..f12f46d0 100644 --- a/plexapi/audio.py +++ b/plexapi/audio.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- from plexapi import media, utils from plexapi.base import Playable, PlexPartialObject -from plexapi.exceptions import NotFound class Audio(PlexPartialObject): @@ -91,9 +90,9 @@ class Artist(Audio): self.guid = data.attrib.get('guid') self.key = self.key.replace('/children', '') # FIX_BUG_50 self.location = utils.findLocations(data, single=True) - self.countries = self._buildItems(data, media.Country) - self.genres = self._buildItems(data, media.Genre) - self.similar = self._buildItems(data, media.Similar) + self.countries = self._buildItems(data, media.Country, bytag=True) + self.genres = self._buildItems(data, media.Genre, bytag=True) + self.similar = self._buildItems(data, media.Similar, bytag=True) def album(self, title): """ Returns the :class:`~plexapi.audio.Album` that matches the specified title. @@ -101,15 +100,13 @@ class Artist(Audio): Parameters: title (str): Title of the album to return. """ - for album in self.albums(): - if album.title.lower() == title.lower(): - return album - raise NotFound('Unable to find album %s' % title) + key = '%s/children' % self.key + return self.fetchItem(key, title=title) def albums(self): """ Returns a list of :class:`~plexapi.audio.Album` objects by this artist. """ key = '%s/children' % self.key - return self.fetchItems(key, tag=Album.TYPE) + return self.fetchItems(key) def track(self, title): """ Returns the :class:`~plexapi.audio.Track` that matches the specified title. @@ -117,10 +114,8 @@ class Artist(Audio): Parameters: title (str): Title of the track to return. """ - for track in self.tracks(): - if track.title.lower() == title.lower(): - return track - raise NotFound('Unable to find track %s' % title) + key = '%s/allLeaves' % self.key + return self.fetchItem(key, title=title) def tracks(self): """ Returns a list of :class:`~plexapi.audio.Track` objects by this artist. """ @@ -186,7 +181,7 @@ class Album(Audio): self.parentTitle = data.attrib.get('parentTitle') self.studio = data.attrib.get('studio') self.year = utils.cast(int, data.attrib.get('year')) - self.genres = self._buildItems(data, media.Genre) + self.genres = self._buildItems(data, media.Genre, bytag=True) def track(self, title): """ Returns the :class:`~plexapi.audio.Track` that matches the specified title. @@ -194,10 +189,8 @@ class Album(Audio): Parameters: title (str): Title of the track to return. """ - for track in self.tracks(): - if track.title.lower() == title.lower(): - return track - raise NotFound('Unable to find track %s' % title) + key = '%s/children' % self.key + return self.fetchItem(key, title=title) def tracks(self): """ Returns a list of :class:`~plexapi.audio.Track` objects in this album. """ @@ -293,8 +286,8 @@ class Track(Audio, Playable): self.ratingCount = utils.cast(int, data.attrib.get('ratingCount')) self.viewOffset = utils.cast(int, data.attrib.get('viewOffset', 0)) self.year = utils.cast(int, data.attrib.get('year')) - self.media = self._buildItems(data, media.Media) - self.moods = self._buildItems(data, media.Mood) + self.media = self._buildItems(data, media.Media, bytag=True) + self.moods = self._buildItems(data, media.Mood, bytag=True) # data for active sessions and history self.sessionKey = utils.cast(int, data.attrib.get('sessionKey')) self.username = utils.findUsername(data) diff --git a/plexapi/base.py b/plexapi/base.py index e5219460..49292124 100644 --- a/plexapi/base.py +++ b/plexapi/base.py @@ -25,7 +25,7 @@ class PlexObject(object): ] if p]) def __setattr__(self, attr, value): - if value is not None or attr.startswith('_'): + if value is not None or attr.startswith('_') or attr not in self.__dict__: self.__dict__[attr] = value def __firstattr(self, *attrs): @@ -37,10 +37,11 @@ class PlexObject(object): def _buildItem(self, elem, cls=None, initpath=None, bytag=False): """ Factory function to build objects based on registered LIBRARY_TYPES. """ initpath = initpath or self._initpath - if cls: return cls(self._root, elem, initpath) libtype = elem.tag if bytag else elem.attrib.get('type') if libtype == 'photo' and elem.tag == 'Directory': libtype = 'photoalbum' + if cls and libtype == cls.TYPE: + return cls(self._root, elem, initpath) if libtype in utils.LIBRARY_TYPES: cls = utils.LIBRARY_TYPES[libtype] return cls(self._root, elem, initpath) @@ -123,8 +124,9 @@ class PlexPartialObject(PlexObject): return other is not None and self.key == other.key def __getattribute__(self, attr): + # Dragons inside.. :-/ + value = utils.getattributeOrNone(PlexPartialObject, self, attr) # Check a few cases where we dont want to reload - value = super(PlexPartialObject, self).__getattribute__(attr) if attr == 'key' or attr.startswith('_'): return value if value not in (None, []): return value if self.isFullObject(): return value @@ -135,7 +137,7 @@ class PlexPartialObject(PlexObject): log.warn("Reloading %s for attr '%s'" % (objname, attr)) # Reload and return the value self.reload() - return super(PlexPartialObject, self).__getattribute__(attr) + return utils.getattributeOrNone(PlexPartialObject, self, attr) def isFullObject(self): """ Retruns True if this is already a full object. A full object means all attributes @@ -246,9 +248,9 @@ class Playable(object): if kwargs: download_url = self.getStreamURL(**kwargs) else: - download_url = self._root.url('%s?download=1' % location.key) + download_url = self._root._url('%s?download=1' % location.key) filepath = utils.download(download_url, filename=filename, - savepath=savepath, session=self._root.session) + savepath=savepath, session=self._root._session) if filepath: filepaths.append(filepath) return filepaths diff --git a/plexapi/media.py b/plexapi/media.py index 62548945..8f242ac7 100644 --- a/plexapi/media.py +++ b/plexapi/media.py @@ -52,7 +52,7 @@ class Media(PlexObject): self.videoFrameRate = data.attrib.get('videoFrameRate') self.videoResolution = data.attrib.get('videoResolution') self.width = cast(int, data.attrib.get('width')) - self.parts = self._buildItems(data, MediaPart) + self.parts = self._buildItems(data, MediaPart, bytag=True) class MediaPart(PlexObject): diff --git a/plexapi/utils.py b/plexapi/utils.py index 3f14d08f..beba2cbc 100644 --- a/plexapi/utils.py +++ b/plexapi/utils.py @@ -135,6 +135,13 @@ def findUsername(data): return None +def getattributeOrNone(obj, self, attr): + try: + return super(obj, self).__getattribute__(attr) + except AttributeError: + return None + + def isInt(str): """ Returns True if the specified string passes as an int. """ try: diff --git a/plexapi/video.py b/plexapi/video.py index 4048dc28..795a788d 100644 --- a/plexapi/video.py +++ b/plexapi/video.py @@ -93,14 +93,14 @@ class Movie(Video, Playable): self.viewOffset = utils.cast(int, data.attrib.get('viewOffset', 0)) self.year = utils.cast(int, data.attrib.get('year')) self.collections = self._buildItems(data, media.Collection) - self.countries = self._buildItems(data, media.Country) - self.directors = self._buildItems(data, media.Director) - self.fields = self._buildItems(data, media.Field) - self.genres = self._buildItems(data, media.Genre) - self.media = self._buildItems(data, media.Media) - self.producers = self._buildItems(data, media.Producer) - self.roles = self._buildItems(data, media.Role) - self.writers = self._buildItems(data, media.Writer) + self.countries = self._buildItems(data, media.Country, bytag=True) + self.directors = self._buildItems(data, media.Director, bytag=True) + self.fields = self._buildItems(data, media.Field, bytag=True) + self.genres = self._buildItems(data, media.Genre, bytag=True) + self.media = self._buildItems(data, media.Media, bytag=True) + self.producers = self._buildItems(data, media.Producer, bytag=True) + self.roles = self._buildItems(data, media.Role, bytag=True) + self.writers = self._buildItems(data, media.Writer, bytag=True) @property def actors(self): @@ -164,8 +164,8 @@ class Show(Video): self.theme = data.attrib.get('theme') self.viewedLeafCount = utils.cast(int, data.attrib.get('viewedLeafCount')) self.year = utils.cast(int, data.attrib.get('year')) - self.genres = self._buildItems(data, media.Genre) - self.roles = self._buildItems(data, media.Role) + self.genres = self._buildItems(data, media.Genre, bytag=True) + self.roles = self._buildItems(data, media.Role, bytag=True) @property def actors(self): @@ -393,9 +393,9 @@ class Episode(Video, Playable): self.rating = utils.cast(float, data.attrib.get('rating')) self.viewOffset = utils.cast(int, data.attrib.get('viewOffset', 0)) self.year = utils.cast(int, data.attrib.get('year')) - self.directors = self._buildItems(data, media.Director) - self.media = self._buildItems(data, media.Media) - self.writers = self._buildItems(data, media.Writer) + self.directors = self._buildItems(data, media.Director, bytag=True) + self.media = self._buildItems(data, media.Media, bytag=True) + self.writers = self._buildItems(data, media.Writer, bytag=True) # data for active sessions and history self.sessionKey = utils.cast(int, data.attrib.get('sessionKey')) self.username = utils.findUsername(data) diff --git a/tests/test_audio.py b/tests/test_audio.py index 242423fe..553228db 100644 --- a/tests/test_audio.py +++ b/tests/test_audio.py @@ -55,7 +55,7 @@ def test_audio_Album_attrs(a_music_album): assert str(m.addedAt.date()) == '2017-01-17' assert [i.tag for i in m.genres] == ['Electronic'] assert m.index == '1' - assert m._initpath == '/library/metadata/21' + assert m._initpath == '/library/metadata/20/children' assert m.key == '/library/metadata/21' assert m.librarySectionID == '3' assert m.listType == 'audio'