2016-10-09 22:05:51 +00:00
|
|
|
import sys
|
|
|
|
import os
|
2016-10-10 02:02:23 +00:00
|
|
|
import traceback
|
2016-10-09 09:14:45 +00:00
|
|
|
import requests
|
|
|
|
import logging
|
|
|
|
import clientinfo
|
2016-11-03 05:46:22 +00:00
|
|
|
import hashlib
|
2016-10-15 02:56:05 +00:00
|
|
|
import xbmc
|
2016-11-01 09:48:24 +00:00
|
|
|
import time
|
2016-10-28 05:02:47 +00:00
|
|
|
from utils import window, settings, language as lang
|
2016-10-09 09:14:45 +00:00
|
|
|
|
|
|
|
log = logging.getLogger("EMBY."+__name__)
|
|
|
|
|
2016-10-13 00:47:43 +00:00
|
|
|
# for info on the metrics that can be sent to Google Analytics
|
|
|
|
# https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters#events
|
|
|
|
|
2016-11-01 09:48:24 +00:00
|
|
|
logEventHistory = {}
|
|
|
|
|
2016-11-02 04:11:04 +00:00
|
|
|
# wrap a function to catch, log and then re throw an exception
|
|
|
|
def log_error(errors=(Exception, )):
|
|
|
|
def decorator(func):
|
|
|
|
def wrapper(*args, **kwargs):
|
|
|
|
try:
|
|
|
|
return func(*args, **kwargs)
|
|
|
|
except errors as error:
|
2016-11-22 12:05:16 +00:00
|
|
|
if not (hasattr(error, 'quiet') and error.quiet):
|
|
|
|
ga = GoogleAnalytics()
|
|
|
|
errStrings = ga.formatException()
|
|
|
|
ga.sendEventData("Exception", errStrings[0], errStrings[1], True)
|
2016-11-02 04:11:04 +00:00
|
|
|
log.exception(error)
|
|
|
|
log.error("log_error: %s \n args: %s \n kwargs: %s",
|
|
|
|
func.__name__, args, kwargs)
|
|
|
|
raise
|
|
|
|
return wrapper
|
|
|
|
return decorator
|
|
|
|
|
|
|
|
# main GA class
|
2016-10-09 09:14:45 +00:00
|
|
|
class GoogleAnalytics():
|
|
|
|
|
2016-11-12 08:12:16 +00:00
|
|
|
testing = False
|
2016-10-09 09:14:45 +00:00
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
|
|
|
client_info = clientinfo.ClientInfo()
|
|
|
|
self.version = client_info.get_version()
|
2016-10-17 03:21:40 +00:00
|
|
|
self.device_id = client_info.get_device_id()
|
|
|
|
|
|
|
|
# user agent string, used for OS and Kodi version identification
|
|
|
|
kodi_ver = xbmc.getInfoLabel("System.BuildVersion")
|
|
|
|
if(not kodi_ver):
|
|
|
|
kodi_ver = "na"
|
|
|
|
kodi_ver = kodi_ver.strip()
|
|
|
|
if(kodi_ver.find(" ") > 0):
|
|
|
|
kodi_ver = kodi_ver[0:kodi_ver.find(" ")]
|
|
|
|
self.userAgent = "Kodi/" + kodi_ver + " (" + self.getUserAgentOS() + ")"
|
2016-10-09 09:14:45 +00:00
|
|
|
|
|
|
|
# Use set user name
|
|
|
|
self.user_name = settings('username') or settings('connectUsername') or 'None'
|
|
|
|
|
|
|
|
# use md5 for client and user for analytics
|
2016-11-03 05:46:22 +00:00
|
|
|
self.device_id = hashlib.md5(self.device_id).hexdigest()
|
|
|
|
self.user_name = hashlib.md5(self.user_name).hexdigest()
|
2016-10-14 03:35:34 +00:00
|
|
|
|
|
|
|
# resolution
|
2016-10-16 00:12:57 +00:00
|
|
|
self.screen_mode = xbmc.getInfoLabel("System.ScreenMode")
|
|
|
|
self.screen_height = xbmc.getInfoLabel("System.ScreenHeight")
|
|
|
|
self.screen_width = xbmc.getInfoLabel("System.ScreenWidth")
|
|
|
|
|
|
|
|
self.lang = xbmc.getInfoLabel("System.Language")
|
2016-10-09 09:14:45 +00:00
|
|
|
|
2016-10-15 02:56:05 +00:00
|
|
|
def getUserAgentOS(self):
|
|
|
|
|
|
|
|
if xbmc.getCondVisibility('system.platform.osx'):
|
2016-10-18 07:30:04 +00:00
|
|
|
return "Mac OS X"
|
2016-10-15 02:56:05 +00:00
|
|
|
elif xbmc.getCondVisibility('system.platform.ios'):
|
|
|
|
return "iOS"
|
|
|
|
elif xbmc.getCondVisibility('system.platform.windows'):
|
2016-10-18 07:30:04 +00:00
|
|
|
return "Windows NT"
|
2016-10-15 02:56:05 +00:00
|
|
|
elif xbmc.getCondVisibility('system.platform.android'):
|
|
|
|
return "Android"
|
|
|
|
elif xbmc.getCondVisibility('system.platform.linux.raspberrypi'):
|
2016-10-20 23:31:13 +00:00
|
|
|
return "Linux Rpi"
|
2016-10-15 02:56:05 +00:00
|
|
|
elif xbmc.getCondVisibility('system.platform.linux'):
|
|
|
|
return "Linux"
|
|
|
|
else:
|
2016-10-15 23:22:58 +00:00
|
|
|
return "Other"
|
2016-10-15 02:56:05 +00:00
|
|
|
|
2016-10-09 22:05:51 +00:00
|
|
|
def formatException(self):
|
2016-11-12 00:45:24 +00:00
|
|
|
|
|
|
|
stack = traceback.extract_stack()
|
2016-10-09 22:05:51 +00:00
|
|
|
exc_type, exc_obj, exc_tb = sys.exc_info()
|
2016-11-12 00:45:24 +00:00
|
|
|
tb = traceback.extract_tb(exc_tb)
|
|
|
|
full_tb = stack[:-1] + tb
|
|
|
|
#log.error(str(full_tb))
|
|
|
|
|
|
|
|
# get last stack frame
|
2016-10-18 00:50:59 +00:00
|
|
|
latestStackFrame = None
|
2016-11-12 00:45:24 +00:00
|
|
|
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)
|
|
|
|
|
2016-10-10 02:02:23 +00:00
|
|
|
errorType = "NA"
|
|
|
|
errorFile = "NA"
|
2016-11-12 00:45:24 +00:00
|
|
|
|
|
|
|
if latestStackFrame is not None:
|
|
|
|
if fileStackTrace is None:
|
|
|
|
fileStackTrace = os.path.split(latestStackFrame[0])[1] + ":" + str(latestStackFrame[1])
|
2016-10-18 00:50:59 +00:00
|
|
|
|
|
|
|
codeLine = "NA"
|
|
|
|
if(len(latestStackFrame) > 3 and latestStackFrame[3] != None):
|
|
|
|
codeLine = latestStackFrame[3].strip()
|
2016-10-10 02:02:23 +00:00
|
|
|
|
2016-11-12 00:45:24 +00:00
|
|
|
errorFile = "%s(%s)(%s)" % (fileStackTrace, exc_obj.message, codeLine)
|
2016-10-13 04:01:09 +00:00
|
|
|
errorFile = errorFile[0:499]
|
2016-10-10 02:02:23 +00:00
|
|
|
errorType = "%s" % (exc_type.__name__)
|
2016-11-05 23:38:33 +00:00
|
|
|
#log.error(errorType + " - " + errorFile)
|
2016-11-12 00:45:24 +00:00
|
|
|
|
2016-10-18 00:50:59 +00:00
|
|
|
del(exc_type, exc_obj, exc_tb)
|
|
|
|
|
2016-10-09 22:50:30 +00:00
|
|
|
return errorType, errorFile
|
2016-11-12 00:45:24 +00:00
|
|
|
|
2016-10-24 03:37:35 +00:00
|
|
|
def getBaseData(self):
|
|
|
|
|
2016-10-09 09:14:45 +00:00
|
|
|
# all the data we can send to Google Analytics
|
|
|
|
data = {}
|
|
|
|
data['v'] = '1'
|
|
|
|
data['tid'] = 'UA-85356267-1' # tracking id, this is the account ID
|
|
|
|
|
|
|
|
data['ds'] = 'plugin' # data source
|
|
|
|
|
|
|
|
data['an'] = 'Kodi4Emby' # App Name
|
|
|
|
data['aid'] = '1' # App ID
|
|
|
|
data['av'] = self.version # App Version
|
|
|
|
#data['aiid'] = '1.1' # App installer ID
|
|
|
|
|
|
|
|
data['cid'] = self.device_id # Client ID
|
2016-10-12 05:16:05 +00:00
|
|
|
#data['uid'] = self.user_name # User ID
|
2016-10-09 09:14:45 +00:00
|
|
|
|
2016-10-15 02:56:05 +00:00
|
|
|
data['ua'] = self.userAgent # user agent string
|
2016-10-09 09:14:45 +00:00
|
|
|
|
2016-10-16 00:12:57 +00:00
|
|
|
# add width and height, only add if full screen
|
|
|
|
if(self.screen_mode.lower().find("window") == -1):
|
|
|
|
data['sr'] = str(self.screen_width) + "x" + str(self.screen_height)
|
|
|
|
|
|
|
|
data["ul"] = self.lang
|
2016-10-24 03:37:35 +00:00
|
|
|
|
|
|
|
return data
|
|
|
|
|
|
|
|
def sendScreenView(self, name):
|
|
|
|
|
|
|
|
data = self.getBaseData()
|
|
|
|
data['t'] = 'screenview' # action type
|
|
|
|
data['cd'] = name
|
|
|
|
|
|
|
|
self.sendData(data)
|
|
|
|
|
2016-11-01 09:48:24 +00:00
|
|
|
def sendEventData(self, eventCategory, eventAction, eventLabel=None, throttle=False):
|
|
|
|
|
2016-11-01 20:08:33 +00:00
|
|
|
# if throttling is enabled then only log the same event every 5 min
|
2016-11-01 09:48:24 +00:00
|
|
|
if(throttle):
|
|
|
|
throttleKey = eventCategory + "-" + eventAction + "-" + str(eventLabel)
|
|
|
|
lastLogged = logEventHistory.get(throttleKey)
|
|
|
|
if(lastLogged != None):
|
|
|
|
timeSinceLastLog = time.time() - lastLogged
|
2016-11-01 20:08:33 +00:00
|
|
|
if(timeSinceLastLog < 300):
|
2016-11-05 23:38:33 +00:00
|
|
|
#log.info("SKIPPING_LOG_EVENT : " + str(timeSinceLastLog) + " " + throttleKey)
|
2016-11-01 09:48:24 +00:00
|
|
|
return
|
|
|
|
logEventHistory[throttleKey] = time.time()
|
2016-10-24 03:37:35 +00:00
|
|
|
|
|
|
|
data = self.getBaseData()
|
|
|
|
data['t'] = 'event' # action type
|
|
|
|
data['ec'] = eventCategory # Event Category
|
|
|
|
data['ea'] = eventAction # Event Action
|
|
|
|
|
2016-10-09 22:50:30 +00:00
|
|
|
if(eventLabel != None):
|
|
|
|
data['el'] = eventLabel # Event Label
|
2016-10-09 09:14:45 +00:00
|
|
|
|
2016-10-13 00:47:43 +00:00
|
|
|
self.sendData(data)
|
|
|
|
|
|
|
|
def sendData(self, data):
|
2016-10-09 09:14:45 +00:00
|
|
|
|
2016-10-13 00:47:43 +00:00
|
|
|
if(settings('metricLogging') == "false"):
|
|
|
|
return
|
2016-11-05 23:38:33 +00:00
|
|
|
|
|
|
|
if (self.testing):
|
|
|
|
log.info("GA: " + str(data))
|
2016-10-13 00:47:43 +00:00
|
|
|
|
2016-10-09 09:14:45 +00:00
|
|
|
if(self.testing):
|
|
|
|
url = "https://www.google-analytics.com/debug/collect" # test URL
|
|
|
|
else:
|
|
|
|
url = "https://www.google-analytics.com/collect" # prod URL
|
2016-11-05 23:38:33 +00:00
|
|
|
|
2016-10-09 11:49:46 +00:00
|
|
|
try:
|
|
|
|
r = requests.post(url, data)
|
|
|
|
except Exception as error:
|
|
|
|
log.error(error)
|
2016-11-05 23:38:33 +00:00
|
|
|
r = None
|
2016-10-09 09:14:45 +00:00
|
|
|
|
2016-11-05 23:38:33 +00:00
|
|
|
if(self.testing and r != None):
|
2016-10-09 22:05:51 +00:00
|
|
|
log.info("GA: " + r.text.encode('utf-8'))
|
2016-10-13 00:47:43 +00:00
|
|
|
|
|
|
|
|
2016-10-09 09:14:45 +00:00
|
|
|
|