Improve error message when playback failes

This commit is contained in:
croneter 2019-02-06 16:14:14 +01:00
parent 63201db07d
commit c7eab63960
4 changed files with 135 additions and 78 deletions

View file

@ -1138,9 +1138,9 @@ msgctxt "#39211"
msgid "Watch later" msgid "Watch later"
msgstr "" msgstr ""
# String attached at the end to get something like "PMS Name is offline" # Error message pop-up if {0} cannot be contacted. {0} will be replaced by e.g. the PMS' name
msgctxt "#39213" msgctxt "#39213"
msgid "is offline" msgid "{0} offline"
msgstr "" msgstr ""
msgctxt "#39215" msgctxt "#39215"

View file

@ -167,6 +167,7 @@ class DownloadUtils():
kwargs['timeout'] = timeout kwargs['timeout'] = timeout
# ACTUAL DOWNLOAD HAPPENING HERE # ACTUAL DOWNLOAD HAPPENING HERE
success = False
try: try:
r = self._doDownload(s, action_type, **kwargs) r = self._doDownload(s, action_type, **kwargs)
@ -176,44 +177,37 @@ class DownloadUtils():
LOG.warn(e) LOG.warn(e)
if reraise: if reraise:
raise raise
except exceptions.ConnectionError as e: except exceptions.ConnectionError as e:
# Connection error # Connection error
LOG.warn("Server unreachable at: %s", url) LOG.warn("Server unreachable at: %s", url)
LOG.warn(e) LOG.warn(e)
if reraise: if reraise:
raise raise
except exceptions.Timeout as e: except exceptions.Timeout as e:
LOG.warn("Server timeout at: %s", url) LOG.warn("Server timeout at: %s", url)
LOG.warn(e) LOG.warn(e)
if reraise: if reraise:
raise raise
except exceptions.HTTPError as e: except exceptions.HTTPError as e:
LOG.warn('HTTP Error at %s', url) LOG.warn('HTTP Error at %s', url)
LOG.warn(e) LOG.warn(e)
if reraise: if reraise:
raise raise
except exceptions.TooManyRedirects as e: except exceptions.TooManyRedirects as e:
LOG.warn("Too many redirects connecting to: %s", url) LOG.warn("Too many redirects connecting to: %s", url)
LOG.warn(e) LOG.warn(e)
if reraise: if reraise:
raise raise
except exceptions.RequestException as e: except exceptions.RequestException as e:
LOG.warn("Unknown error connecting to: %s", url) LOG.warn("Unknown error connecting to: %s", url)
LOG.warn(e) LOG.warn(e)
if reraise: if reraise:
raise raise
except SystemExit: except SystemExit:
LOG.info('SystemExit detected, aborting download') LOG.info('SystemExit detected, aborting download')
self.stopSession() self.stopSession()
if reraise: if reraise:
raise raise
except Exception: except Exception:
LOG.warn('Unknown error while downloading. Traceback:') LOG.warn('Unknown error while downloading. Traceback:')
import traceback import traceback
@ -223,6 +217,7 @@ class DownloadUtils():
# THE RESPONSE ##### # THE RESPONSE #####
else: else:
success = True
# We COULD contact the PMS, hence it ain't dead # We COULD contact the PMS, hence it ain't dead
if authenticate is True: if authenticate is True:
self.count_error = 0 self.count_error = 0
@ -300,12 +295,12 @@ class DownloadUtils():
url, r.status_code) url, r.status_code)
return True return True
# And now deal with the consequences of the exceptions finally:
if authenticate is True: if not success and authenticate:
# Make the addon aware of status # Deal with the consequences of the exceptions
self.count_error += 1 # Make the addon aware of status
if self.count_error >= self.connection_attempts: self.count_error += 1
LOG.warn('Failed to connect to %s too many times. ' if self.count_error >= self.connection_attempts:
'Declare PMS dead', url) LOG.warn('Failed to connect to %s too many times. '
app.CONN.online = False 'Declare PMS dead', url)
return app.CONN.online = False

View file

@ -11,7 +11,6 @@ from .plex_api import API
from .plex_db import PlexDB from .plex_db import PlexDB
from . import plex_functions as PF from . import plex_functions as PF
from . import utils from . import utils
from .downloadutils import DownloadUtils as DU
from .kodi_db import KodiVideoDB from .kodi_db import KodiVideoDB
from . import playlist_func as PL from . import playlist_func as PL
from . import playqueue as PQ from . import playqueue as PQ
@ -50,10 +49,18 @@ def playback_triage(plex_id=None, plex_type=None, path=None, resolve=True):
global RESOLVE global RESOLVE
# If started via Kodi context menu, we never resolve # If started via Kodi context menu, we never resolve
RESOLVE = resolve if not app.PLAYSTATE.context_menu_play else False RESOLVE = resolve if not app.PLAYSTATE.context_menu_play else False
if not app.ACCOUNT.authenticated: if not app.CONN.online or not app.ACCOUNT.authenticated:
LOG.error('Not yet authenticated for PMS, abort starting playback') if not app.CONN.online:
# "Unauthorized for PMS" LOG.error('PMS not online for playback')
utils.dialog('notification', utils.lang(29999), utils.lang(30017)) # "{0} offline"
utils.dialog('notification',
utils.lang(29999),
utils.lang(39213).format(app.CONN.server_name),
icon='{plex}')
else:
LOG.error('Not yet authenticated for PMS, abort starting playback')
# "Unauthorized for PMS"
utils.dialog('notification', utils.lang(29999), utils.lang(30017))
_ensure_resolve(abort=True) _ensure_resolve(abort=True)
return return
with app.APP.lock_playqueues: with app.APP.lock_playqueues:
@ -135,16 +142,8 @@ def _playlist_playback(plex_id, plex_type):
for the next item in line :-) for the next item in line :-)
(by the way: trying to get active Kodi player id will return []) (by the way: trying to get active Kodi player id will return [])
""" """
xml = PF.GetPlexMetadata(plex_id) xml = PF.GetPlexMetadata(plex_id, reraise=True)
try: if xml in (None, 401):
xml[0].attrib
except (IndexError, TypeError, AttributeError):
LOG.error('Could not get a PMS xml for plex id %s', plex_id)
# "Play error"
utils.dialog('notification',
utils.lang(29999),
utils.lang(30128),
icon='{error}')
_ensure_resolve(abort=True) _ensure_resolve(abort=True)
return return
# Kodi bug: playqueue will ALWAYS be audio playqueue UNTIL playback # Kodi bug: playqueue will ALWAYS be audio playqueue UNTIL playback
@ -164,16 +163,9 @@ def _playback_init(plex_id, plex_type, playqueue, pos):
Playback setup if Kodi starts playing an item for the first time. Playback setup if Kodi starts playing an item for the first time.
""" """
LOG.info('Initializing PKC playback') LOG.info('Initializing PKC playback')
xml = PF.GetPlexMetadata(plex_id) xml = PF.GetPlexMetadata(plex_id, reraise=True)
try: if xml in (None, 401):
xml[0].attrib
except (IndexError, TypeError, AttributeError):
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"
utils.dialog('notification',
utils.lang(29999),
utils.lang(30128),
icon='{error}')
_ensure_resolve(abort=True) _ensure_resolve(abort=True)
return return
if playqueue.kodi_pl.size() > 1: if playqueue.kodi_pl.size() > 1:
@ -182,6 +174,11 @@ def _playback_init(plex_id, plex_type, playqueue, pos):
_init_existing_kodi_playlist(playqueue, pos) _init_existing_kodi_playlist(playqueue, pos)
except PL.PlaylistError: except PL.PlaylistError:
LOG.error('Playback_init for existing Kodi playlist failed') LOG.error('Playback_init for existing Kodi playlist failed')
# "Play error"
utils.dialog('notification',
utils.lang(29999),
utils.lang(30128),
icon='{error}')
_ensure_resolve(abort=True) _ensure_resolve(abort=True)
return return
# Now we need to use setResolvedUrl for the item at position ZERO # Now we need to use setResolvedUrl for the item at position ZERO
@ -259,12 +256,10 @@ def _ensure_resolve(abort=False):
will be destroyed. will be destroyed.
""" """
if RESOLVE: if RESOLVE:
if not abort: # Releases the other Python thread without a ListItem
# Releases the other Python thread without a ListItem transfer.send(True)
transfer.send(True) # Shows PKC error message
else: # transfer.send(None)
# Shows PKC error message
transfer.send(None)
if abort: if abort:
# Reset some playback variables # Reset some playback variables
app.PLAYSTATE.context_menu_play = False app.PLAYSTATE.context_menu_play = False
@ -418,7 +413,10 @@ def _conclude_playback(playqueue, pos):
LOG.info('Resuming playback at %s', item.offset) LOG.info('Resuming playback at %s', item.offset)
if v.KODIVERSION >= 18 and api: if v.KODIVERSION >= 18 and api:
# Kodi 18 Alpha 3 broke StartOffset # Kodi 18 Alpha 3 broke StartOffset
percent = item.offset / api.runtime() * 100.0 try:
percent = item.offset / api.runtime() * 100.0
except ZeroDivisionError:
percent = 0.0
LOG.debug('Resuming at %s percent', percent) LOG.debug('Resuming at %s percent', percent)
listitem.setProperty('StartPercent', str(percent)) listitem.setProperty('StartPercent', str(percent))
else: else:
@ -446,21 +444,20 @@ def process_indirect(key, offset, resolve=True):
key, offset, resolve) key, offset, resolve)
global RESOLVE global RESOLVE
RESOLVE = resolve RESOLVE = resolve
offset = int(v.PLEX_TO_KODI_TIMEFACTOR * float(offset)) if offset != '0' else None
if key.startswith('http') or key.startswith('{server}'): if key.startswith('http') or key.startswith('{server}'):
xml = DU().downloadUrl(key) xml = PF.get_playback_xml(key, app.CONN.server_name)
elif key.startswith('/system/services'): elif key.startswith('/system/services'):
xml = DU().downloadUrl('http://node.plexapp.com:32400%s' % key) xml = PF.get_playback_xml('http://node.plexapp.com:32400%s' % key,
'plexapp.com',
authenticate=False,
token=app.ACCOUNT.plex_token)
else: else:
xml = DU().downloadUrl('{server}%s' % key) xml = PF.get_playback_xml('{server}%s' % key, app.CONN.server_name)
try: if xml is None:
xml[0].attrib
except (TypeError, IndexError, AttributeError):
LOG.error('Could not download PMS metadata')
_ensure_resolve(abort=True) _ensure_resolve(abort=True)
return return
if offset != '0':
offset = int(v.PLEX_TO_KODI_TIMEFACTOR * float(offset))
# Todo: implement offset
api = API(xml[0]) api = API(xml[0])
listitem = transfer.PKCListItem() listitem = transfer.PKCListItem()
api.create_listitem(listitem) api.create_listitem(listitem)
@ -469,19 +466,31 @@ def process_indirect(key, offset, resolve=True):
playqueue.clear() playqueue.clear()
item = PL.Playlist_Item() item = PL.Playlist_Item()
item.xml = xml[0] item.xml = xml[0]
item.offset = int(offset) item.offset = offset
item.plex_type = v.PLEX_TYPE_CLIP item.plex_type = v.PLEX_TYPE_CLIP
item.playmethod = 'DirectStream' item.playmethod = 'DirectStream'
# Need to get yet another xml to get the final playback url # Need to get yet another xml to get the final playback url
xml = DU().downloadUrl('http://node.plexapp.com:32400%s'
% xml[0][0][0].attrib['key'])
try: try:
xml[0].attrib xml = PF.get_playback_xml('http://node.plexapp.com:32400%s'
% xml[0][0][0].attrib['key'],
'plexapp.com',
authenticate=False,
token=app.ACCOUNT.plex_token)
except (TypeError, IndexError, AttributeError): except (TypeError, IndexError, AttributeError):
LOG.error('Could not download last xml for playurl') LOG.error('XML malformed: %s', xml.attrib)
xml = None
if xml is None:
_ensure_resolve(abort=True) _ensure_resolve(abort=True)
return return
playurl = xml[0].attrib['key']
try:
playurl = xml[0].attrib['key']
except (TypeError, IndexError, AttributeError):
LOG.error('Last xml malformed: %s', xml.attrib)
_ensure_resolve(abort=True)
return
item.file = playurl item.file = playurl
listitem.setPath(utils.try_encode(playurl)) listitem.setPath(utils.try_encode(playurl))
playqueue.items.append(item) playqueue.items.append(item)

View file

@ -9,7 +9,7 @@ from copy import deepcopy
from time import time from time import time
from threading import Thread from threading import Thread
from .downloadutils import DownloadUtils as DU from .downloadutils import DownloadUtils as DU, exceptions
from . import backgroundthread, utils, plex_tv, variables as v, app from . import backgroundthread, utils, plex_tv, variables as v, app
############################################################################### ###############################################################################
@ -454,7 +454,7 @@ def _poke_pms(pms, queue):
url, pms['uuid'], xml.get('machineIdentifier')) url, pms['uuid'], xml.get('machineIdentifier'))
def GetPlexMetadata(key): def GetPlexMetadata(key, reraise=False):
""" """
Returns raw API metadata for key as an etree XML. Returns raw API metadata for key as an etree XML.
@ -481,18 +481,71 @@ def GetPlexMetadata(key):
# 'includeConcerts': 1 # 'includeConcerts': 1
} }
url = url + '?' + urlencode(arguments) url = url + '?' + urlencode(arguments)
xml = DU().downloadUrl(url)
if xml == 401:
# Either unauthorized (taken care of by doUtils) or PMS under strain
return 401
# Did we receive a valid XML?
try: try:
xml.attrib xml = DU().downloadUrl(url, reraise=reraise)
# Nope we did not receive a valid XML except exceptions.RequestException:
except AttributeError: # "PMS offline"
LOG.error("Error retrieving metadata for %s", url) utils.dialog('notification',
xml = None utils.lang(29999),
return xml utils.lang(39213).format(app.CONN.server_name),
icon='{plex}')
except Exception:
# "Error"
utils.dialog('notification',
utils.lang(29999),
utils.lang(30135),
icon='{error}')
else:
if xml == 401:
# Either unauthorized (taken care of by doUtils) or PMS under strain
return 401
# Did we receive a valid XML?
try:
xml[0].attrib
# Nope we did not receive a valid XML
except (TypeError, IndexError, AttributeError):
LOG.error("Error retrieving metadata for %s", url)
xml = None
return xml
def get_playback_xml(url, server_name, authenticate=True, token=None):
"""
Returns None if something went wrong
"""
header_options = {'X-Plex-Token': token} if not authenticate else None
try:
xml = DU().downloadUrl(url,
authenticate=authenticate,
headerOptions=header_options,
reraise=True)
except exceptions.RequestException:
# "{0} offline"
utils.dialog('notification',
utils.lang(29999),
utils.lang(39213).format(server_name),
icon='{plex}')
except Exception as e:
LOG.error(e)
import traceback
LOG.error("Traceback:\n%s", traceback.format_exc())
# "Play error"
utils.dialog('notification',
utils.lang(29999),
utils.lang(30128),
icon='{error}')
else:
try:
xml[0].attrib
except (TypeError, IndexError, AttributeError):
LOG.error('Could not get a valid xml, unfortunately')
# "Play error"
utils.dialog('notification',
utils.lang(29999),
utils.lang(30128),
icon='{error}')
else:
return xml
def GetAllPlexChildren(key): def GetAllPlexChildren(key):