Improve playback startup resiliance

- Fixes #426
This commit is contained in:
croneter 2018-03-10 12:24:57 +01:00
parent fe01405d3e
commit 2cd00f21b7

View file

@ -54,7 +54,7 @@ def playback_triage(plex_id=None, plex_type=None, path=None, resolve=True):
LOG.error('Not yet authenticated for PMS, abort starting playback') LOG.error('Not yet authenticated for PMS, abort starting playback')
# "Unauthorized for PMS" # "Unauthorized for PMS"
dialog('notification', lang(29999), lang(30017)) dialog('notification', lang(29999), lang(30017))
_ensure_resolve() _ensure_resolve(abort=True)
return return
playqueue = PQ.get_playqueue_from_type( playqueue = PQ.get_playqueue_from_type(
v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[plex_type]) v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[plex_type])
@ -64,8 +64,12 @@ def playback_triage(plex_id=None, plex_type=None, path=None, resolve=True):
LOG.debug('playQueue position: %s for %s', pos, playqueue) LOG.debug('playQueue position: %s for %s', pos, playqueue)
# Have we already initiated playback? # Have we already initiated playback?
try: try:
playqueue.items[pos] item = playqueue.items[pos]
except IndexError: except IndexError:
initiate = True
else:
initiate = True if item.plex_id != plex_id else False
if initiate:
_playback_init(plex_id, plex_type, playqueue, pos) _playback_init(plex_id, plex_type, playqueue, pos)
else: else:
# kick off playback on second pass # kick off playback on second pass
@ -84,7 +88,7 @@ def _playback_init(plex_id, plex_type, playqueue, pos):
LOG.error('Could not get a PMS xml for plex id %s', plex_id) LOG.error('Could not get a PMS xml for plex id %s', plex_id)
# "Play error" # "Play error"
dialog('notification', lang(29999), lang(30128), icon='{error}') dialog('notification', lang(29999), lang(30128), icon='{error}')
_ensure_resolve() _ensure_resolve(abort=True)
return return
if playqueue.kodi_pl.size() > 1: if playqueue.kodi_pl.size() > 1:
# Special case - we already got a filled Kodi playqueue # Special case - we already got a filled Kodi playqueue
@ -92,7 +96,7 @@ def _playback_init(plex_id, plex_type, playqueue, pos):
_init_existing_kodi_playlist(playqueue) _init_existing_kodi_playlist(playqueue)
except PL.PlaylistError: except PL.PlaylistError:
LOG.error('Aborting playback_init for longer Kodi playlist') LOG.error('Aborting playback_init for longer Kodi playlist')
_ensure_resolve() _ensure_resolve(abort=True)
return return
# Now we need to use setResolvedUrl for the item at position pos # Now we need to use setResolvedUrl for the item at position pos
_conclude_playback(playqueue, pos) _conclude_playback(playqueue, pos)
@ -101,9 +105,8 @@ def _playback_init(plex_id, plex_type, playqueue, pos):
# playqueues # playqueues
# Fail the item we're trying to play now so we can restart the player # Fail the item we're trying to play now so we can restart the player
_ensure_resolve() _ensure_resolve()
api = API(xml[0])
trailers = False trailers = False
if (plex_type == v.PLEX_TYPE_MOVIE and not api.resume_point() and if (plex_type == v.PLEX_TYPE_MOVIE and not state.RESUME_PLAYBACK and
settings('enableCinema') == "true"): settings('enableCinema') == "true"):
if settings('askCinema') == "true": if settings('askCinema') == "true":
# "Play trailers?" # "Play trailers?"
@ -124,7 +127,7 @@ def _playback_init(plex_id, plex_type, playqueue, pos):
plex_id, xml.attrib.get('librarySectionUUID')) plex_id, xml.attrib.get('librarySectionUUID'))
# "Play error" # "Play error"
dialog('notification', lang(29999), lang(30128), icon='{error}') dialog('notification', lang(29999), lang(30128), icon='{error}')
_ensure_resolve() _ensure_resolve(abort=True)
return return
# Should already be empty, but just in case # Should already be empty, but just in case
PL.get_playlist_details_from_xml(playqueue, xml) PL.get_playlist_details_from_xml(playqueue, xml)
@ -135,11 +138,13 @@ def _playback_init(plex_id, plex_type, playqueue, pos):
# Reset some playback variables # Reset some playback variables
state.CONTEXT_MENU_PLAY = False state.CONTEXT_MENU_PLAY = False
state.FORCE_TRANSCODE = False state.FORCE_TRANSCODE = False
# Do NOT set offset, because Kodi player will return here to resolveURL
# New thread to release this one sooner (e.g. harddisk spinning up) # New thread to release this one sooner (e.g. harddisk spinning up)
thread = Thread(target=Player().play, thread = Thread(target=threaded_playback,
args=(playqueue.kodi_pl, )) args=(playqueue.kodi_pl, pos, None))
thread.setDaemon(True) thread.setDaemon(True)
LOG.info('Done initializing PKC playback, starting Kodi player') LOG.info('Done initializing playback, starting Kodi player at pos %s',
pos)
# By design, PKC will start Kodi playback using Player().play(). Kodi # By design, PKC will start Kodi playback using Player().play(). Kodi
# caches paths like our plugin://pkc. If we use Player().play() between # caches paths like our plugin://pkc. If we use Player().play() between
# 2 consecutive startups of exactly the same Kodi library item, Kodi's # 2 consecutive startups of exactly the same Kodi library item, Kodi's
@ -149,7 +154,7 @@ def _playback_init(plex_id, plex_type, playqueue, pos):
thread.start() thread.start()
def _ensure_resolve(): def _ensure_resolve(abort=False):
""" """
Will check whether RESOLVE=True and if so, fail Kodi playback startup Will check whether RESOLVE=True and if so, fail Kodi playback startup
with the path 'PKC_Dummy_Path_Which_Fails' using setResolvedUrl (and some with the path 'PKC_Dummy_Path_Which_Fails' using setResolvedUrl (and some
@ -158,11 +163,17 @@ def _ensure_resolve():
This way we're making sure that other Python instances (calling default.py) This way we're making sure that other Python instances (calling default.py)
will be destroyed. will be destroyed.
""" """
if RESOLVE is True: if RESOLVE:
state.PKC_CAUSED_STOP = True state.PKC_CAUSED_STOP = True
result = Playback_Successful() result = Playback_Successful()
result.listitem = PKC_ListItem(path='PKC_Dummy_Path_Which_Fails') result.listitem = PKC_ListItem(path='PKC_Dummy_Path_Which_Fails')
pickle_me(result) pickle_me(result)
if abort:
# Reset some playback variables
state.CONTEXT_MENU_PLAY = False
state.FORCE_TRANSCODE = False
state.RESUMABLE = False
state.RESUME_PLAYBACK = False
def _init_existing_kodi_playlist(playqueue): def _init_existing_kodi_playlist(playqueue):
@ -291,6 +302,7 @@ def _conclude_playback(playqueue, pos):
listitem.setSubtitles(api.cache_external_subs()) listitem.setSubtitles(api.cache_external_subs())
elif item.playmethod == 'Transcode': elif item.playmethod == 'Transcode':
playutils.audio_subtitle_prefs(listitem) playutils.audio_subtitle_prefs(listitem)
if state.RESUME_PLAYBACK is True: if state.RESUME_PLAYBACK is True:
state.RESUME_PLAYBACK = False state.RESUME_PLAYBACK = False
if (item.offset is None and if (item.offset is None and
@ -335,7 +347,7 @@ def process_indirect(key, offset, resolve=True):
xml[0].attrib xml[0].attrib
except (TypeError, IndexError, AttributeError): except (TypeError, IndexError, AttributeError):
LOG.error('Could not download PMS metadata') LOG.error('Could not download PMS metadata')
_ensure_resolve() _ensure_resolve(abort=True)
return return
if offset != '0': if offset != '0':
offset = int(v.PLEX_TO_KODI_TIMEFACTOR * float(offset)) offset = int(v.PLEX_TO_KODI_TIMEFACTOR * float(offset))
@ -358,7 +370,7 @@ def process_indirect(key, offset, resolve=True):
xml[0].attrib xml[0].attrib
except (TypeError, IndexError, AttributeError): except (TypeError, IndexError, AttributeError):
LOG.error('Could not download last xml for playurl') LOG.error('Could not download last xml for playurl')
_ensure_resolve() _ensure_resolve(abort=True)
return return
playurl = xml[0].attrib['key'] playurl = xml[0].attrib['key']
item.file = playurl item.file = playurl