A few more updates to get plexattrs.py fully running

This commit is contained in:
Michael Shepanski 2017-02-06 01:28:58 -05:00
parent 6a35f50a43
commit 4624512356
9 changed files with 123 additions and 86 deletions

View file

@ -91,9 +91,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._buildSubitems(data, media.Country)
self.genres = self._buildSubitems(data, media.Genre)
self.similar = self._buildSubitems(data, media.Similar)
self.countries = self._buildItems(data, media.Country)
self.genres = self._buildItems(data, media.Genre)
self.similar = self._buildItems(data, media.Similar)
def album(self, title):
""" Returns the :class:`~plexapi.audio.Album` that matches the specified title.
@ -186,7 +186,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._buildSubitems(data, media.Genre)
self.genres = self._buildItems(data, media.Genre)
def track(self, title):
""" Returns the :class:`~plexapi.audio.Track` that matches the specified title.
@ -293,8 +293,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._buildSubitems(data, media.Media)
self.moods = self._buildSubitems(data, media.Mood)
self.media = self._buildItems(data, media.Media)
self.moods = self._buildItems(data, media.Mood)
# data for active sessions and history
self.sessionKey = utils.cast(int, data.attrib.get('sessionKey'))
self.username = utils.findUsername(data)

View file

@ -135,17 +135,37 @@ class PlexObject(object):
return cls(self._root, elem, initpath)
raise UnknownType('Unknown library type: %s' % libtype)
def _buildSubitems(self, data, cls, tag=None, filters=None, *args):
""" Build and return a list of items (optionally filtered by tag). """
def _buildItems(self, data, cls=None, tag=None, attrs=None, safe=False):
""" Build and return a list of items (optionally filtered by tag).
Parameters:
data (ElementTree): XML data to search for items.
cls (PlexObject): Optionally specify the PlexObject to be built. If not specified
_buildItem will be called and the best guess item will be built.
tag (str): Only build items with the specified tag. If not specified and
cls is specified, tag will be set to cls.TYPE.
attrs (dict): Dict containing attributes to filter the elements by. If not
specified, all elements will be considered.
safe (bool): If True, dont raise an exception when unable to build an object.
"""
items = []
tag = tag or cls.TYPE
filters = filters or {}
tag = cls.TYPE if not tag and cls else tag
attrs = attrs or {}
for elem in data:
if elem.tag == tag:
for attr, value in filters.items():
if elem.attrib.get(attr) != str(value):
continue
items.append(cls(self._root, elem, self._initpath, *args))
try:
if not tag or elem.tag == tag:
for attr, value in attrs.items():
if elem.attrib.get(attr) != str(value):
continue
if cls is not None:
items.append(cls(self._root, elem, self._initpath))
else:
items.append(self._buildItem(elem, self._initpath))
except Exception as err:
if safe:
log.warn('Failed to build %s (type=%s); %s' % (elem.tag, elem.attrib.get('type', 'NA'), err))
continue
raise
return items
def _fetchItem(self, key, title=None, name=None):

View file

@ -1,5 +1,4 @@
# -*- coding: utf-8 -*-
import logging
from plexapi import X_PLEX_CONTAINER_SIZE, log, utils
from plexapi.base import PlexObject
from plexapi.compat import unquote
@ -81,11 +80,11 @@ class Library(PlexObject):
def onDeck(self):
""" Returns a list of all media items on deck. """
return utils.listItems(self.server, '/library/onDeck')
return self._fetchItems('/library/onDeck')
def recentlyAdded(self):
""" Returns a list of all media items recently added. """
return utils.listItems(self.server, '/library/recentlyAdded')
return self._fetchItems('/library/recentlyAdded')
def get(self, title): # this should use hub search when its merged
""" Return the first item from all items with the specified title.
@ -121,8 +120,8 @@ class Library(PlexObject):
args['type'] = utils.searchType(libtype)
for attr, value in kwargs.items():
args[attr] = value
query = '/library/all%s' % utils.joinArgs(args)
return utils.listItems(self.server, query)
key = '/library/all%s' % utils.joinArgs(args)
return self._fetchItems(key)
def cleanBundles(self):
""" Poster images and other metadata for items in your library are kept in "bundle"
@ -475,23 +474,17 @@ class PhotoSection(LibrarySection):
@utils.register_libtype
class Hub(object):
class Hub(PlexObject):
TYPE = 'Hub'
HUBTYPES = {'genre':Genre, 'director':Director, 'actor':Role}
FILTERTYPES = {'genre':Genre, 'director':Director, 'actor':Role}
def __init__(self, server, data, initpath):
def _loadData(self, data):
self._data = data
self.server = server
self.initpath = initpath
self.hubIdentifier = data.attrib.get('hubIdentifier')
self.size = utils.cast(int, data.attrib.get('size'))
self.title = data.attrib.get('title')
self.type = data.attrib.get('type')
if self.type in self.HUBTYPES:
mediacls = self.HUBTYPES[self.type]
self.items = [mediacls(self.server, elem) for elem in data]
else:
self.items = self._safe_builditems(data)
self.items = self._buildItems(data)
def __repr__(self):
return '<Hub:%s>' % self.title.encode('utf8')
@ -499,14 +492,11 @@ class Hub(object):
def __len__(self):
return self.size
def _safe_builditems(self, data):
items = []
for elem in data:
try:
items.append(utils.buildItem(self.server, elem, '/hubs'))
except Exception as err:
logging.warn('Failed %s to build %s; Error: %s' % (self.type, self.title, err))
return items
def _buildItems(self, data):
if self.type in self.FILTERTYPES:
cls = self.FILTERTYPES[self.type]
return [cls(self._root, elem, self._initpath) for elem in data]
return super(Hub, self)._buildItems(data, safe=True)
@utils.register_libtype

View file

@ -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._buildSubitems(data, MediaPart)
self.parts = self._buildItems(data, MediaPart)
def __repr__(self):
title = self.video.title.replace(' ','.')[0:20]
@ -84,13 +84,31 @@ class MediaPart(PlexObject):
self.id = cast(int, data.attrib.get('id'))
self.key = data.attrib.get('key')
self.size = cast(int, data.attrib.get('size'))
self.videoStreams = self._buildSubitems(data, VideoStream, 'Stream', {'streamType':VideoStream.STREAMTYPE})
self.audioStreams = self._buildSubitems(data, AudioStream, 'Stream', {'streamType':AudioStream.STREAMTYPE})
self.subtitleStreams = self._buildSubitems(data, SubtitleStream, 'Stream', {'streamType':SubtitleStream.STREAMTYPE})
self.streams = self._buildStreams(data)
def __repr__(self):
return '<%s:%s>' % (self.__class__.__name__, self.id)
def _buildStreams(self, data):
streams = []
for elem in data:
for cls in (VideoStream, AudioStream, SubtitleStream):
if elem.attrib.get('streamType') == str(cls.STREAMTYPE):
streams.append(cls(self._root, elem, self._initpath))
return streams
@property
def videoStreams(self):
return [s for s in self.streams if s.streamType == VideoStream.STREAMTYPE]
@property
def audioStreams(self):
return [s for s in self.streams if s.streamType == AudioStream.STREAMTYPE]
@property
def subtitleStreams(self):
return [s for s in self.streams if s.streamType == SubtitleStream.STREAMTYPE]
class MediaPartStream(PlexObject):
""" Base class for media streams. These consist of video, audio and subtitles.

View file

@ -252,7 +252,7 @@ class MyPlexResource(PlexObject):
self.home = utils.cast(bool, data.attrib.get('home'))
self.synced = utils.cast(bool, data.attrib.get('synced'))
self.presence = utils.cast(bool, data.attrib.get('presence'))
self.connections = self._buildSubitems(data, ResourceConnection)
self.connections = self._buildItems(data, ResourceConnection)
def __repr__(self):
return '<%s:%s>' % (self.__class__.__name__, self.name.encode('utf8'))

View file

@ -110,7 +110,7 @@ class Photo(PlexPartialObject):
self.type = data.attrib.get('type')
self.updatedAt = utils.toDatetime(data.attrib.get('updatedAt'))
self.year = utils.cast(int, data.attrib.get('year'))
self.media = self._buildSubitems(data, media.Media)
self.media = self._buildItems(data, media.Media)
def photoalbum(self):
""" Return this photo's :class:`~plexapi.photo.Photoalbum`. """

View file

@ -279,8 +279,8 @@ class PlexServer(PlexObject):
params['section'] = utils.SEARCHTYPES[mediatype]
if limit:
params['limit'] = limit
url = '/hubs/search?%s' % urlencode(params)
for hub in utils.listItems(self, url, bytag=True):
key = '/hubs/search?%s' % urlencode(params)
for hub in self._fetchItems(key, bytag=True):
results += hub.items
return results

View file

@ -92,15 +92,15 @@ class Movie(Video, Playable):
self.userRating = utils.cast(float, data.attrib.get('userRating'))
self.viewOffset = utils.cast(int, data.attrib.get('viewOffset', 0))
self.year = utils.cast(int, data.attrib.get('year'))
self.collections = self._buildSubitems(data, media.Collection)
self.countries = self._buildSubitems(data, media.Country)
self.directors = self._buildSubitems(data, media.Director)
self.fields = self._buildSubitems(data, media.Field)
self.genres = self._buildSubitems(data, media.Genre)
self.media = self._buildSubitems(data, media.Media)
self.producers = self._buildSubitems(data, media.Producer)
self.roles = self._buildSubitems(data, media.Role)
self.writers = self._buildSubitems(data, media.Writer)
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.videoStreams = utils.findStreams(self.media, 'videostream') # these dont go here
# self.audioStreams = utils.findStreams(self.media, 'audiostream') # these dont go here
# self.subtitleStreams = utils.findStreams(self.media, 'subtitlestream') # these dont go here
@ -167,8 +167,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._buildSubitems(data, media.Genre)
self.roles = self._buildSubitems(data, media.Role)
self.genres = self._buildItems(data, media.Genre)
self.roles = self._buildItems(data, media.Role)
@property
def actors(self):
@ -417,12 +417,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._buildSubitems(data, media.Director)
self.media = self._buildSubitems(data, media.Media)
self.writers = self._buildSubitems(data, media.Writer)
# self.videoStreams = utils.findStreams(self.media, 'videostream')
# self.audioStreams = utils.findStreams(self.media, 'audiostream')
# self.subtitleStreams = utils.findStreams(self.media, 'subtitlestream')
self.directors = self._buildItems(data, media.Director)
self.media = self._buildItems(data, media.Media)
self.writers = self._buildItems(data, media.Writer)
# data for active sessions and history
self.sessionKey = utils.cast(int, data.attrib.get('sessionKey'))
self.username = utils.findUsername(data)

View file

@ -48,6 +48,11 @@ DONT_RELOAD = (
'client.PlexClient', # we dont have the token to reload.
#'server.PlexServer', # setting version to None? :(
)
TAGATTRS = {
'Media': 'media',
'Country': 'countries',
}
STOP_RECURSING_AT = (
#'media.MediaPart',
)
@ -64,16 +69,17 @@ class PlexAttributes():
def run(self):
starttime = time.time()
# self._parse_myplex()
# self._parse_server()
# self._parse_library()
# self._parse_audio()
# self._parse_photo()
# self._parse_movie()
# self._parse_show()
# self._parse_client()
# self._parse_playlist()
# self._parse_sync()
self._parse_myplex()
self._parse_server()
self._parse_search()
self._parse_library()
self._parse_audio()
self._parse_photo()
self._parse_movie()
self._parse_show()
self._parse_client()
self._parse_playlist()
self._parse_sync()
self.runtime = round((time.time() - starttime) / 60.0, 1)
return self
@ -88,21 +94,24 @@ class PlexAttributes():
def _parse_server(self):
self._load_attrs(self.plex, 'serv')
self._load_attrs(self.plex.account(), 'serv')
self._load_attrs(self.plex.history()[:20], 'hist')
# self._load_attrs(self.plex.playlists())
# for search in ('cre', 'ani', 'mik', 'she'):
# self._load_attrs(self.plex.search('cre'))
# self._load_attrs(self.plex.sessions(), 'sess')
self._load_attrs(self.plex.history()[:50], 'hist')
self._load_attrs(self.plex.history()[50:], 'hist')
self._load_attrs(self.plex.sessions(), 'sess')
def _parse_search(self):
for search in ('cre', 'ani', 'mik', 'she', 'bea'):
self._load_attrs(self.plex.search(search), 'hub')
def _parse_library(self):
cat = 'lib'
self._load_attrs(self.plex.library, cat)
# self._load_attrs(self.plex.library.sections())
# self._load_attrs(self.plex.library.all()[:20])
# self._load_attrs(self.plex.library.onDeck()[:20])
# self._load_attrs(self.plex.library.recentlyAdded()[:20])
# for search in ('cat', 'dog', 'rat'):
# self._load_attrs(self.plex.library.search(search)[:20])
#self._load_attrs(self.plex.library.all()[:50], 'all')
self._load_attrs(self.plex.library.onDeck()[:50], 'deck')
self._load_attrs(self.plex.library.recentlyAdded()[:50], 'add')
for search in ('cat', 'dog', 'rat', 'gir', 'mou'):
self._load_attrs(self.plex.library.search(search)[:50], 'srch')
# TODO: Implement section search (remove library search?)
# TODO: Implement section search filters
def _parse_audio(self):
cat = 'lib'
@ -142,7 +151,7 @@ class PlexAttributes():
for show in showsection.all():
self._load_attrs(show, cat)
for season in show.seasons():
self._load_attrs(show, cat)
self._load_attrs(season, cat)
for episode in season.episodes():
self._load_attrs(episode, cat)
@ -200,6 +209,9 @@ class PlexAttributes():
if cat: categories[attr].add(cat)
if elem.attrib[attr] and len(examples[attr]) <= self.opts.examples:
examples[attr].add(elem.attrib[attr])
for subelem in elem:
attrname = TAGATTRS.get(subelem.tag, '%ss' % subelem.tag.lower())
attrs['%s[]' % attrname] += 1
def _load_obj_attrs(self, clsname, obj, attrs):
if clsname in STOP_RECURSING_AT: return None