Skip to content

Commit b401ef3

Browse files
authored
Merge pull request #258 from pkkid/chap
Chap
2 parents 75bf75f + f6aa27e commit b401ef3

File tree

4 files changed

+64
-12
lines changed

4 files changed

+64
-12
lines changed

plexapi/base.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ def __init__(self, server, data, initpath=None):
4343
self._server = server
4444
self._data = data
4545
self._initpath = initpath or self.key
46+
self._details_key = ''
4647
if data is not None:
4748
self._loadData(data)
4849

@@ -182,7 +183,7 @@ def listAttrs(self, data, attr, **kwargs):
182183

183184
def reload(self, key=None):
184185
""" Reload the data for this object from self.key. """
185-
key = key or self.key
186+
key = key or self._details_key or self.key
186187
if not key:
187188
raise Unsupported('Cannot reload an object not built from a URL.')
188189
self._initpath = key
@@ -275,7 +276,7 @@ def __getattribute__(self, attr):
275276
if attr == 'key' or attr.startswith('_'): return value
276277
if value not in (None, []): return value
277278
if self.isFullObject(): return value
278-
# Log warning that were reloading the object
279+
# Log the reload.
279280
clsname = self.__class__.__name__
280281
title = self.__dict__.get('title', self.__dict__.get('name'))
281282
objname = "%s '%s'" % (clsname, title) if title else clsname
@@ -448,6 +449,14 @@ def _loadData(self, data):
448449
self.viewedAt = utils.toDatetime(data.attrib.get('viewedAt')) # history
449450
self.playlistItemID = utils.cast(int, data.attrib.get('playlistItemID')) # playlist
450451

452+
def isFullObject(self):
453+
""" Retruns True if this is already a full object. A full object means all attributes
454+
were populated from the api path representing only this item. For example, the
455+
search result for a movie often only contain a portion of the attributes a full
456+
object (main url) for that movie contain.
457+
"""
458+
return self._details_key == self._initpath or not self.key
459+
451460
def getStreamURL(self, **params):
452461
""" Returns a stream url that may be used by external applications such as VLC.
453462

plexapi/media.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,26 @@ class Writer(MediaTag):
469469
FILTER = 'writer'
470470

471471

472+
@utils.registerPlexObject
473+
class Chapter(PlexObject):
474+
""" Represents a single Writer media tag.
475+
476+
Attributes:
477+
TAG (str): 'Chapter'
478+
"""
479+
TAG = 'Chapter'
480+
481+
def _loadData(self, data):
482+
self._data = data
483+
self.id = cast(int, data.attrib.get('id', 0))
484+
self.filter = data.attrib.get('filter') # I couldn't filter on it anyways
485+
self.tag = data.attrib.get('tag')
486+
self.title = self.tag
487+
self.index = cast(int, data.attrib.get('index'))
488+
self.start = cast(int, data.attrib.get('startTimeOffset'))
489+
self.end = cast(int, data.attrib.get('endTimeOffset'))
490+
491+
472492
@utils.registerPlexObject
473493
class Field(PlexObject):
474494
""" Represents a single Field.

plexapi/myplex.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -512,11 +512,12 @@ def sections(self):
512512
class MyPlexResource(PlexObject):
513513
""" This object represents resources connected to your Plex server that can provide
514514
content such as Plex Media Servers, iPhone or Android clients, etc. The raw xml
515-
for the data presented here can be found at: https://plex.tv/api/resources?includeHttps=1
515+
for the data presented here can be found at:
516+
https://plex.tv/api/resources?includeHttps=1&includeRelay=1
516517
517518
Attributes:
518519
TAG (str): 'Device'
519-
key (str): 'https://plex.tv/api/resources?includeHttps=1'
520+
key (str): 'https://plex.tv/api/resources?includeHttps=1&includeRelay=1'
520521
accessToken (str): This resources accesstoken.
521522
clientIdentifier (str): Unique ID for this resource.
522523
connections (list): List of :class:`~myplex.ResourceConnection` objects
@@ -537,7 +538,7 @@ class MyPlexResource(PlexObject):
537538
synced (bool): Unknown (possibly True if the resource has synced content?)
538539
"""
539540
TAG = 'Device'
540-
key = 'https://plex.tv/api/resources?includeHttps=1'
541+
key = 'https://plex.tv/api/resources?includeHttps=1&includeRelay=1'
541542

542543
def _loadData(self, data):
543544
self._data = data
@@ -557,6 +558,11 @@ def _loadData(self, data):
557558
self.synced = utils.cast(bool, data.attrib.get('synced'))
558559
self.presence = utils.cast(bool, data.attrib.get('presence'))
559560
self.connections = self.findItems(data, ResourceConnection)
561+
self.publicAddressMatches = utils.cast(bool, data.attrib.get('publicAddressMatches'))
562+
# This seems to only be available if its not your device (say are shared server)
563+
self.httpsRequired = utils.cast(bool, data.attrib.get('httpsRequired'))
564+
self.ownerid = utils.cast(int, data.attrib.get('ownerId', 0))
565+
self.sourceTitle = data.attrib.get('sourceTitle') # owners plex username.
560566

561567
def connect(self, ssl=None, timeout=None):
562568
""" Returns a new :class:`~server.PlexServer` or :class:`~client.PlexClient` object.
@@ -615,6 +621,7 @@ def _loadData(self, data):
615621
self.uri = data.attrib.get('uri')
616622
self.local = utils.cast(bool, data.attrib.get('local'))
617623
self.httpuri = 'http://%s:%s' % (self.address, self.port)
624+
self.relay = utils.cast(bool, data.attrib.get('relay'))
618625

619626

620627
class MyPlexDevice(PlexObject):

plexapi/video.py

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def _loadData(self, data):
3030
self._data = data
3131
self.listType = 'video'
3232
self.addedAt = utils.toDatetime(data.attrib.get('addedAt'))
33-
self.key = data.attrib.get('key')
33+
self.key = data.attrib.get('key', '')
3434
self.lastViewedAt = utils.toDatetime(data.attrib.get('lastViewedAt'))
3535
self.librarySectionID = data.attrib.get('librarySectionID')
3636
self.ratingKey = utils.cast(int, data.attrib.get('ratingKey'))
@@ -79,11 +79,11 @@ def markUnwatched(self):
7979

8080

8181
@utils.registerPlexObject
82-
class Movie(Video, Playable):
82+
class Movie(Playable, Video):
8383
""" Represents a single Movie.
8484
8585
Attributes:
86-
TAG (str): 'Diectory'
86+
TAG (str): 'Video'
8787
TYPE (str): 'movie'
8888
art (str): Key to movie artwork (/library/metadata/<ratingkey>/art/<artid>)
8989
audienceRating (float): Audience rating (usually from Rotten Tomatoes).
@@ -111,14 +111,21 @@ class Movie(Video, Playable):
111111
producers (List<:class:`~plexapi.media.Producer`>): List of producers objects.
112112
roles (List<:class:`~plexapi.media.Role`>): List of role objects.
113113
writers (List<:class:`~plexapi.media.Writer`>): List of writers objects.
114+
chapters (List<:class:`~plexapi.media.Chapter`>): List of Chapter objects.
115+
similar (List<:class:`~plexapi.media.Similar`>): List of Similar objects.
114116
"""
115117
TAG = 'Video'
116118
TYPE = 'movie'
119+
_include = ('?checkFiles=1&includeExtras=1&includeRelated=1'
120+
'&includeOnDeck=1&includeChapters=1&includePopularLeaves=1'
121+
'&includeConcerts=1&includePreferences=1')
117122

118123
def _loadData(self, data):
119124
""" Load attribute values from Plex XML response. """
120125
Video._loadData(self, data)
121126
Playable._loadData(self, data)
127+
128+
self._details_key = self.key + self._include
122129
self.art = data.attrib.get('art')
123130
self.audienceRating = utils.cast(float, data.attrib.get('audienceRating'))
124131
self.audienceRatingImage = data.attrib.get('audienceRatingImage')
@@ -147,6 +154,8 @@ def _loadData(self, data):
147154
self.roles = self.findItems(data, media.Role)
148155
self.writers = self.findItems(data, media.Writer)
149156
self.labels = self.findItems(data, media.Label)
157+
self.chapters = self.findItems(data, media.Chapter)
158+
self.similar = self.findItems(data, media.Similar)
150159

151160
@property
152161
def actors(self):
@@ -204,7 +213,7 @@ class Show(Video):
204213
""" Represents a single Show (including all seasons and episodes).
205214
206215
Attributes:
207-
TAG (str): 'Diectory'
216+
TAG (str): 'Directory'
208217
TYPE (str): 'show'
209218
art (str): Key to show artwork (/library/metadata/<ratingkey>/art/<artid>)
210219
banner (str): Key to banner artwork (/library/metadata/<ratingkey>/art/<artid>)
@@ -223,6 +232,7 @@ class Show(Video):
223232
year (int): Year the show was released.
224233
genres (List<:class:`~plexapi.media.Genre`>): List of genre objects.
225234
roles (List<:class:`~plexapi.media.Role`>): List of role objects.
235+
similar (List<:class:`~plexapi.media.Similar`>): List of Similar objects.
226236
"""
227237
TAG = 'Directory'
228238
TYPE = 'show'
@@ -255,6 +265,7 @@ def _loadData(self, data):
255265
self.genres = self.findItems(data, media.Genre)
256266
self.roles = self.findItems(data, media.Role)
257267
self.labels = self.findItems(data, media.Label)
268+
self.similar = self.findItems(data, media.Similar)
258269

259270
@property
260271
def actors(self):
@@ -341,7 +352,7 @@ class Season(Video):
341352
""" Represents a single Show Season (including all episodes).
342353
343354
Attributes:
344-
TAG (str): 'Diectory'
355+
TAG (str): 'Directory'
345356
TYPE (str): 'season'
346357
leafCount (int): Number of episodes in season.
347358
index (int): Season number.
@@ -437,11 +448,11 @@ def download(self, savepath=None, keep_orginal_name=False, **kwargs):
437448

438449

439450
@utils.registerPlexObject
440-
class Episode(Video, Playable):
451+
class Episode(Playable, Video):
441452
""" Represents a single Shows Episode.
442453
443454
Attributes:
444-
TAG (str): 'Diectory'
455+
TAG (str): 'Video'
445456
TYPE (str): 'episode'
446457
art (str): Key to episode artwork (/library/metadata/<ratingkey>/art/<artid>)
447458
chapterSource (str): Unknown (media).
@@ -471,11 +482,15 @@ class Episode(Video, Playable):
471482
"""
472483
TAG = 'Video'
473484
TYPE = 'episode'
485+
_include = ('?checkFiles=1&includeExtras=1&includeRelated=1'
486+
'&includeOnDeck=1&includeChapters=1&includePopularLeaves=1'
487+
'&includeConcerts=1&includePreferences=1')
474488

475489
def _loadData(self, data):
476490
""" Load attribute values from Plex XML response. """
477491
Video._loadData(self, data)
478492
Playable._loadData(self, data)
493+
self._details_key = self.key + self._include
479494
self._seasonNumber = None # cached season number
480495
self.art = data.attrib.get('art')
481496
self.chapterSource = data.attrib.get('chapterSource')
@@ -504,6 +519,7 @@ def _loadData(self, data):
504519
self.writers = self.findItems(data, media.Writer)
505520
self.labels = self.findItems(data, media.Label)
506521
self.collections = self.findItems(data, media.Collection)
522+
self.chapters = self.findItems(data, media.Chapter)
507523

508524
def __repr__(self):
509525
return '<%s>' % ':'.join([p for p in [

0 commit comments

Comments
 (0)