From 03a202673274d6f9bda8a137504f525d4df86dc6 Mon Sep 17 00:00:00 2001 From: Shaun Date: Sat, 16 Dec 2017 12:55:28 +1100 Subject: [PATCH] add error submission prompt --- addon.xml | 2 +- .../resource.language.en_gb/strings.po | 8 ++ resources/lib/error.py | 121 ++++++++++++++++++ resources/lib/functions.py | 2 + resources/lib/play_utils.py | 3 +- resources/lib/translation.py | 4 +- resources/lib/widgets.py | 2 + service.py | 4 +- 8 files changed, 142 insertions(+), 4 deletions(-) create mode 100644 resources/lib/error.py diff --git a/addon.xml b/addon.xml index bbd00e1..10e42cc 100644 --- a/addon.xml +++ b/addon.xml @@ -1,7 +1,7 @@ diff --git a/resources/language/resource.language.en_gb/strings.po b/resources/language/resource.language.en_gb/strings.po index 00bb807..3de6b31 100644 --- a/resources/language/resource.language.en_gb/strings.po +++ b/resources/language/resource.language.en_gb/strings.po @@ -657,3 +657,11 @@ msgstr "" msgctxt "#30310" msgid "Enable Emby remote control" msgstr "" + +msgctxt "#30311" +msgid "EmbyCon encountered an error!" +msgstr "" + +msgctxt "#30312" +msgid "Do you want to send this error to the devs?" +msgstr "" \ No newline at end of file diff --git a/resources/lib/error.py b/resources/lib/error.py new file mode 100644 index 0000000..bcbba05 --- /dev/null +++ b/resources/lib/error.py @@ -0,0 +1,121 @@ + +import traceback +import sys +import os +import httplib +import json + +import xbmcgui +import xbmc + +from simple_logging import SimpleLogging +from clientinfo import ClientInformation +from translation import i18n + +log = SimpleLogging(__name__) + +def catch_except(errors=(Exception, ), default_value=False): + # Will wrap method with try/except and print parameters for easier debugging + def decorator(func): + def wrapper(*args, **kwargs): + try: + return func(*args, **kwargs) + except errors as error: + if not (hasattr(error, 'quiet') and error.quiet): + return_value = xbmcgui.Dialog().yesno(i18n('error'), i18n('embycon_error'), i18n('embycon_error_submit')) + if return_value: + log.debug("Sending Error Data") + try: + submit_error_data() + except Exception as error: + log.debug("Sending Error Data Failed: " + str(error)) + + return default_value + return wrapper + return decorator + +def submit_error_data(): + + error_type, error_short, error_stack = format_exception() + + data = {} + data["event"] = "ErrorReport" + data["error_stack"] = error_stack + data["error_type"] = error_type + data["error_short"] = error_short + data["sys.argv"] = sys.argv + data["kodi_version"] = xbmc.getInfoLabel("System.BuildVersion") + data["addon_version"] = ClientInformation().getVersion() + + post_data = json.dumps(data) + log.debug("ERROR_DATA: " + post_data) + + server = "allthedata.pythonanywhere.com" + url_path = "/submit" + conn = httplib.HTTPConnection(server, timeout=40) + head = {} + head["Content-Type"] = "application/json" + conn.request(method="POST", url=url_path, body=post_data, headers=head) + data = conn.getresponse() + log.debug("Submit Responce Code: " + str(data.status)) + + +def format_exception(): + + stack = traceback.extract_stack() + exc_type, exc_obj, exc_tb = sys.exc_info() + stack_trace_data = traceback.format_tb(exc_tb) + tb = traceback.extract_tb(exc_tb) + full_tb = stack[:-1] + tb + # log.error(str(full_tb)) + + # get last stack frame + latestStackFrame = None + if (len(tb) > 0): + latestStackFrame = tb[-1] + # log.error(str(tb)) + + fileStackTrace = "" + try: + # get files from stack + stackFileList = [] + for frame in full_tb: + # log.error(str(frame)) + frameFile = (os.path.split(frame[0])[1])[:-3] + frameLine = frame[1] + if (len(stackFileList) == 0 or stackFileList[-1][0] != frameFile): + stackFileList.append([frameFile, [str(frameLine)]]) + else: + file = stackFileList[-1][0] + lines = stackFileList[-1][1] + lines.append(str(frameLine)) + stackFileList[-1] = [file, lines] + # log.error(str(stackFileList)) + + for item in stackFileList: + lines = ",".join(item[1]) + fileStackTrace += item[0] + "," + lines + ":" + # log.error(str(fileStackTrace)) + except Exception as e: + fileStackTrace = None + log.error(e) + + errorType = "NA" + errorFile = "NA" + + if latestStackFrame is not None: + if fileStackTrace is None: + fileStackTrace = os.path.split(latestStackFrame[0])[1] + ":" + str(latestStackFrame[1]) + + codeLine = "NA" + if (len(latestStackFrame) > 3 and latestStackFrame[3] != None): + codeLine = latestStackFrame[3].strip() + + errorFile = "%s(%s)(%s)" % (fileStackTrace, exc_obj.message, codeLine) + errorFile = errorFile[0:499] + errorType = "%s" % (exc_type.__name__) + # log.error(errorType + " - " + errorFile) + + del (exc_type, exc_obj, exc_tb) + + return errorType, errorFile, stack_trace_data diff --git a/resources/lib/functions.py b/resources/lib/functions.py index cd1e476..8345506 100644 --- a/resources/lib/functions.py +++ b/resources/lib/functions.py @@ -15,6 +15,7 @@ import xbmcgui import xbmcaddon import xbmc +from resources.lib.error import catch_except from downloadutils import DownloadUtils from utils import getDetailsString, getArt, cache_artwork from kodi_utils import HomeWindow @@ -43,6 +44,7 @@ downloadUtils = DownloadUtils() dataManager = DataManager() +@catch_except() def mainEntryPoint(): log.debug("===== EmbyCon START =====") diff --git a/resources/lib/play_utils.py b/resources/lib/play_utils.py index 5d21cae..68ff39f 100644 --- a/resources/lib/play_utils.py +++ b/resources/lib/play_utils.py @@ -9,6 +9,7 @@ import time import json import hashlib +from resources.lib.error import catch_except from simple_logging import SimpleLogging from downloadutils import DownloadUtils from resume_dialog import ResumeDialog @@ -20,7 +21,7 @@ from json_rpc import json_rpc log = SimpleLogging(__name__) downloadUtils = DownloadUtils() - +@catch_except() def playFile(play_info): id = play_info.get("item_id") diff --git a/resources/lib/translation.py b/resources/lib/translation.py index 9c2fe72..e9c81cb 100644 --- a/resources/lib/translation.py +++ b/resources/lib/translation.py @@ -117,5 +117,7 @@ STRINGS = { 'playback_starting_': 30306, 'play_trailer': 30307, 'select_trailer': 30308, - 'select_source': 30309 + 'select_source': 30309, + 'embycon_error': 30311, + 'embycon_error_submit': 30312 } diff --git a/resources/lib/widgets.py b/resources/lib/widgets.py index c3098d4..fee82e1 100644 --- a/resources/lib/widgets.py +++ b/resources/lib/widgets.py @@ -12,12 +12,14 @@ from utils import getArt from datamanager import DataManager from simple_logging import SimpleLogging from kodi_utils import HomeWindow +from resources.lib.error import catch_except log = SimpleLogging(__name__) downloadUtils = DownloadUtils() dataManager = DataManager() kodi_version = int(xbmc.getInfoLabel('System.BuildVersion')[:2]) +@catch_except() def checkForNewContent(): log.debug("checkForNewContent Called") diff --git a/service.py b/service.py index 6145206..3d0a061 100644 --- a/service.py +++ b/service.py @@ -8,6 +8,7 @@ import time import json import traceback +from resources.lib.error import catch_except from resources.lib.downloadutils import DownloadUtils from resources.lib.simple_logging import SimpleLogging from resources.lib.play_utils import playFile @@ -77,6 +78,7 @@ def sendProgress(): url = "{server}/emby/Sessions/Playing/Progress" download_utils.downloadUrl(url, postBody=postdata, method="POST") +@catch_except() def promptForStopActions(item_id, current_possition): settings = xbmcaddon.Addon(id='plugin.video.embycon') @@ -181,7 +183,7 @@ def promptForStopActions(item_id, current_possition): break - +@catch_except() def stopAll(played_information): if len(played_information) == 0: return