22import os
33from urllib .parse import quote_plus , urlencode
44
5- from plexapi import media , utils
5+ from plexapi import media , utils , settings , library
66from plexapi .base import Playable , PlexPartialObject
77from plexapi .exceptions import BadRequest , NotFound
88
@@ -390,6 +390,10 @@ class Show(Video):
390390 TYPE = 'show'
391391 METADATA_TYPE = 'episode'
392392
393+ _include = ('?checkFiles=1&includeExtras=1&includeRelated=1'
394+ '&includeOnDeck=1&includeChapters=1&includePopularLeaves=1'
395+ '&includeMarkers=1&includeConcerts=1&includePreferences=1' )
396+
393397 def __iter__ (self ):
394398 for season in self .seasons ():
395399 yield season
@@ -399,6 +403,7 @@ def _loadData(self, data):
399403 Video ._loadData (self , data )
400404 # fix key if loaded from search
401405 self .key = self .key .replace ('/children' , '' )
406+ self ._details_key = self .key + self ._include
402407 self .art = data .attrib .get ('art' )
403408 self .banner = data .attrib .get ('banner' )
404409 self .childCount = utils .cast (int , data .attrib .get ('childCount' ))
@@ -431,6 +436,29 @@ def isWatched(self):
431436 """ Returns True if this show is fully watched. """
432437 return bool (self .viewedLeafCount == self .leafCount )
433438
439+ def preferences (self ):
440+ """ Returns a list of :class:`~plexapi.settings.Preferences` objects. """
441+ items = []
442+ data = self ._server .query (self ._details_key )
443+ for item in data .iter ('Preferences' ):
444+ for elem in item :
445+ items .append (settings .Preferences (data = elem , server = self ._server ))
446+
447+ return items
448+
449+ def hubs (self ):
450+ """ Returns a list of :class:`~plexapi.library.Hub` objects. """
451+ data = self ._server .query (self ._details_key )
452+ for item in data .iter ('Related' ):
453+ return self .findItems (item , library .Hub )
454+
455+ def onDeck (self ):
456+ """ Returns shows On Deck :class:`~plexapi.video.Video` object.
457+ If show is unwatched, return will likely be the first episode.
458+ """
459+ data = self ._server .query (self ._details_key )
460+ return self .findItems ([item for item in data .iter ('OnDeck' )][0 ])[0 ]
461+
434462 def seasons (self , ** kwargs ):
435463 """ Returns a list of :class:`~plexapi.video.Season` objects. """
436464 key = '/library/metadata/%s/children?excludeAllLeaves=1' % self .ratingKey
@@ -645,7 +673,7 @@ class Episode(Playable, Video):
645673
646674 _include = ('?checkFiles=1&includeExtras=1&includeRelated=1'
647675 '&includeOnDeck=1&includeChapters=1&includePopularLeaves=1'
648- '&includeConcerts=1&includePreferences=1' )
676+ '&includeMarkers=1& includeConcerts=1&includePreferences=1' )
649677
650678 def _loadData (self , data ):
651679 """ Load attribute values from Plex XML response. """
@@ -681,6 +709,7 @@ def _loadData(self, data):
681709 self .labels = self .findItems (data , media .Label )
682710 self .collections = self .findItems (data , media .Collection )
683711 self .chapters = self .findItems (data , media .Chapter )
712+ self .markers = self .findItems (data , media .Marker )
684713
685714 def __repr__ (self ):
686715 return '<%s>' % ':' .join ([p for p in [
@@ -712,6 +741,13 @@ def seasonEpisode(self):
712741 """ Returns the s00e00 string containing the season and episode. """
713742 return 's%se%s' % (str (self .seasonNumber ).zfill (2 ), str (self .index ).zfill (2 ))
714743
744+ @property
745+ def hasIntroMarker (self ):
746+ """ Returns True if this episode has an intro marker in the xml. """
747+ if not self .isFullObject ():
748+ self .reload ()
749+ return any (marker .type == 'intro' for marker in self .markers )
750+
715751 def season (self ):
716752 """" Return this episodes :func:`~plexapi.video.Season`.. """
717753 return self .fetchItem (self .parentKey )
0 commit comments