mirror of
https://github.com/pkkid/python-plexapi
synced 2024-11-10 06:04:15 +00:00
A few more updates to get plexattrs.py fully running
This commit is contained in:
parent
6a35f50a43
commit
4624512356
9 changed files with 123 additions and 86 deletions
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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'))
|
||||
|
|
|
@ -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`. """
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue