Merge pull request #708 from croneter/improve-playerror-messages
Improve error messages when playback failes
This commit is contained in:
commit
05a6700d55
4 changed files with 135 additions and 78 deletions
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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):
|
||||||
|
|
Loading…
Reference in a new issue