Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions plexapi/audio.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,11 +155,7 @@ def __iter__(self):
def hubs(self):
""" Returns a list of :class:`~plexapi.library.Hub` objects. """
data = self._server.query(self._details_key)
directory = data.find('Directory')
if directory:
related = directory.find('Related')
if related:
return self.findItems(related, library.Hub)
return self.findItems(data, library.Hub, rtag='Related')

def album(self, title):
""" Returns the :class:`~plexapi.audio.Album` that matches the specified title.
Expand Down Expand Up @@ -406,7 +402,7 @@ def locations(self):
""" This does not exist in plex xml response but is added to have a common
interface to get the locations of the track.

Retruns:
Returns:
List<str> of file paths where the track is found on disk.
"""
return [part.file for part in self.iterParts() if part]
Expand Down
12 changes: 7 additions & 5 deletions plexapi/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

from plexapi import log, utils
from plexapi.exceptions import BadRequest, NotFound, UnknownType, Unsupported
from plexapi.utils import tag_plural, tag_helper

DONT_RELOAD_FOR_KEYS = {'key', 'session'}
DONT_OVERWRITE_SESSION_KEYS = {'usernames', 'players', 'transcodeSessions', 'session'}
Expand Down Expand Up @@ -263,7 +262,7 @@ def fetchItems(self, ekey, cls=None, container_start=None, container_size=None,
item.librarySectionID = librarySectionID
return items

def findItems(self, data, cls=None, initpath=None, **kwargs):
def findItems(self, data, cls=None, initpath=None, rtag=None, **kwargs):
""" Load the specified data to find and build all items with the specified tag
and attrs. See :func:`~plexapi.base.PlexObject.fetchItem` for more details
on how this is used.
Expand All @@ -273,6 +272,9 @@ def findItems(self, data, cls=None, initpath=None, **kwargs):
kwargs['etag'] = cls.TAG
if cls and cls.TYPE and 'type' not in kwargs:
kwargs['type'] = cls.TYPE
# rtag to iter on a specific root tag
if rtag:
data = next(data.iter(rtag), [])
# loop through all data elements to find matches
items = []
for elem in data:
Expand Down Expand Up @@ -478,7 +480,7 @@ def analyze(self):
self._server.query(key, method=self._server._session.put)

def isFullObject(self):
""" Retruns True if this is already a full object. A full object means all attributes
""" Returns True if this is already a full object. A full object means all attributes
were populated from the api path representing only this item. For example, the
search result for a movie often only contain a portion of the attributes a full
object (main url) for that movie would contain.
Expand Down Expand Up @@ -521,9 +523,9 @@ def _edit_tags(self, tag, items, locked=True, remove=False):
"""
if not isinstance(items, list):
items = [items]
value = getattr(self, tag_plural(tag))
value = getattr(self, utils.tag_plural(tag))
existing_tags = [t.tag for t in value if t and remove is False]
tag_edits = tag_helper(tag, existing_tags + items, locked, remove)
tag_edits = utils.tag_helper(tag, existing_tags + items, locked, remove)
self.edit(**tag_edits)

def refresh(self):
Expand Down
10 changes: 3 additions & 7 deletions plexapi/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -603,17 +603,13 @@ def _loadFilters(self):

key = _key % (self.key, 'all')
data = self._server.query(key)
meta = data.find('Meta')
if meta:
self._filterTypes = self.findItems(meta, FilteringType)
self._fieldTypes = self.findItems(meta, FilteringFieldType)
self._filterTypes = self.findItems(data, FilteringType, rtag='Meta')
self._fieldTypes = self.findItems(data, FilteringFieldType, rtag='Meta')

if self.TYPE != 'photo': # No collections for photo library
key = _key % (self.key, 'collections')
data = self._server.query(key)
meta = data.find('Meta')
if meta:
self._filterTypes.extend(self.findItems(meta, FilteringType))
self._filterTypes.extend(self.findItems(data, FilteringType, rtag='Meta'))

def filterTypes(self):
""" Returns a list of available :class:`~plexapi.library.FilteringType` for this library section. """
Expand Down
181 changes: 90 additions & 91 deletions plexapi/media.py

Large diffs are not rendered by default.

9 changes: 1 addition & 8 deletions plexapi/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,8 @@ class AdvancedSettingsMixin(object):

def preferences(self):
""" Returns a list of :class:`~plexapi.settings.Preferences` objects. """
items = []
data = self._server.query(self._details_key)
for item in data.iter('Preferences'):
for elem in item:
setting = settings.Preferences(data=elem, server=self._server)
setting._initpath = self.key
items.append(setting)

return items
return self.findItems(data, settings.Preferences, rtag='Preferences')

def preference(self, pref):
""" Returns a :class:`~plexapi.settings.Preferences` object for the specified pref.
Expand Down
29 changes: 7 additions & 22 deletions plexapi/myplex.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
from plexapi.server import PlexServer
from plexapi.sonos import PlexSonosClient
from plexapi.sync import SyncItem, SyncList
from plexapi.utils import joinArgs
from requests.status_codes import _codes as codes


Expand Down Expand Up @@ -128,26 +127,18 @@ def _loadData(self, data):
self.title = data.attrib.get('title')
self.username = data.attrib.get('username')
self.uuid = data.attrib.get('uuid')
subscription = data.find('subscription')

subscription = data.find('subscription')
self.subscriptionActive = utils.cast(bool, subscription.attrib.get('active'))
self.subscriptionStatus = subscription.attrib.get('status')
self.subscriptionPlan = subscription.attrib.get('plan')

self.subscriptionFeatures = []
for feature in subscription.iter('feature'):
self.subscriptionFeatures.append(feature.attrib.get('id'))
self.subscriptionFeatures = self.listAttrs(subscription, 'id', etag='feature')

roles = data.find('roles')
self.roles = []
if roles is not None:
for role in roles.iter('role'):
self.roles.append(role.attrib.get('id'))
self.roles = self.listAttrs(roles, 'id', etag='role')

entitlements = data.find('entitlements')
self.entitlements = []
for entitlement in entitlements.iter('entitlement'):
self.entitlements.append(entitlement.attrib.get('id'))
self.entitlements = self.listAttrs(entitlements, 'id', etag='entitlement')

# TODO: Fetch missing MyPlexAccount attributes
self.profile_settings = None
Expand Down Expand Up @@ -460,7 +451,7 @@ def updateFriend(self, user, server, sections=None, removeSections=False, allowS
if isinstance(allowChannels, dict):
params['filterMusic'] = self._filterDictToStr(filterMusic or {})
if params:
url += joinArgs(params)
url += utils.joinArgs(params)
response_filters = self.query(url, self._session.put)
return response_servers, response_filters

Expand Down Expand Up @@ -885,13 +876,7 @@ def sections(self):
"""
url = MyPlexAccount.FRIENDSERVERS.format(machineId=self.machineIdentifier, serverId=self.id)
data = self._server.query(url)
sections = []

for section in data.iter('Section'):
if ElementTree.iselement(section):
sections.append(Section(self, section, url))

return sections
return self.findItems(data, Section, rtag='SharedServer')

def history(self, maxresults=9999999, mindate=None):
""" Get all Play History for a user in this shared server.
Expand Down Expand Up @@ -1076,7 +1061,7 @@ def _loadData(self, data):
self.screenDensity = data.attrib.get('screenDensity')
self.createdAt = utils.toDatetime(data.attrib.get('createdAt'))
self.lastSeenAt = utils.toDatetime(data.attrib.get('lastSeenAt'))
self.connections = [connection.attrib.get('uri') for connection in data.iter('Connection')]
self.connections = self.listAttrs(data, 'uri', etag='Connection')

def connect(self, timeout=None):
""" Returns a new :class:`~plexapi.client.PlexClient` or :class:`~plexapi.server.PlexServer`
Expand Down
2 changes: 1 addition & 1 deletion plexapi/photo.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ def locations(self):
""" This does not exist in plex xml response but is added to have a common
interface to get the locations of the photo.

Retruns:
Returns:
List<str> of file paths where the photo is found on disk.
"""
return [part.file for item in self.media for part in item.parts if part]
Expand Down
18 changes: 9 additions & 9 deletions plexapi/playlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from plexapi.library import LibrarySection
from plexapi.mixins import ArtMixin, PosterMixin
from plexapi.playqueue import PlayQueue
from plexapi.utils import cast, deprecated, toDatetime
from plexapi.utils import deprecated


@utils.registerPlexObject
Expand Down Expand Up @@ -42,23 +42,23 @@ class Playlist(PlexPartialObject, Playable, ArtMixin, PosterMixin):
def _loadData(self, data):
""" Load attribute values from Plex XML response. """
Playable._loadData(self, data)
self.addedAt = toDatetime(data.attrib.get('addedAt'))
self.allowSync = cast(bool, data.attrib.get('allowSync'))
self.addedAt = utils.toDatetime(data.attrib.get('addedAt'))
self.allowSync = utils.cast(bool, data.attrib.get('allowSync'))
self.composite = data.attrib.get('composite') # url to thumbnail
self.content = data.attrib.get('content')
self.duration = cast(int, data.attrib.get('duration'))
self.durationInSeconds = cast(int, data.attrib.get('durationInSeconds'))
self.duration = utils.cast(int, data.attrib.get('duration'))
self.durationInSeconds = utils.cast(int, data.attrib.get('durationInSeconds'))
self.icon = data.attrib.get('icon')
self.guid = data.attrib.get('guid')
self.key = data.attrib.get('key', '').replace('/items', '') # FIX_BUG_50
self.leafCount = cast(int, data.attrib.get('leafCount'))
self.leafCount = utils.cast(int, data.attrib.get('leafCount'))
self.playlistType = data.attrib.get('playlistType')
self.ratingKey = cast(int, data.attrib.get('ratingKey'))
self.smart = cast(bool, data.attrib.get('smart'))
self.ratingKey = utils.cast(int, data.attrib.get('ratingKey'))
self.smart = utils.cast(bool, data.attrib.get('smart'))
self.summary = data.attrib.get('summary')
self.title = data.attrib.get('title')
self.type = data.attrib.get('type')
self.updatedAt = toDatetime(data.attrib.get('updatedAt'))
self.updatedAt = utils.toDatetime(data.attrib.get('updatedAt'))
self._items = None # cache for self.items
self._section = None # cache for self.section

Expand Down
Loading