mirror of
https://github.com/pkkid/python-plexapi
synced 2024-11-26 13:40:22 +00:00
Update timeline to return an active timeline object w/ attributes (#572)
* timeline() now returns ClientTimeline objects * timeline() creates and returns ClientTimeline objects with associated attributes and caching * Refactor isPlayingMedia to use the new attributes and fix it's default value * Clarify docstrings * Remove default param in timeline call & fix docstring typo * return empty list if `timelines()` comes back empty Web clients can occasionally return no timelines if no media has been played on them or if nothing has played for a while, this prevents errors in those cases. * typo * Workaround for unresponsive clients * Use sendCommand rather than timelines() for PTP workaround * Remove workaround, set timeline's wait default to 1 * set timelines() wait default to 0, document buggy behavior * Use ClientTimeline.key for consistency Co-authored-by: jjlawren <jjlawren@users.noreply.github.com> * cast playQueue's IDs as int Co-authored-by: jjlawren <jjlawren@users.noreply.github.com> * Add audio attribs & make casts bool from int where it makes sense. Co-authored-by: jjlawren <jjlawren@users.noreply.github.com>
This commit is contained in:
parent
163d94d249
commit
f9f3535751
1 changed files with 63 additions and 13 deletions
|
@ -69,6 +69,8 @@ class PlexClient(PlexObject):
|
|||
self._proxyThroughServer = False
|
||||
self._commandId = 0
|
||||
self._last_call = 0
|
||||
self._timeline_cache = []
|
||||
self._timeline_cache_timestamp = 0
|
||||
if not any([data is not None, initpath, baseurl, token]):
|
||||
self._baseurl = CONFIG.get('auth.client_baseurl', 'http://localhost:32433')
|
||||
self._token = logfilter.add_secret(CONFIG.get('auth.client_token'))
|
||||
|
@ -199,7 +201,7 @@ class PlexClient(PlexObject):
|
|||
self._last_call = t
|
||||
elif t - self._last_call >= 80 and self.product in ('ptp', 'Plex Media Player'):
|
||||
self._last_call = t
|
||||
self.timeline(wait=0)
|
||||
self.sendCommand(ClientTimeline.key, wait=0)
|
||||
|
||||
params['commandID'] = self._nextCommandId()
|
||||
key = '/player/%s%s' % (command, utils.joinArgs(params))
|
||||
|
@ -540,20 +542,68 @@ class PlexClient(PlexObject):
|
|||
|
||||
# -------------------
|
||||
# Timeline Commands
|
||||
def timeline(self, wait=1):
|
||||
""" Poll the current timeline and return the XML response. """
|
||||
return self.sendCommand('timeline/poll', wait=wait)
|
||||
def timelines(self, wait=0):
|
||||
"""Poll the client's timelines, create, and return timeline objects.
|
||||
Some clients may not always respond to timeline requests, believe this
|
||||
to be a Plex bug.
|
||||
"""
|
||||
t = time.time()
|
||||
if t - self._timeline_cache_timestamp > 1:
|
||||
self._timeline_cache_timestamp = t
|
||||
timelines = self.sendCommand(ClientTimeline.key, wait=wait) or []
|
||||
self._timeline_cache = [ClientTimeline(self, data) for data in timelines]
|
||||
|
||||
def isPlayingMedia(self, includePaused=False):
|
||||
""" Returns True if any media is currently playing.
|
||||
return self._timeline_cache
|
||||
|
||||
@property
|
||||
def timeline(self):
|
||||
"""Returns the active timeline object."""
|
||||
return next((x for x in self.timelines() if x.state != 'stopped'), None)
|
||||
|
||||
def isPlayingMedia(self, includePaused=True):
|
||||
"""Returns True if any media is currently playing.
|
||||
|
||||
Parameters:
|
||||
includePaused (bool): Set True to treat currently paused items
|
||||
as playing (optional; default True).
|
||||
as playing (optional; default True).
|
||||
"""
|
||||
for mediatype in self.timeline(wait=0):
|
||||
if mediatype.get('state') == 'playing':
|
||||
return True
|
||||
if includePaused and mediatype.get('state') == 'paused':
|
||||
return True
|
||||
return False
|
||||
state = getattr(self.timeline, "state", None)
|
||||
return bool(state == 'playing' or (includePaused and state == 'paused'))
|
||||
|
||||
|
||||
class ClientTimeline(PlexObject):
|
||||
"""Get the timeline's attributes."""
|
||||
|
||||
key = 'timeline/poll'
|
||||
|
||||
def _loadData(self, data):
|
||||
self._data = data
|
||||
self.address = data.attrib.get('address')
|
||||
self.audioStreamId = utils.cast(int, data.attrib.get('audioStreamId'))
|
||||
self.autoPlay = utils.cast(bool, data.attrib.get('autoPlay'))
|
||||
self.containerKey = data.attrib.get('containerKey')
|
||||
self.controllable = data.attrib.get('controllable')
|
||||
self.duration = utils.cast(int, data.attrib.get('duration'))
|
||||
self.itemType = data.attrib.get('itemType')
|
||||
self.key = data.attrib.get('key')
|
||||
self.location = data.attrib.get('location')
|
||||
self.machineIdentifier = data.attrib.get('machineIdentifier')
|
||||
self.partCount = utils.cast(int, data.attrib.get('partCount'))
|
||||
self.partIndex = utils.cast(int, data.attrib.get('partIndex'))
|
||||
self.playQueueID = utils.cast(int, data.attrib.get('playQueueID'))
|
||||
self.playQueueItemID = utils.cast(int, data.attrib.get('playQueueItemID'))
|
||||
self.playQueueVersion = utils.cast(int, data.attrib.get('playQueueVersion'))
|
||||
self.port = utils.cast(int, data.attrib.get('port'))
|
||||
self.protocol = data.attrib.get('protocol')
|
||||
self.providerIdentifier = data.attrib.get('providerIdentifier')
|
||||
self.ratingKey = utils.cast(int, data.attrib.get('ratingKey'))
|
||||
self.repeat = utils.cast(bool, data.attrib.get('repeat'))
|
||||
self.seekRange = data.attrib.get('seekRange')
|
||||
self.shuffle = utils.cast(bool, data.attrib.get('shuffle'))
|
||||
self.state = data.attrib.get('state')
|
||||
self.subtitleColor = data.attrib.get('subtitleColor')
|
||||
self.subtitlePosition = data.attrib.get('subtitlePosition')
|
||||
self.subtitleSize = utils.cast(int, data.attrib.get('subtitleSize'))
|
||||
self.time = utils.cast(int, data.attrib.get('time'))
|
||||
self.type = data.attrib.get('type')
|
||||
self.volume = utils.cast(int, data.attrib.get('volume'))
|
||||
|
|
Loading…
Reference in a new issue